feat(secrets): implement secrets storage and management functionality#3425
Merged
Conversation
- Added a new migration to create the "secrets" table with necessary constraints and indexes. - Introduced SecretStorage class for managing secrets, including create, update, delete, and list operations. - Implemented tools for creating and listing secrets, ensuring values are encrypted and access is controlled based on user and organization scopes. - Updated context and tests to integrate the new secrets functionality across the application. This commit enhances the security and management of sensitive information within the application.
Contributor
🧪 BenchmarkShould we run the Virtual MCP strategy benchmark for this PR? React with 👍 to run the benchmark.
Benchmark will run on the next push after you react. |
Contributor
Release OptionsSuggested: Minor ( React with an emoji to override the release type:
Current version:
|
…mponent - Introduced a new migration to create the "secrets" table with necessary constraints and indexes. - Updated the EnvVarsField component to accept additional props for virtualMcpId and orgSlug, enhancing its functionality. - Integrated a new RunningSandboxNotice component to inform users about the need to restart their sandbox for environment variable changes to take effect. This commit enhances the management of secrets and improves user experience in the environment variable handling process.
…ndling - Introduced a new migration to create the "secrets" table with necessary constraints and indexes. - Updated the EnvVarsField component to improve entry updates, ensuring better handling of environment variable changes. This commit enhances the management of secrets and optimizes the user experience in the environment variable handling process.
Contributor
There was a problem hiding this comment.
3 issues found across 36 files
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
tlgimenes
added a commit
that referenced
this pull request
May 21, 2026
…n with prod 082-secrets origin/main now has 082-secrets (PR #3425) at the position the earlier renumber commit (edb42f3) parked 082-thread-run-locally. Two migrations sharing the same 082- numeric prefix breaks Kysely's ordering guarantee (and the locally-run 082-thread-run-locally errors out with corrupted-migrations when the directory now sorts 082-secrets first). Shift all six branch migrations up by one slot: 082-thread-run-locally -> 083-thread-run-locally 083-drop-host-sandbox-rows -> 084-drop-host-sandbox-rows 084-rename-runner-kind -> 085-rename-runner-kind 085-thread-pins-and-vm-map-rekey -> 086-thread-pins-and-vm-map-rekey 086-fix-vm-map-rekey -> 087-fix-vm-map-rekey 087-purge-cli-activate-keys -> 088-purge-cli-activate-keys Updates the index.ts imports / migrations map and the two test files that self-imported their own migration module (086/087 test files). All 88 migrations apply cleanly in the test runner; 8/8 renamed tests pass. Local devs who already ran any of the old-numbered migrations on this branch must either reset their postgres data dir or rename the rows in kysely_migration to the new numbers before pulling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tlgimenes
added a commit
that referenced
this pull request
May 21, 2026
…me (#3417) * feat: harness implementation — laptop link daemon, agent pill, inlined runtime Squashes 19 commits from tlgimenes/harness-impl-research. Laptop link daemon: `deco link` boots a local Bun.serve, opens a Warp tunnel to deco.host, registers with the cluster's /api/links to receive a linkSecret, then exposes an HMAC-signed control plane for sandbox lifecycle + reverse proxy. Capability probe surfaces claude-code, codex, and decopilot-sandbox to the cluster. SIGINT/SIGTERM deregister cleanly. Sandbox provider abstraction: RunnerKind → SandboxProviderKind across type/storage column/helpers. New remote-user provider plugs the user's link daemon in via ctx.linkRegistry; docker + agent-sandbox keep an env-singleton fallback. vmMap re-keyed to (user, branch, providerKind) with tolerant readers for legacy 2-level + runnerKind shapes; migration 086-fix-vm-map-rekey performs the rename that earlier migrations no-op'd on a dropped table. Chat UI: single Agent pill replaces Runner+Harness, eligibility-aware. Clonable agents (Start Website + GitHub-imported) skip the no-AI-provider empty state when the laptop link is online with a CLI harness, and the two CLI options (Claude Code / Codex desktop) surface without a cloud key — they run on laptop-stored credentials. Decopilot fallback for agents with no GitHub repo. Connect-laptop empty-state tile + dialog. cli-activate provider path removed (now on the laptop link); migration 087 purges sentinel keys. Inlining refactor (vs prior research branch): apps/link → apps/mesh/src/ {cli/commands/link.ts, link-daemon/}, @decocms/harnesses → apps/mesh/src/ harnesses/, @decocms/link-protocol → apps/mesh/src/links/protocol/. Drops three single-consumer packages, keeps one binary (deco), folds session/tunnel/login plumbing into the existing CLI. packages/sandbox reaches into apps/mesh via relative imports — documented inline as a trade-off accepted per "abstractions to a minimum". Migrations 082-087 are renumbered to start strictly after main's latest applied (081-async-research-jobs-result-content) so prod's executed list stays a clean prefix and the new migrations apply fresh on deploy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(chat): spec for merging AgentPill into AgentModelTrigger Captures the design decisions from brainstorming: collapse the AgentPill (Decopilot/Claude Code/Codex) into the chat-input model trigger as a sectioned popover with green styling on local-CLI sections, mirror existing lock semantics, and fix the gap-collapse bug on the trigger button. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(chat): drop decopilot-laptop, lock spec scope Q6 resolved: remove the cloud-Decopilot-on-local-sandbox option entirely (existing localStorage validation already migrates stale values to null). Adds the verified "what stays vs. what goes" inventory after exploring real call sites. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(chat): implementation plan for merged model selector Nine bite-sized tasks: trim agent-options.ts, build getAgentSections (+ tests), AgentSection component, AgentModelPopover shell, rewrite AgentModelTrigger with green styling and gap fix, plumb new props through input.tsx, slim ThreadPills, delete AgentPill, and final type/lint/fmt/test verification. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(chat): drop decopilot-laptop AgentOption and computeAgentOptions Removes the cloud-Decopilot-on-local-sandbox option (decopilot-laptop) and computeAgentOptions, which gets replaced by getAgentSections in a following commit. Keeps AGENT_OPTION_PINS + pinsForOption / pinsToOption since chat-context.tsx still needs them. The localStorage load in chat-context already validates against AGENT_OPTION_PINS, so stale 'decopilot-laptop' values silently migrate to null. * feat(chat): add getAgentSections for merged model selector Introduces AgentKind/AgentSection types and a pure getAgentSections function that returns the three sections (Decopilot, Claude Code, Codex) shown in the new merged chat-input popover. Mirrors the gates computeAgentOptions used (sans decopilot-laptop) and flags CLI sections with isLocal: true to drive the green styling. Preserves the existing getAgentModelSet helper used by the settings page. Renamed agent-models.ts to agent-models.tsx because the Decopilot tier entries now hold JSX icon nodes (Lightning01/Stars01/Atom01). * feat(chat): add AgentSection component for merged model selector Renders one section in the new sectioned popover — header with the agent title (plus " · on this laptop" suffix for CLI agents), three tier rows with descriptions, and the existing "On" indicator. Local sections sit on a faint bg-success/5 band; disabled sections render opacity-40 + pointer-events-none + a small lock icon. Also adds Bun test infrastructure (bunfig preload + happy-dom + @testing-library/react + jest-dom matchers) so React component tests can render in a DOM and use toBeInTheDocument(). No prior precedent existed in the repo, so the setup file is new. * fix(chat): scope happy-dom preload to apps/mesh, revert spurious package.json sort Code review flagged that the root bunfig.toml preloaded happy-dom for all workspace tests, replacing Node natives like fetch/Request/Response/URL in packages/* tests that depend on them. Switched to Option B: removed both bunfig.toml files (root and apps/mesh) and made apps/mesh/src/test/setup.ts an importable module with side effects. React component tests now `import "../../../../test/setup"` (or equivalent) at the top, guarded by GlobalRegistrator.isRegistered to avoid double-registration. This keeps happy-dom contained to component tests that opt in, while packages/* tests keep running against Node natives (804 pass / 0 fail vs the prior 470/31/15 baseline). Also reverts an unrelated @decocms/sandbox alphabetical re-sort that crept into the Task 3 commit -- restores its prior position between marked and mesh-plugin-workflows. * feat(chat): add AgentModelPopover shell for merged selector Composes AgentSection rows from a getAgentSections result. Handles lock semantics — when lockedAgent is set, only the matching section is interactive; the others render disabled. Row click fires onSelect with (kind, tier) and the locking is verified by tests. * feat(chat): merge agent picker into AgentModelTrigger The trigger now opens the new sectioned AgentModelPopover (Decopilot, Claude Code, Codex) instead of the old SimpleModeTierDropdown / CLI tier list split. Closed pill goes text-success + bg-success/10 when the active agent is a CLI variant, matching the 'Desktop connected' green elsewhere. Picks both agent (via setPendingAgentOption) and tier in a single click and fires the eager VM start for CLI agents when a branch is set. Also fixes the phantom 6px gap that appeared when the label collapses at narrow container widths. Also passes a concrete URL to the happy-dom GlobalRegistrator in the shared test setup so component tests whose transitive imports reach better-auth/react createAuthClient (which reads window.location at module init) no longer blow up on the default about:blank URL. * fix(test): clean up DOM between component tests bun:test doesn't auto-call RTL's cleanup() the way Jest does, so DOM from one component test was leaking into the next file's render, e.g. a popover trigger from agent-model-trigger.test.tsx persisting into agent-section.test.tsx and breaking getByText queries. Add an afterEach in setup.ts that calls cleanup() to mirror the implicit Jest behavior. Because bun:test caches this setup module across every test file in a run, the top-level afterEach only registers in whichever test file imports setup first. To cover the remaining files we also install a Bun.plugin onLoad hook that prepends an afterEach -> cleanup() registration to any test source that already imports test/setup, so each file's own scope gets the hook regardless of load order. * refactor(test): drop Bun.plugin magic, use explicit setupComponentTest() The plugin onLoad transform that injected afterEach() into every test file was clever but surprising — future contributors would have no idea why test files appear to have shifted line numbers in stack traces. Replace it with an exported setupComponentTest() function that each component test calls at module top-level. Registration lands in the right scope without source rewrites or runtime magic. * chore(chat): pass currentBranch + virtualMcpId to AgentModelTrigger The merged AgentModelTrigger needs both props so it can fire the eager VM start that ThreadPills used to own when the user picks a CLI agent. Plumbs them through the input mount without otherwise touching the composer layout. * refactor(chat): drop AgentPill from ThreadPills The agent picker now lives inside the chat-input's AgentModelTrigger. ThreadPills shrinks back to a single BranchPill (still locked when the thread has messages). The eager VM-start logic moves into the merged AgentModelTrigger row click. * chore(chat): delete obsolete AgentPill component Replaced by the sectioned popover inside AgentModelTrigger. No remaining importers. * fix(chat): pin native streams + augment bun:test Matchers in setup.ts Two regressions surfaced during T9 verification when running the chat test directory as a whole: 1. happy-dom's GlobalRegistrator overwrites globalThis.TransformStream and WritableStream but leaves ReadableStream alone. Once a component test triggered registration, store-level tests that exercise ai-sdk's readUIMessageStream blew up with "readable should be ReadableStream" because stream.pipeThrough received a happy-dom TransformStream while the stream itself was the native ReadableStream. Snapshot the native classes before register() and restore them after. 2. The published @testing-library/jest-dom type augmentation targets the jest/vitest globals, so .toBeInTheDocument() flagged TS errors on bun:test's Matchers<T>. Declare the augmentation against "bun:test" so the matchers extended at runtime are visible to the type checker. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(chat): guard nullable modelId before indexing lookup in laptop-cli AgentTierEntry.modelId is `string | null` (Decopilot tiers leave it null). The existing guard checked `!model` after `lookup[entry.modelId]`, which TypeScript correctly flagged because the index access itself rejects a null key. Add the explicit pre-guard so the type narrows cleanly and the runtime semantics stay identical. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(migrations): shift branch migrations to 083-088 to avoid collision with prod 082-secrets origin/main now has 082-secrets (PR #3425) at the position the earlier renumber commit (edb42f3) parked 082-thread-run-locally. Two migrations sharing the same 082- numeric prefix breaks Kysely's ordering guarantee (and the locally-run 082-thread-run-locally errors out with corrupted-migrations when the directory now sorts 082-secrets first). Shift all six branch migrations up by one slot: 082-thread-run-locally -> 083-thread-run-locally 083-drop-host-sandbox-rows -> 084-drop-host-sandbox-rows 084-rename-runner-kind -> 085-rename-runner-kind 085-thread-pins-and-vm-map-rekey -> 086-thread-pins-and-vm-map-rekey 086-fix-vm-map-rekey -> 087-fix-vm-map-rekey 087-purge-cli-activate-keys -> 088-purge-cli-activate-keys Updates the index.ts imports / migrations map and the two test files that self-imported their own migration module (086/087 test files). All 88 migrations apply cleanly in the test runner; 8/8 renamed tests pass. Local devs who already ran any of the old-numbered migrations on this branch must either reset their postgres data dir or rename the rows in kysely_migration to the new numbers before pulling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(migrations): guard vmMap aggregate with COALESCE in 085-rename-runner-kind Wrap inner and outer jsonb_object_agg calls in COALESCE(..., '{}'::jsonb) so rows whose vmMap is an empty object — or whose nested user_map is empty — don't end up with metadata = NULL after the rewrite (jsonb_set returns NULL when any argument is NULL). Also restrict the UPDATE to rows where metadata->'vmMap' is an object so non-object vmMap values are skipped. Addresses PR review on #3417. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(link-daemon): tighten handle validation to non-empty string Reject non-string truthy bodies (arrays, objects, numbers) with 400 instead of letting them flow through to ensureSandbox. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): detect --port=X inline form for deco link Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
This commit enhances the security and management of sensitive information within the application.
What is this contribution about?
Screenshots/Demonstration
How to Test
Migration Notes
Review Checklist
Summary by cubic
Implements an encrypted secrets vault with org/user scopes and integrates secret-backed env vars into virtual MCPs. Secrets are resolved on VM start and sent to the sandbox daemon; values are stored encrypted and never returned.
New Features
SecretStorage(create/update/delete/list/resolve), encrypted via Credential Vault with cross-user checks.SECRET_CREATE,SECRET_LIST, new "Secrets" category, andsecrets:managepermission.metadata.runtime.envsupports literals or secret refs; resolved and PUT to the daemon before install/dev. Runtime card adds anEnvVarsFieldwith .env paste and a restart notice; autosave skips incomplete rows; removed the old VM drawer env panel.RuntimeMetadataandRuntimeEnvEntryfrom@decocms/mesh-sdk.Migration
082-secretsto create thesecretstable and indexes.Written for commit e54cae1. Summary will update on new commits. Review in cubic