Repository navigation
feat(secrets): scope secret storage to a team - #2928
Merged
Merged
Conversation
🎩 PreviewA preview build has been created at: |
This was referenced Oct 22, 2026
camielvs
force-pushed
the
cvs/team-scoped-secrets
branch
from
October 22, 2026 20:12
7f9b852 to
8e9703e
Compare
camielvs
force-pushed
the
cvs/team-scoped-secrets
branch
from
October 23, 2026 22:58
8e9703e to
4d5a7f8
Compare
camielvs
force-pushed
the
cvs/team-scoped-secrets
branch
2 times, most recently
from
October 27, 2026 17:33
ea8e047 to
ff12fc0
Compare
camielvs
force-pushed
the
cvs/team-scoped-secrets
branch
2 times, most recently
from
October 29, 2026 23:33
2bd2220 to
d657d94
Compare
camielvs
force-pushed
the
cvs/team-scoped-secrets
branch
from
October 30, 2026 18:51
d657d94 to
11fb445
Compare
camielvs
force-pushed
the
cvs/team-scoped-secrets
branch
from
October 30, 2026 20:35
11fb445 to
783a658
Compare
camielvs
marked this pull request as ready for review
October 30, 2026 21:24
camielvs
force-pushed
the
cvs/team-scoped-secrets
branch
from
October 30, 2026 21:46
783a658 to
5ec22ef
Compare
maxy-shpfy
approved these changes
Oct 30, 2026
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
force-pushed
the
cvs/team-scoped-secrets
branch
from
October 31, 2026 00:12
5ec22ef to
eeb9d79
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
secretsStoragenow 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 handedfetchSecretsListto React Query directly, which would now receive the query context as its team id.SecretsQueryKeys.Allkeys the user's secrets as["secrets", "user"]and a team's as["secrets", "team", teamId]. The unusedIdkey is removed, since it no longer fits that shape.addSecretandupdateSecretpassdescriptionandexpires_at, which the API has always accepted as query params. No form sets them yet.[object ReadableStream]. They throwSecretsApiErrorwith the status, orTeamImpersonationErrorwhen 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
Line Breakdown
Checklist
Screenshots (if applicable)
None. There's no UI change.
Test Instructions
To check by hand that nothing regressed:
All three should behave as on
master.Additional Comments
The guided tour's mock secrets are unchanged.
🤖 Generated with Claude Code