## Summary Remove devcontainer support from the product surface and codebase: the parser crate, workflow bridge, lifecycle execution path, typed events, CLI progress rendering, generated client field, and public/internal documentation references are all gone. ## What Changed - Deleted the dedicated parser crate and removed its Cargo dependencies and lockfile entries. - Removed workflow initialization paths that resolved repository devcontainer metadata, applied Daytona snapshots from it, merged environment variables from it, or ran its lifecycle commands. - Removed the typed event variants and CLI progress handlers for the retired lifecycle events while leaving shared unknown-event handling intact. - Cleaned the generated TypeScript client and tracked docs so repository search has no remaining devcontainer references outside git history. ## Verification - `cargo +nightly-2026-04-14 fmt --all` - `cargo +nightly-2026-04-14 fmt --check --all` - `cargo build --workspace` - `cargo nextest run -p fabro-types` - `cargo nextest run -p fabro-workflow` - `cargo nextest run -p fabro-cli run_progress` - `cd lib/packages/fabro-api-client && bun run generate && bun run typecheck` - `cargo metadata --no-deps --format-version 1 | rg -i "fabro-devcontainer|devcontainer"` - `rg -n -i "devcontainer|dev container|dev-container|dev_container|fabro-devcontainer|\\.devcontainer" . --glob '!target/**' --glob '!.worktrees/**'` --- [](https://github.com/EveryInc/compound-engineering-plugin) 🤖 Generated with GPT-5 via [Codex](https://openai.com/codex)
4.9 KiB
Server Secrets Strategy
This document defines how Fabro handles server-level secrets.
Core Rules
ServerSecretsis the canonical reader for bootstrap server secrets only.- It reads bootstrap secrets from
process envand<storage>/server.env. - Resolution is snapshot-based: env and file are read once at construction, then treated as immutable for the life of the process.
process envwins overserver.envon conflicts.- Optional integration secrets are vault-only in server runtime. Do not add optional server integrations to
ServerSecretsor add new runtime env fallback paths. fabro server startnever generates secrets. Missing required secrets are a startup error.std::env::set_varandstd::env::remove_varare banned workspace-wide. Tests are not exempt. Enforced by clippy viadisallowed_methodsinclippy.toml; intentional exceptions must be annotated with a scoped#[expect(clippy::disallowed_methods, reason = "...")]at the call site.
Bootstrap Server Secrets
These values may be read via state.server_secret(...) because the server can need them before optional integrations are available:
| Secret | Used by |
|---|---|
SESSION_SECRET |
Cookie encryption and JWT signing derivation |
FABRO_DEV_TOKEN |
Dev-token user auth when server.auth.methods includes dev-token |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN |
Static S3 object-store credentials for server storage builders |
These optional integration secrets are not server bootstrap secrets. They are read from the vault only:
- LLM provider API keys and OAuth credential records
GITHUB_TOKENGITHUB_APP_PRIVATE_KEYGITHUB_APP_CLIENT_SECRETGITHUB_APP_WEBHOOK_SECRETFABRO_SLACK_APP_TOKENFABRO_SLACK_BOT_TOKENDAYTONA_API_KEYBRAVE_SEARCH_API_KEY
FABRO_JWT_PRIVATE_KEY and FABRO_JWT_PUBLIC_KEY are removed. SESSION_SECRET is the single auth root.
Startup
- Foreground and daemon startup use the same validation path.
- Required-at-startup secrets are:
SESSION_SECRETFABRO_DEV_TOKENwhen dev-token auth is enabledGITHUB_APP_CLIENT_SECRETfrom the vault when GitHub auth is enabled
- Requiredness is independent from source. GitHub auth can require a vault secret at startup even though it is not a bootstrap
ServerSecretsvalue. - Other optional integration secrets remain lazy/feature-specific rather than universal boot blockers.
Provisioning
Bootstrap secrets come from one of two sources:
- Platform env for 12-factor deployments
server.envwritten by install flows
Optional integration secrets are provisioned into the vault, usually with fabro secret set or fabro install.
There is no startup-time secret generation. A temporary startup migration moves recognized legacy optional secrets from process env or server.env into the vault, removes matching server.env entries after writing a backup, and logs conflicts by key name only. Runtime lookup remains vault-only after that migration step. See migrations-strategy.md for the migration pattern.
Subprocess Boundaries
- Worker and render-graph subprocesses start from
env_clear()and re-add only explicit allowlisted variables. - Authority-bearing values are re-injected intentionally. For worker subprocesses this is
FABRO_WORKER_TOKEN, plus any explicitly required internal value such as a vault-derivedGITHUB_APP_PRIVATE_KEY; it is not user auth state such asFABRO_DEV_TOKENorauth.json. - The worker reads
FABRO_WORKER_TOKENfrom its env at startup (inmain()before Tokio initializes) and immediately callsstd::env::remove_varto scrub it. The token then flows through function arguments torunner::execute. Every descendant process (hooks, sandbox commands, MCP stdio, etc.) therefore inherits a worker env that no longer contains the bearer, so an unscrubbed spawn site cannot leak it. - The daemon child inherits the parent env unchanged except for output-format hygiene (
FABRO_JSONremoval).
Tests
- In-process tests must inject bootstrap server secrets with construction-time stubs (
EnvSource,StubEnv) or by writingserver.env. - In-process tests for optional integrations must write the vault and must not rely on process env or
server.env. - Subprocess tests must set child env with
Command::env. - Tests must not mutate the process-wide environment.
Rotation
- Secret rotation requires restart.
- Live rotation is intentionally unsupported.
Adding A New Server Secret
- Classify it in
fabro-staticasBootstraporOptionalVault. - For bootstrap secrets, provision through platform env or install-written
server.env, then read throughstate.server_secret(...). - For optional integration secrets, provision through the vault and read through
state.vault_secret(...). - Decide explicitly whether startup should fail when it is absent.
- If a worker or render subprocess needs it, re-inject it explicitly rather than broadening inheritance casually.