feat(spawn): inject Automic Vault secrets into worker launch templates - #2479
Closed
ansellchiu wants to merge 1 commit into
Closed
feat(spawn): inject Automic Vault secrets into worker launch templates#2479ansellchiu wants to merge 1 commit into
ansellchiu wants to merge 1 commit into
Conversation
Add an opt-in `config/av-inject` toggle that wraps every verified worker launch in `av inject +KEY... -- <launch>` so crewmates and secondmates receive the captain's static API keys from Automic Vault as environment variables, never written to disk and never printed. - bin/fm-av-inject-lib.sh owns the mode parsing (default off; on/true/yes/1 enable, FM_AV_INJECT overrides the file), av resolution, key-name validation, and the `<av> inject +KEY... -- ` wrapper. Enabled-but-missing av refuses the spawn rather than launching without the expected secrets. - fm-spawn.sh places an __AVINJECT__ placeholder immediately before the agent binary in every launch template so all env prefixes stay before av inject; the raw unverified-adapter escape hatch is deliberately unwrapped. The prefix is resolved once, early, so a misconfigured home fails fast. - config/av-inject is primary-authoritative and inherited into secondmate homes alongside the other local config toggles. - Documented in AGENTS.md, docs/configuration.md, and secondmate-provisioning; covered by tests/fm-av-inject.test.sh (library units plus a spawn-path regression proving the real claude launch is wrapped only when opted in). The keys and injection mechanics follow the automic-vault-r1 research plan. Injection stays OFF by default because it must not be enabled before the attended per-key `av save` migration is verified.
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.
Intent
Implement the Automic Vault (av) secret-injection wrapper in fm-spawn.sh's launch templates, per the automic-vault-r1 research plan (data/automic-vault-r1/report.md and decisions.md). Goal: workers (crewmates and secondmates) receive the captain's static API keys from Automic Vault as environment variables, injected via 'av inject +KEY... -- ', with the value never written to disk and never printed or logged.
Exact keys from the plan: EXA_API_KEY, PARALLEL_API_KEY, TAVILY_API_KEY, LINKUP_API_KEY, BRAVE_SEARCH_API_KEY, BRAVE_ANSWERS_API_KEY, DEEPSEEK_API_KEY, BUZZ_XYZ_KEY (buzz.xyz renamed because '.'/space are invalid av key names). av inject copies the full ambient env plus the named secrets and execs the child, so every existing env prefix must stay BEFORE 'av inject' on the launch line. Captain approvals (decisions.md): keep secrets 'When Unlocked', grant per-secret Direct Access Rules to the verified launcher, keep 1Password originals as fallback, and ship this fm-spawn.sh wrapper.
Deliberate design decisions I made:
This is a change to firstmate's shared tracked material, developed under firstmate-coding-guidelines (one-owner rule, inline-stub/trigger hygiene, one-sentence-per-line Markdown, plain dashes, colocated behavior-driven test).
What Changed
bin/fm-av-inject-lib.sh, which owns theav inject +KEY... --contract: opt-in mode parsing (via a gitignoredconfig/av-injecttoggle, overridable byFM_AV_INJECT), absolute-path resolution ofav, key-name validation, and construction of the injection prefix for the fixed key set (EXA, PARALLEL, TAVILY, LINKUP, BRAVE_SEARCH, BRAVE_ANSWERS, DEEPSEEK, BUZZ_XYZ). It fails closed when enabled butavis missing or a key name is invalid.__AVINJECT__placeholder into every verified launch template inbin/fm-spawn.sh(claude, codex, opencode, pi/pi-signed, grok, cursor, kimi, muse), resolved once early so all env prefixes stay beforeav inject; the raw unverified-adapter path is deliberately left unwrapped. Addedconfig/av-injecttoFM_INHERITABLE_CONFIGinbin/fm-config-inherit-lib.shso secondmate homes inherit the toggle.docs/configuration.md,AGENTS.md, and the secondmate-provisioning skill, and addedtests/fm-av-inject.test.shcovering the library units plus a spawn-path regression proving launches are wrapped (env prefixes beforeav inject) only when opted in.Risk Assessment
✅ Low: Well-bounded, opt-in, default-off change with correct env-prefix ordering across all templates, fail-closed resolution, and thorough colocated tests; no reachable defect or intent contradiction found.
Testing
Ran the focused suite
tests/fm-av-inject.test.sh(7/7 pass) and produced product-level CLI transcripts showing the actual end-user surface: the full 8-keyav inject +KEY... --wrapper string, both fail-closed refusals with their exact operator-facing error messages, and the literal claude launch command fm-spawn.sh sends to the pane in both an opted-in home (env prefixes intact beforeav inject, keys in plan order,-- claudeafter) and a default home (unwrapped). This is a bash/CLI change with no rendered UI surface, so the appropriate reviewer-visible evidence is the captured launch-command transcripts rather than screenshots. All checks pass; worktree left clean; evidence written to the dedicated evidence directory.Evidence: Real fm-spawn.sh claude launch line: opted-in (wrapped) vs default (unwrapped)
[OPTED-IN] env -u CURSOR_AGENT -u CURSOR_INVOKED_AS CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION=false '.../av' inject +EXA_API_KEY +PARALLEL_API_KEY +TAVILY_API_KEY +LINKUP_API_KEY +BRAVE_SEARCH_API_KEY +BRAVE_ANSWERS_API_KEY +DEEPSEEK_API_KEY +BUZZ_XYZ_KEY -- claude --dangerously-skip-permissions ... [DEFAULT] env -u CURSOR_AGENT -u CURSOR_INVOKED_AS CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION=false claude --dangerously-skip-permissions ... (no inject wrapper)Evidence: Wrapper string + fail-closed operator messages
[1] on -> '<av>' inject +EXA_API_KEY ... +BUZZ_XYZ_KEY -- [2] off -> prefix=[] rc=0 [3] enabled+no av -> rc=1 error: av-inject is enabled but the 'av' CLI (Automic Vault) was not found on PATH... [4] invalid key -> rc=1 error: av-inject key name 'bad-key' is not a valid secret name ([A-Za-z_][A-Za-z0-9_]*)Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
bin/fm-spawn.sh:1242- AV_INJECT_PREFIX is resolved unconditionally at bin/fm-spawn.sh:1242, before the harness type is known to be a verified template vs. the raw unverified-adapter escape hatch (LAUNCH=$ARG3 at line 1191, which carries no AVINJECT placeholder). Consequence: an av-inject-enabled home that spawns a raw unverified adapter whileavis missing on PATH will fail closed and refuse to spawn, even though injection would never have been applied to that launch. This is fail-safe (a misconfigured enabled home shouldn't be missing av anyway) and the injection-applies-to-verified-only boundary is still honored when av is present, so no action is needed; noting the interaction only for transparency.✅ **Test** - passed
✅ No issues found.
bash tests/fm-av-inject.test.sh— all 7 checks pass (mode precedence, disabled no-op, fail-closed on missing av, default-key wrapper string, key-name rejection, opt-in spawn wrapping, default unwrapped spawn)Manual: sourcedbin/fm-av-inject-lib.shunder bash and ranfm_av_inject_prefixfor on/off/missing-av/invalid-key cases, captured toav-inject-wrapper.txtManual e2e: drovebin/fm-spawn.shwith fake tmux/av and a real isolated git worktree to capture the literal claude launch line for an opted-in vs. default home, captured toav-inject-spawn-launch.txt✅ **Document** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.