feat(secrets): add optional descriptions to workspace secrets - #6796
Conversation
Workspace secrets already have a backing credential row with a description column, but nothing surfaced it. Teammates had no way to record what a secret is for. - Add a Description field to the secret detail page, matching the integrations credential page, gated on workspace-secret admin - Fold the value and description editors into one Save/Discard pair and one unsaved-changes guard; two guards cannot coexist, since each seeds its own same-URL history entry - Match descriptions in the secrets settings search - Expose description on GET/PUT /api/v2/secrets and in the CLI Descriptions are workspace-only: env_personal credential rows are per-workspace mirrors of one user-global secret, so one saved there would exist in a single workspace, and a personal secret has no teammates to inform. The API rejects a description on personal scope rather than silently dropping it, and omitting it on PUT leaves any existing description untouched so a value rotation cannot erase it.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryMedium Risk Overview API & CLI: UI: The secret detail page shows a Description section for workspace secrets and merges value edits with display name/description through Backend: Reviewed by Cursor Bugbot for commit 5d35f39. Configure here. |
Greptile SummaryThe PR adds optional descriptions to workspace secrets across the detail UI, public v2 API, CLI, search, documentation, persistence, and cache updates. The follow-up change stabilizes the secret-value section callbacks and returned object as requested in the previous review thread.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/app/workspace/[workspaceId]/settings/components/secrets/hooks/use-secret-value.ts | Stabilizes save/discard callbacks and the returned section object without leaving the previously reported issue outstanding. |
| apps/sim/app/workspace/[workspaceId]/components/credential-detail/hooks/use-credential-detail-form.ts | Integrates an optional editable section into the shared dirty, saving, save, discard, and navigation-guard lifecycle. |
| apps/sim/app/workspace/[workspaceId]/settings/secrets/[credentialId]/secret-detail.tsx | Adds workspace-secret description editing and unifies value and metadata actions under one form. |
| apps/sim/lib/secrets/application/use-cases.ts | Forwards workspace descriptions while explicitly rejecting descriptions for personal secrets. |
| apps/sim/lib/credentials/secret-values.ts | Persists supplied descriptions while preserving existing descriptions when the field is omitted. |
| apps/sim/lib/api/contracts/v2/secrets.ts | Extends public secret contracts with nullable output descriptions and optional workspace-only update input. |
| packages/sim-cli/src/commands/secrets.ts | Adds description output and the workspace-only set flag to the CLI. |
Sequence Diagram
sequenceDiagram
participant User
participant UI as Secret Detail UI
participant Form as Combined Form
participant API as Secret/Credential API
participant DB as Credential Store
User->>UI: Edit value and/or description
UI->>Form: Save combined dirty state
Form->>API: Save value when changed
API->>DB: Update encrypted value
Form->>API: Save description when changed
API->>DB: Update credential description
DB-->>UI: Refresh secret metadata
Reviews (2): Last reviewed commit: "fix(secrets): keep the API docs example ..." | Re-trigger Greptile
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c60e92e. Configure here.
- Patch the credential detail cache optimistically on update. `onMutate` cancelled the detail query but only patched the lists, so a detail-backed editor stayed dirty after a successful save until the refetch landed — long enough for Discard to restore the pre-save value over the committed one, and for Back to open the unsaved-changes guard. - Memoize `useSecretValue`'s returned callbacks and object, per the hook convention, so the composed form's save/discard stop churning per render. - Reject a description on a personal secret in the domain layer rather than only at the v2 boundary. The internal credential update path accepted one for any type, writing data every reader hides. - Normalize an empty description to null so the API and UI agree. - Correct the secrets documentation, which described a Display Name field the detail view does not have and omitted the scope rule. - Drop the CLI's copy of the 500-character bound; it can't import the contract, so a copy only drifts from the message the API already returns. - Collapse a redundant save guard and align the description write gate with the render gate. Leaves the integrations credential page byte-identical to staging.
Backward-compatibility fixes for anyone who never sets a description. - Move the blank-to-null normalization out of the contract and into the route. A Zod `.transform()` on any property drops the whole request schema's OpenAPI examples, which had silently removed the Set Secret request example from the published docs. - Append the CLI `description` column instead of inserting it before `updated`. `--output text` is positional, so inserting would shift every field an existing script cuts. - Reject a description on a personal secret with a message that says so, rather than dropping the field and falling through to the generic "no updatable fields" error.
|
@cursor review |

Summary
descriptionexposed onGET/PUT /api/v2/secretsand in the CLI (secrets listcolumn,secrets set --description)No migration —
credential.descriptionalready existed and every workspace secret already has a backingcredentialrow.Workspace-only by design:
env_personalrows are per-workspace mirrors of one user-global secret, so a description saved there would exist in a single workspace, and a personal secret has no teammates to inform. The API rejects a description on personal scope rather than silently dropping it. OmittingdescriptiononPUTleaves any existing one untouched, so rotating a value can't erase it.Type of Change
Testing
Tested manually.
bun run check:audits(29/29),type-check, andlintall pass. 1104 tests green acrossapp/api/v2,lib/secrets,lib/credentials,lib/environment, plus 341 CLI tests. New tests cover the v2 read projection (workspace description emitted, personal nulled), description forwarding onPUT, omission leaving it untouched, and personal scope rejected — each verified to fail without its fix.Generated artifacts regenerated via their own scripts:
packages/sim-cli/src/generated/v2-api.ts,apps/docs/openapi-v2-resources.json,apps/docs/content/docs/en/cli/{reference,secrets}.mdx.Checklist