Agent credential bootstrap โ
- Status: done, opened 2026-08-24.
- Issue:
#955: feat(tooling): agent credential bootstrap, two more per-machine secret files plus PATH-robust gcloud - Launch plan: none, this is agent-session tooling/DX, not a product feature.
What is this? โ
Two more per-machine credential files come from Secret Manager instead of staying manually assembled, and gcloud stops being a PATH gamble. โ
- The operator put 24 new secrets into GCP Secret Manager on 2026-08-24 so agent sessions could stop being blocked on missing local credential files.
npm run env:bootstrap(tooling/scripts/bootstrap-env.mjs) already pulled.env.localfrom Secret Manager; this extends it to two more targets that e2e/browser-test tooling reads directly instead of throughprocess.env:~/.lantern-agent-probe.env(Agent Probe admin creds) and~/.lantern-test-accounts.env(app + merchant test accounts). Which keys go where lives in one shared list,tooling/shared/credential-files.cjs, so the bootstrap script andlantern doctorcan never drift apart on it. - Alongside it,
gcloudis now resolved to an absolute path (tooling/shared/gcloud.mjs) instead of trusted on PATH. A non-interactive agent shell never sources the Cloud SDK's PATH line in~/.bashrc(it lives after the interactive-shell guard's earlyreturn, confirmed by reading the file: a bare shell hits thatreturnon line 9 and never reaches the SDK source line near line 125). A session hit exactly this today and came within one step of telling the operator she needed to re-authenticate when she already was. ./lantern doctornow reports on both new files: missing, wrong permissions (want 0600), present but missing specific keys, or present and complete, each with the exact fix command. Stays severitywarning, never a gate, so a fresh clone with neither file still exits 0 (the onboarding-routing rule: a newcomer is never told they are blocked on credentials).
What is the current state? โ
All three pieces are built, live-verified, and pushed; nothing is deferred. โ
npm run env:bootstrapcreated both files for real on this machine:ls -lconfirmed mode0600for both (.lantern-agent-probe.envat 190 bytes,.lantern-test-accounts.envat 624 bytes), all 24 secrets fetched with zero failures.- The e2e script that actually needs these files was checked against the real one:
tooling/e2e/admin-signin-tier.e2e.mjs's ownparseEnvcorrectly readsAGENT_PROBE_EMAILas a non-empty string out of the probe file (value never printed; only presence and length checked). ./lantern doctorreports both files, and all four report states (missing / wrong mode / missing keys / complete) were exercised against synthetic scratch files, not just reasoned about, per thesafe-automationskill's checklist.- The concurrency question the
safe-automationskill requires asking ("what happens when three of these run at once?") was answered and then tested for real: six concurrent Node processes racing on one synthetic target file, using the exact pid-suffixed-tmp-plus-atomic-rename mechanism the real writer uses, produced one clean uncorrupted result with zero leftover tmp files. - Full scenario list, real commands, and real captured output:
docs/engineering/testing/runs/agent-credential-bootstrap/README.md. - Both files are gitignored (defensively; they live at
$HOME, outside the repo tree entirely, so git never saw them regardless) and confirmed invisible togit status. - Secret names were confirmed against
gcloud secrets list --project lantern-app-devrather than trusted from the brief. All 24 requested names matched exactly; zero renames were needed.
What else is related? โ
2026-06-12-onboarding-bootstrap-cli-design.md: the original design spec behindlantern doctor/lantern setup/bootstrap-env.mjs. This project extends that design with two more bootstrap targets; it does not replace any of it.#858: feat(onboarding): finish-line hook, doctor-green prints the day-one path: a different, unbuilt concern (a hook that fires once doctor goes fully green), not overlapping with this project's scope.- The
browser-testskill names exactly which keys each file needs and which scripts read them; thesafe-automationskill's concurrency question shaped the pid-suffixed tmp-file write. Both were read before building, per this project's own instructions.
Found but deliberately not fixed. โ
mergeIntoEnvFile(the pre-existing.env.localwriter inbootstrap-env.mjs) uses a non-pid-suffixed tmp path (${filePath}.tmp), so two concurrentenv:bootstrapruns could in principle interleave on that one write: the same class of race this project's own new writer (writeSecretFile) was built to avoid. Not touched here: it is pre-existing, untouched-by-incident behavior outside the three pieces asked for, and the fix is a two-line change if picked up later.- Several other scripts shell out to bare
gcloud:tooling/scripts/onboard-grant.mjs,tooling/scripts/set-admin-role.mjs,services/jobs/ingest-invoices/src/authSetup.js, assorteddeploy:devpackage.json scripts, and a handful of.shsetup scripts. Onlybootstrap-env.mjsandlantern.mjs'sdoctorwere switched to the resolver, because those are the two an agent session runs non-interactively today; the others are human-run, interactive-shell tools (deploys, onboarding grants) where PATH already resolves correctly. Widening the fix to them was judged out of scope for this side quest rather than silently left for someone to rediscover. - An ad hoc
gcloud ...command typed directly into a shell (not through either fixed script) still needs its full path or PATH support in a non-interactive agent shell. The operator's own preference was to resolve this in the tooling rather than edit her shell profile, which is what this project did; extending~/.bashrcto export the Cloud SDK's PATH unconditionally (outside the interactive-shell guard) would additionally cover that ad hoc case, but editing her shell profile was explicitly out of scope without her word, so it is named here as a recommendation, not done.