docs(prd): 0070 secret-handling as a future pattern (SecretProvider, #355)

Add a "Secret handling — FUTURE pattern (not v1)" subsection: vault as a
separate trust domain holding long-lived roots, deriving short-lived
scoped creds where the upstream allows (with the honest limit that a
compromised proxy can still abuse currently-authorized access). The
mechanism is a generic, user-extensible SecretProvider that generalizes
PRD 0048's DeployKeyProvisioner and drops into the manifest wherever a raw
token is accepted — discovered from ~/.bot-bottle/contrib like user
AgentProviders. Marked explicitly as not required for the initial
orchestrator; tracked as #355.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
This commit is contained in:
2026-07-13 12:10:45 -04:00
parent 2a473eb404
commit c46d392b44
+47
View File
@@ -117,6 +117,48 @@ If a backend can't honor the invariant, source-IP consolidation is not
safe there and that backend keeps per-bottle sidecars. The invariant is a
hard precondition, not an aspiration.
### Secret handling — a FUTURE pattern (not v1)
> **Status: future / not required for the initial orchestrator.** The
> initial cut can inject secrets as today; this section records the
> direction so v1 doesn't paint itself into a corner. Tracked as its own
> work in **#355** (generic `SecretProvider`).
The residual weakness after all of the above is **long-lived credential
concentration** — the data-plane proxies must hold every bottle's upstream
tokens because the agent must never see them. You can't policy-gate the
component whose job is to *use* all the secrets, so a full RCE of a proxy
drains its authorized set regardless. Two moves bound this without
pretending to prevent it:
1. **Vault as a separate trust domain.** The long-lived *roots* live in a
distinct process (ideally its own VM) that the byte-parsing data plane
never shares memory with. The proxies request secrets from it; the
crown jewels are not in the process an agent-facing parser can pop.
2. **Derive short-lived, scoped creds where the upstream allows it.** The
vault holds the root and mints expiring, narrowly-scoped credentials
per bottle-start (or per request) — GitHub App installation tokens,
OAuth/STS token exchange, forge deploy tokens. A compromise then leaks
short-lived material, not permanent keys. For upstreams stuck on static
keys the vault passes the value through (only the at-rest / audit /
revocation benefits apply, not lifetime reduction) — this residue is
accepted and documented, not solved.
Even a plain per-request fetch (no derivation) still buys **at-rest**
reduction (process memory holds only in-flight secrets), a **detection /
rate-limit / revocation chokepoint**, and clean **cross-component
scoping** (an egress RCE can't request git-gate's creds). It does *not*
prevent abuse of currently-authorized access during the compromise window.
**Mechanism (see #355):** generalize the existing `DeployKeyProvisioner`
(PRD 0048) into a user-extensible **`SecretProvider`** droppable into the
manifest anywhere a raw token value is accepted, discovered from
`~/.bot-bottle/contrib/<name>/secret_provider.py` exactly as user
`AgentProvider`s are — because maintaining every forge/cloud/OAuth
provider in-tree is untenable. The orchestrator's vault mints via these
providers; but the abstraction is shippable independently and today's
per-bottle sidecars can use it too.
## Design
### The contract (backend-agnostic)
@@ -247,6 +289,11 @@ Keep the sidecar **service one shared thing** throughout.
is what lets a dedicated `gitea` runner user drop the root-equivalent
`docker` group — it only needs broker-socket access + `kvm`/pool group
membership. This work unblocks it.
- **Generic `SecretProvider` (#355):** the future secret-handling
mechanism (see "Secret handling") — generalizes PRD 0048's
`DeployKeyProvisioner` into a user-extensible provider that mints
short-lived creds. Shippable independently; the orchestrator's vault
mints through it.
- **PR #350 (netpool single-source):** the same "one source per fact,
composition over parallel hierarchies" discipline the contract follows.