mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-07 08:27:12 +00:00
clippy.toml already bans std::env::{set_var,remove_var} via
disallowed_methods, and every existing call site carries a scoped
#[expect(clippy::disallowed_methods, reason = "...")]. The shell grep
is redundant and forced a second, less granular allowlist.
Also update server-secrets-strategy.md to describe clippy as the
enforcement mechanism.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.4 KiB
3.4 KiB
Server Secrets Strategy
This document defines how Fabro handles server-level secrets.
Core Rules
ServerSecretsis the canonical server-secret reader.- It reads 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.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.
Active Server Secrets
These values belong to the server runtime and are read via state.server_secret(...):
| 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 |
GITHUB_APP_PRIVATE_KEY |
GitHub App credentials |
GITHUB_APP_WEBHOOK_SECRET |
GitHub webhook verification |
GITHUB_APP_CLIENT_SECRET |
GitHub OAuth login |
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_SECRETwhen GitHub auth is enabled
- Other server secrets remain lazy/feature-specific rather than universal boot blockers.
Provisioning
Server secrets come from one of two sources:
- Platform env for 12-factor deployments
server.envwritten by install flows
There is no compatibility layer for removed secrets and no startup-time secret generation.
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, 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, devcontainer setup, 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 server secrets with construction-time stubs (
EnvSource,StubEnv) or by writingserver.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
- Provision it through platform env or install-written
server.env. - Read it through
state.server_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.