PRD 0070's rule — only the orchestrator opens bot-bottle.db; the data plane reaches state through the control-plane RPC — was not in force. Three data-plane daemons held a direct read-write handle on the shared SQLite file: the supervise MCP server, the egress DLP addon (the most attack-exposed process, TLS-bumping hostile traffic), and the git-gate pre-receive hook. An RCE in any of them could read every bottle's plaintext identity_token and forge attribution fleet-wide (issue #469). Add the agent half of the supervise flow to the control plane: POST /supervise/propose -> queue a proposal, 201 {proposal_id} POST /supervise/poll -> non-blocking decision poll, 200 {status,...} Both attribute the caller by (source_ip, identity_token) exactly like /resolve — never a caller-supplied slug — so a bottle can only ever queue or read its own proposals even if the data plane is compromised. A decided poll archives server-side, preserving the archive-after-read contract. Data plane: the supervise server, egress addon, and git-gate hook now queue/poll through PolicyResolver.propose_supervise / poll_supervise instead of opening the DB. supervise_server keeps its ~30s grace window by polling the RPC; egress keeps its safelist keyed by resolved bottle; the git-gate hook gets (source_ip, identity_token) from the CGI env. Packaging: drop the DB bind-mount and SUPERVISE_DB_PATH from the data-plane containers/VMs (docker gateway + infra, macOS infra, firecracker infra). The orchestrator remains the sole opener of the one file via BOT_BOTTLE_ROOT / host_db_path(). Update PRD 0070: the rule is now in force; remove the transitional caveat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Product requirement docs
One PRD per feature: what to build, why, and how it's scoped. The PRD
is the durable spec — it should stand on its own without a Gitea issue
thread (see ../README.md for when a PRD is the right
document vs. a research note or a decision record).
Naming and numbering
New PRDs use a prd-new-<kebab-title>.md placeholder name while the PR
is open. On merge to main a CI workflow assigns the next sequential
number (0024-…, 0025-…), renames the file, and updates the title
header. Numbers are never reused; gaps are fine.
Once numbered, the filename stays fixed for the life of the doc.
Status
The Status: line near the top tracks the PRD's lifecycle:
- Draft — proposed, not yet shipped.
- Active — the design has shipped to
mainand is in effect. - Superseded by PRD NNNN — replaced by a later PRD; kept for history.
- Retargeted by PRD NNNN — folded into a later PRD's scope.
Format
# PRD prd-new: <short title> ← placeholder; CI fills in the number on merge
- **Status:** Draft
- **Author:** <who>
- **Created:** YYYY-MM-DD
- **Issue:** #<n> # optional — convenience pointer only
## Summary
One paragraph: what this builds and the pain it solves.
## Problem
The current state and why it's inadequate.
## Goals / Success Criteria
Bullets a reviewer can check the finished work against.
## Non-goals
What this explicitly does not do — and won't, to head off scope creep.
## Scope
In scope / out of scope, when the boundary needs spelling out.
## Design
How it works: schema, data flow, diagrams, algorithms as needed.
## Implementation chunks
Ordered, mergeable steps (optional; for multi-PR features).
## Open questions
Unresolved decisions — resolve or fold into Design before shipping.
Sections are a guide, not a straitjacket: drop the ones a given PRD doesn't need (a small change rarely needs Scope or Implementation chunks) and add others where they help (e.g. Testing strategy, Alternatives considered, References). Keep the rationale self-contained — inline the reasoning rather than linking out to an issue thread, so the PRD survives a move off Gitea.