Skip to content

feat(secrets): scope secret storage to a team - #2928

Draft
camielvs wants to merge 1 commit into
cvs/team-contextfrom
cvs/team-scoped-secrets
Draft

camielvs wants to merge 1 commit into
cvs/team-contextfrom
cvs/team-scoped-secrets

Conversation

@camielvs

@camielvs camielvs commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Secrets are stored per caller, so a team's secrets are read and written by sending the impersonation header on the four secrets requests. Every function in secretsStorage now takes an optional team id and sends the header only when one is given. Every caller still passes none, so today's behaviour is unchanged.

  • secretsListQuery(teamId) builds the cache key and the fetch from the same team id, so the cache can never hold one account's secrets under another's key. The three screens that list secrets now use it. Before, they handed fetchSecretsList to React Query directly, which would now receive the query context as its team id.
  • SecretsQueryKeys.All keys the user's secrets as ["secrets", "user"] and a team's as ["secrets", "team", teamId]. The unused Id key is removed, since it no longer fits that shape.
  • addSecret and updateSecret pass description and expires_at, which the API has always accepted as query params. No form sets them yet.
  • Failures now carry the backend's reason instead of [object ReadableStream]. They throw SecretsApiError with the status, or TeamImpersonationError when the backend refuses to act as the team. That lets a later screen tell a refusal apart from an expired session.

Related Issue and Pull requests

Type of Change

  • New feature
  • Improvement

Line Breakdown

Category Added Removed
Production code 67 28
Comments 0 3
Tests 236 0
Total 303 31

Checklist

  • I have tested this does not break current pipelines / runs functionality
  • I have tested the changes on staging

Screenshots (if applicable)

None. There's no UI change.

Test Instructions

pnpm vitest run src/components/shared/SecretsManagement

To check by hand that nothing regressed:

  1. With a backend configured, open Settings → Secrets.
  2. Add, replace and remove a secret.
  3. Open the secret picker on a task argument and pick a secret.

All three should behave as on master.

Additional Comments

The guided tour's mock secrets are unchanged.

🤖 Generated with Claude Code

@camielvs
camielvs added this pull request to stack #2930 October 22, 2026 19:23
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

🎩 Preview

A preview build has been created at: cvs/team-scoped-secrets/4d5a7f8

Secrets are stored per caller, so a team's secrets are read and written
by sending the impersonation header on these four requests. Every
function in `secretsStorage` now takes an optional team id and sends the
header only when one is given. Every caller still passes none, so they
behave exactly as before.

- `secretsListQuery(teamId)` builds the cache key and the fetch from the
  same team id, so the cache can never hold one account's secrets under
  another's key. The three screens that list secrets use it instead of
  handing `fetchSecretsList` to React Query directly, which would now
  receive the query context as its team id.
- `SecretsQueryKeys.All` keys the user's secrets as `["secrets", "user"]`
  and a team's as `["secrets", "team", teamId]`. The unused `Id` key is
  gone, since it no longer fits that shape.
- `addSecret` and `updateSecret` pass `description` and `expires_at`,
  which the API has always accepted as query params. No form sets them
  yet.
- Failures carry the backend's reason instead of
  `[object ReadableStream]`. They throw `SecretsApiError` with the
  status, or `TeamImpersonationError` when the backend refuses to act as
  the team, so a later screen can tell that apart from an expired
  session.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@camielvs
camielvs force-pushed the cvs/team-scoped-secrets branch from 8e9703e to 4d5a7f8 Compare October 23, 2026 22:58

This branch has not been deployed

No deployments
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.

1 participant