Skip to content

feat: unified cross-channel access control (#311) - #312

Merged
vybe merged 1 commit into
mainfrom
feature/311-unified-channel-access-control
Apr 13, 2026
Merged

feat: unified cross-channel access control (#311)#312
vybe merged 1 commit into
mainfrom
feature/311-unified-channel-access-control

Conversation

@vybe

@vybe vybe commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the unified cross-channel access control primitive from #311: one verified email, one allow-list, one gate. Each adapter translates its native identifier into a verified email; everything downstream (sharing, access requests, per-user memory) keys off that single identity.

  • Schema: require_email / open_access on agent_ownership (via AccessPolicyMixin), verified_email / verified_at on telegram_chat_links, new access_requests table.
  • Adapters: ChannelAdapter.resolve_verified_email + prompt_auth; Telegram /login state machine (reuses email_login_codes); Slack via users.info.
  • Router: single gate in message_router that checks owner/admin/agent_sharing, falls through to open_access, or upserts a pending access request. Group chats bypass; unpoliced agents retain legacy permissive behavior.
  • Routes: GET/PUT /api/agents/{name}/access-policy, GET /api/agents/{name}/access-requests, POST /api/agents/{name}/access-requests/{id}/decide (approve inserts into agent_sharing + whitelists email).
  • UI: SharingPanel.vue gains policy toggles and a pending-requests inbox.
  • Docs: new canonical flow docs/memory/feature-flows/unified-channel-access-control.md + updates to agent-sharing, telegram-integration, slack-integration flows.

Design notes

  • agent_sharing becomes the cross-channel allow-list (same email admits the user on web, Telegram, and Slack).
  • public_user_memory (MEM-001) now keys off the verified email automatically — the router uses verified_email as source_user_email when present.
  • Backward compatibility: agents with both policy flags off retain the existing permissive behavior, so no live bots break on merge.
  • Telegram /login uses the existing email_login_codes infrastructure (same 6-digit code table as web email auth).
  • Slack has no /login step — the workspace OAuth token already proves identity, so users.info is called live (requires the users:read.email scope).

Architectural invariants respected

Test plan

  • 21 smoke tests in tests/test_channel_access_control.py21/21 passing (15.6s)
  • Migration applied cleanly on running stack (access_control in schema_migrations)
  • DB roundtrip: upsert_access_requestlist_access_requestsdecide_access_request(approve=True) inserts into agent_sharing
  • email_has_agent_access returns True for admin, False for stranger
  • All four endpoints smoke-tested end-to-end against localhost backend
  • Manual: Telegram /login user@example.com → code email → /login 123456 → subsequent messages pass (not run — requires live bot)
  • Manual: Slack DM with require_email=True fetches email via users.info (not run — requires live workspace)
  • Follow-up PR: unify feat: Public chat link email whitelist and access requests #252 (public-link web surface) onto this primitive

Closes #311

🤖 Generated with Claude Code

…allow-list, access requests

Introduces a single access-control primitive across web, Telegram, and Slack.
Each adapter's only new responsibility is translating its native identity into
a verified email; all downstream concerns (allow-list, access requests,
per-user memory) run off that key.

Changes
-------
Schema (migration `access_control`):
- agent_ownership.require_email, open_access (via AccessPolicyMixin)
- telegram_chat_links.verified_email, verified_at
- new access_requests(agent_name, email, channel, status, decided_by, decided_at)

Adapters:
- ChannelAdapter gains resolve_verified_email + prompt_auth
- Telegram /login state machine reuses email_login_codes; /logout, /whoami
- Slack resolves via users.info (workspace OAuth is already verified)
- message_router adds a single gate: owner/admin/sharing → open_access →
  access_request. Group chats bypass. Unpoliced agents keep legacy behavior.

Routes (routers/sharing.py):
- GET/PUT /api/agents/{name}/access-policy
- GET /api/agents/{name}/access-requests
- POST /api/agents/{name}/access-requests/{id}/decide
  (approve inserts into agent_sharing + whitelists email if email auth is on)

UI: SharingPanel.vue gains policy toggles and a pending-requests inbox.

Tests: 21 smoke tests in test_channel_access_control.py, all passing.

Docs: new feature-flow document + updates to agent-sharing, telegram-integration,
slack-integration flows.

Closes #311

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vybe
vybe merged commit 9dd06f2 into main Apr 13, 2026
vybe added a commit that referenced this pull request Apr 13, 2026
…licy

Retire the per-link require_email flag on agent_public_links in favor of
agent_ownership.require_email + open_access (the policy already used by
Slack and Telegram via adapters.message_router). Public web chat now runs
the same unified gate: owner/admin/shared → allow; open_access → allow;
else pending access_request + 403.

- routers/public.py: helpers _agent_requires_email / _agent_allows_open_access;
  all link["require_email"] reads replaced; public_chat applies the shared
  gate after email verification
- routers/public_links.py, db_models.py, db/public_links.py, database.py:
  drop require_email from Pydantic models, responses, and SQL; column
  retained in schema (Slack legacy slack_link_connections join still reads
  it) and written as 0 by default
- db/migrations.py: new public_link_require_email_unified migration ORs
  any legacy per-link require_email=1 into agent_ownership.require_email=1
  so existing email-required links stay email-required
- PublicLinksPanel.vue: remove "Email Required" badge + require_email
  checkbox + payload fields; point users to the Channel Access Policy

Tests: tests/test_public_links.py — 6 new TestUnifiedAccessPolicy smoke
tests; existing fixtures rewritten to drive /access-policy instead of the
per-link flag. 27 passed, 4 skipped (pre-existing SMTP/DB prereqs).

Docs: public-agent-links.md and unified-channel-access-control.md updated;
channel coverage table now shows web as live and the #252 TODO is resolved.

Refs #311 #312

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vybe added a commit that referenced this pull request Apr 13, 2026
…ccess_requests schema

Expands the Sharing section from 3 to 6 endpoints (access-policy
get/set, access-requests list/decide), annotates new agent_ownership
columns (require_email, open_access), and adds the access_requests
table + telegram_chat_links verified_email column per #311/#312.

Refs #311 #312

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Unified channel access control — verified-email identity, per-agent allow-list, access requests (web / Telegram / Slack)

1 participant