bot-bottle-vault: secret custody + phone-to-host secret sync (VaultProvider interface) #328
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Primary design goal
Everything below is in service of that goal. It splits into two independent problems that should not be solved by the same mechanism:
bot-bottle-vault.Conflating these two would mean either the dashboard becomes a secrets store (weakens the sidecar-only-injection invariant) or the vault becomes a command channel (expands its threat model for no reason). Keep them separate; the dashboard is a client of the vault, same as any host is.
Relationship to #327
#327's "store env vars / secrets" section defined secrets as custody, not storage: encrypted at rest, masked on every read API, delivered to the sidecar at launch, referenced by name in a definition. This issue is that backend, pulled out into its own component so it can:
Goals
VaultProvidercontract that bot-bottle's sidecar/orchestrator calls to resolvesecret-name -> valueat launch time, entirely within the sidecar's existing injection boundary (agent never sees it, same as today's host-secret sidecar model).bot-bottle-vault), interfacing with bot-bottle core over theVaultProvidercontract — not a new core dependency.bot-bottle-vaultas an opinionated reference implementation second, once we know what it needs to do differently.Non-goals (v1)
Design:
VaultProviderinterfaceA minimal contract the sidecar/orchestrator speaks, so the backend is swappable:
resolve(name) -> value— called by the sidecar at launch, over the sidecar's existing network boundary (never exposed to the agent).list() -> [{name, updated_at}]— for the dashboard's secrets UI (#327'sGET /api/secrets); values never included.set(name, value)/rotate(name, value)/delete(name)— write-only from the client's perspective.Any provider below can sit behind this contract. Bot-bottle core only ever talks to the contract.
Provider options (buy vs. build)
bwsCLI/SDK, machine accountsbot-bottle-vault(custom, Holepunch/Hyperbee-based)Recommendation: ship the
VaultProvidercontract first, wire it to Bitwarden Secrets Manager (self-hosted) as the initial backend, and treatbot-bottle-vaultas a future reference implementation once we know the contract is right — not the v1 dependency.Phone workflow (target end state)
./cli.py/ bottle install flow).bot-bottle-vault's own client) to the same account.resolve(name)at launch, injects the value — the phone and the agent both never see the value pass through the agent's environment.Security notes
bot-bottle-vault) stores ciphertext only; it holds no keys capable of decrypting values at rest.printenv-shows-proxy-URLs-only guarantee) — the vault only changes where secret values come from, not how they reach the agent.Open questions
VaultProviderconfig live — per-account setting in the dashboard (#327), or a host-level config?bot-bottle-vaultitself: Holepunch/Hyperbee vs. Tailscale+stdlib-HTTP for its internal transport — deferred until the contract proves out with an existing provider.Acceptance (v1)
VaultProvidercontract defined and implemented against by the sidecar/orchestrator, gated behind the same account scoping as #327's definitions.VaultProviderimplementation.GET /api/secrets) reflects names/metadata from the vault, never values.