Skip to content

User-scoped secret connections (personal vs workspace-shared) #258

Description

@ryw

Summary

Make secret connections (workspace_secret_connection) optionally user-scoped, so a personal API key isn't forced into a workspace-shared bucket.

The inconsistency

TAS already has two credential models, scoped differently:

  • Native-MCP connections (workspace_connection) — per-user (user_id); each member OAuths their own.
  • Secret connections (workspace_secret_connection, keyed by (workspace_id, slug)) — workspace-shared. No user_id; only a created_by audit column.

So a personal API key (e.g. a user's attio_api_key) stored as a secret connection is shared: any member's agent in the workspace can use it, and anyone with secret access can rotate/overwrite it. (The plaintext is never exposed — AES-encrypted, UI shows only …last4 — so the exposure is usability + existence, not the raw value.)

Proposal

Add an optional owner scope to secret connections:

  • Schema: add nullable user_id to workspace_secret_connection; a row is workspace-shared when null, personal when set. Key/uniqueness becomes (workspace_id, COALESCE(user_id,'workspace'), slug) or similar.
  • Creation UI: a visibility toggle — "Me" / "Workspace" (mirrors the webhook "Run as" / connection ownership patterns).
  • Listing/visibility: a member sees workspace-shared secrets + only their own personal ones.
  • Resolution: when a run dispatches, resolve the run owner's personal secret first, then fall back to the workspace-shared one. tas_tools.secret(name) keeps the same call shape; the resolution becomes owner-aware.

Code anchors

  • api/migrations/0039_workspace_secret_connection.sql — schema (add user_id).
  • web/src/lib/secret-connections.ts — CRUD + listing (scope the queries, add owner).
  • The run's secret bundle (TAS_SECRETS) is assembled per-workspace today (web/src/lib/... dispatch → runner); it would need to be assembled for the run owner (workspace-shared ∪ owner's personal).
  • The Secret connections settings UI (add the visibility toggle).
  • AAD: aadSecretConnection(workspaceId, slug) (web/src/lib/crypto-aad.ts) would need the owner folded in for personal rows.

Scope note / when it matters

For providers that offer OAuth (Attio, etc.), the cleaner answer is to use the per-user native-MCP connection instead of an API-key secret at all — that's already per-user. This feature is for API-key-only providers where a per-user credential has no other home today.

Surfaced by: the attio_api_key secret being workspace-shared while the Attio OAuth connection is per-user (single-tenant dogfood).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority: p2Valuable after the current P0/P1 queue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions