🤖 feat: add inject toggle for global secrets#2600
Conversation
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fcae9f2350
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed both review items and pushed fixes. Please take another look. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cf1cdc7c3e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed malformed injectAll sanitization and added regression coverage. Please re-review. |
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Add optional `injectAll` on global secrets, inject opted-in globals into `getEffectiveSecrets`, expose an Inject switch in global secrets settings, and cover behavior with config tests. --- _Generated with `mux` • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$2.45`_ <!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=2.45 -->
Fix project-scope secrets grid header alignment and ensure duplicate global secret keys honor last-writer semantics for injectAll decisions. Add regression test coverage for duplicate-key behavior. --- _Generated with `mux` • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$2.45`_ <!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=2.45 -->
Keep valid key/value secrets when persisted `injectAll` is malformed, and treat inject-all as enabled only when explicitly `true`. Add regression coverage for malformed injectAll with project alias resolution. --- _Generated with `mux` • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$2.45`_ <!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=2.45 -->
a687d22 to
905129e
Compare
Add a read-only injected-global view in Project secrets settings, backed by a new secrets API endpoint that returns global injectAll keys active for the selected project. Keep precedence explicit: project keys override injected globals. --- _Generated with `mux` • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$2.45`_ <!-- mux-attribution: model=openai:gpt-5.3-codex thinking=xhigh costs=2.45 -->
|
@codex review Added project-scope visibility for globally injected secrets and corresponding API support/tests. Please review. |
|
Codex Review: Didn't find any major issues. 🎉 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
Add an
injectAlltoggle for global secrets and show a read-only "Injected from Global" list in project settings so inherited env keys are visible.Background
Global secrets previously acted only as shared storage and were injected only when a project secret explicitly referenced them. This change makes common secrets (e.g. org-wide tokens) opt-in injectable across all projects.
Implementation
SecretSchemawith optionalinjectAll.Config.getEffectiveSecrets()to:{ secret: "KEY" }aliases,injectAll.injectAllvalues by stripping the flag while preserving validkey/valuesecrets.injectAll === true.secrets.getInjectedGlobals(projectPath)API andConfig.getInjectedGlobalSecrets()to expose injected global keys for a project in a read-only way.injectAllrecovery.Validation
bun test src/node/config.test.ts✅make typecheck✅make lint✅make static-check✅Risks
📋 Implementation Plan
Plan: Add
injectAllToggle to Global SecretsContext & Goal
Global secrets today are shared storage only — they are never injected into project environments unless a project explicitly references them via
{ secret: "GLOBAL_KEY" }. The user wants a simple toggle on each global secret so that when enabled, that secret is automatically injected into every project's environment without requiring per-project references.User impact: One toggle per secret replaces N per-project reference entries. New projects automatically get the secret with zero configuration.
Architecture Summary
The existing secrets system has 3 layers:
SecretSchemainsrc/common/orpc/schemas/secrets.ts) → defines{ key, value }Config.getEffectiveSecretsinsrc/node/config.ts) → merges project secrets + resolved global referencessecretsToRecord(config.getEffectiveSecrets(projectPath))) → converts toRecord<string, string>forprocess.envAll callers already go through
getEffectiveSecrets→secretsToRecord. This means a single change ingetEffectiveSecretspropagates to every injection point (workspace init, bash tool, terminal, tasks, AI service).Estimated LoC: ~120 product code, ~80 test code
Implementation Steps
Step 1: Extend
SecretSchemawithinjectAllFile:
src/common/orpc/schemas/secrets.tsAdd an optional
injectAllboolean to the schema. Existing secrets without this field default tofalse(backward-compatible).Why
.optional(): This field only appears on global secrets. The Zod schema is shared between global and project secrets, so the field must be optional. Existingsecrets.jsonfiles withoutinjectAllparse cleanly —undefinedis treated asfalse.Step 2: Update
Config.getEffectiveSecretsto mergeinjectAllglobalsFile:
src/node/config.ts, methodgetEffectiveSecrets(line ~1367)Currently this method only returns project secrets (with global references resolved). Change it to also prepend global secrets that have
injectAll: true, with project secrets taking precedence (last-writer-wins).Key behaviors:
injectAllglobals are prepended, so explicit project secrets always win.injectAllglobal by defining the same key (either literal or reference).injectAllfield is stripped from the output — callers receive plain{ key, value: string }entries.Step 3: Update
Config.isSecretto acceptinjectAllFile:
src/node/config.ts, static methodisSecret(line ~1245)The
isSecrettype guard validates persisted data. It needs to tolerate theinjectAllfield. Currently it checks forkeyandvalue— no change needed since it doesn't reject extra properties. But verify this is the case. IfparseSecretsArrayornormalizeSecretsConfigstrips unknown fields, those need updating too.After review:
parseSecretsArrayfilters viaConfig.isSecret()which checkstypeof value.key === "string" && isSecretValue(value.value). TheinjectAllfield passes through as an extra property — no code change needed for parsing/normalization.Step 4: Update the Secrets Settings UI — add toggle per global secret row
File:
src/browser/components/Settings/sections/SecretsSection.tsxWhen
scope === "global", add aSwitchtoggle in each secret row forinjectAll. The toggle should be inline and only appear in global scope (project secrets don't haveinjectAll).4a. Import the
Switchcomponent:4b. Add an
updateSecretInjectAllcallback:4c. Update the grid layout for global scope to include the toggle column. Currently:
grid-cols-[1fr_1fr_auto_auto](key, value, eye, trash)grid-cols-[1fr_auto_1fr_auto_auto](key, type, value, eye, trash)Change global to:
grid-cols-[1fr_1fr_auto_auto_auto](key, value, eye, inject-toggle, trash)4d. Add a column header label
Inject(only in global scope).4e. Render the
Switchin each global secret row:4f. Fix the
secretsEqualcomparison to includeinjectAll:4g. Update the help text. Replace:
With:
Step 5: Add tests
File:
src/node/config.test.tsAdd tests in the existing
describe("secrets")block:Step 6: Update the existing test assertion
File:
src/node/config.test.tsThe existing test
"does not inherit global secrets by default"(line 339) should still pass because:injectAll: trueare not injected.injectAll, so behavior is unchanged.Verify this test still passes after the change. No modification expected.
Files Changed (Summary)
src/common/orpc/schemas/secrets.tsinjectAll: z.boolean().optional()toSecretSchemasrc/node/config.tsgetEffectiveSecretsto mergeinjectAllglobalssrc/browser/components/Settings/sections/SecretsSection.tsxSwitchtoggle, grid column, help text, equality checksrc/node/config.test.tsValidation
make typecheck— ensureSecrettype flows correctly everywhere.bun test src/node/config.test.ts— new + existing secret tests pass.make lint— no lint errors.Generated with
mux• Model:openai:gpt-5.3-codex• Thinking:xhigh• Cost:$2.45