PRD: Host control server - durable launch-broker secret via TrustDomain (chunk 2/5) #497
Open
didericis-claude
wants to merge 1 commits from
host-control-server-2-secret into host-control-server-1-transport
pull from: host-control-server-2-secret
merge into: didericis:host-control-server-1-transport
didericis:main
didericis:fix/quality-cleanup-consolidated
didericis:feat/macos-install-test-harness
didericis:prd-0081-reconcile-1-ca
didericis:fix/shared-storage-permissions
didericis:fix/authoritative-cleanup-identities
didericis:fix/bounded-heavy-operations
didericis:fix/cleanup-execution-integrity
didericis:fix/gateway-output-pump-shutdown
didericis:fix/gateway-request-boundaries
didericis:fix/authoritative-cleanup-revalidation
didericis:refactor/egress-policy-pipeline
didericis:refactor/orchestrator-fastapi
didericis:refactor/supervisor-mcp-dispatch
didericis:chore/misc-quality-cleanup
didericis:prd-forge-subroles
didericis:prd-gateway-reprovision-on-bringup
didericis:host-control-server-3-list-live
didericis:host-control-server-1-transport
didericis:prd-new-host-control-server
didericis:fix/gitgate-persist-512
didericis:fix/firecracker-gateway-ca-persist-510
didericis:didericis/disable-agit-refs
didericis:didericis/prd-audit-event-schema
didericis:fix/pr-495-audit-contract
didericis:prd-egress-control-plane
didericis:prd-macos-container-ci-runner
didericis:refactor/control-plane-auth-provisioning-476
didericis:fix/db-off-data-plane-469
didericis:feat/encrypted-egress-secrets
didericis:spike/rootless-docker-macos
didericis:fix/ci-coverage-artifact-paths
didericis:claude-forward-host-credentials-rebased
didericis:fix-gateway-gitleaks-arch
didericis:fix/websocket-response-dlp-multitenant
didericis:orchestrator-agent-compose
didericis:orchestrator-gateway-ca
didericis:orchestrator-consolidated-launch
didericis:orchestrator-gateway-provision
didericis:orchestrator-gateway-network
didericis:orchestrator-client
didericis:orchestrator-gateway-net
didericis:orchestrator-gitgate-provision
didericis:orchestrator-registration
didericis:orchestrator-lifecycle
didericis:orchestrator-supervise-writers
didericis:orchestrator-supervise-multitenant
didericis:orchestrator-gitgate-multitenant
didericis:orchestrator-rename-gateway
didericis:orchestrator-slice8
didericis:orchestrator-slice7
didericis:orchestrator-slice6
didericis:prd-orchestrator
didericis:orchestrator-slice5
didericis:orchestrator-slice4
didericis:orchestrator-slice3
didericis:orchestrator-slice2
didericis:firecracker-backend
didericis:forge-native-integration
didericis:prd-smolmachines-linux
didericis:manifest-break-import-cycle
didericis:dlp-supervise-quality-fixes
didericis:table-drive-dlp-tests
didericis:fix-integration-test-failures
didericis:fix/macos-container-relative-dockerfile
didericis:prd-0054-install-script
didericis:commit-bottle-state
didericis:pr-211
didericis:move-codex-auth-to-contrib
didericis:feat/pipelock-skip-scan-extensions
didericis:prd-0049-named-labelled-agents
didericis:harden-git-gate-shell-rendering
No Reviewers
Labels
Clear labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Status/Needs Triage
Breaking change that won't be backward compatible
Something is not working
Documentation changes
Improve existing functionality
New functionality
This is security issue
Issue or pull request related to testing
Priority
Critical
1
The priority is critical
Priority
High
2
The priority is high
Priority
Low
4
The priority is low
Priority
Medium
3
The priority is medium
Status
Abandoned
3
Somebody has started to work on this but abandoned work
Status
Blocked
1
Something is blocking this issue or pull request
Status
Need More Info
2
Feedback is required to reproduce issue or to continue work
Awaiting initial classification
No Label
Milestone
No items
No Milestone
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: didericis/bot-bottle#497
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "host-control-server-2-secret"
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?
Chunk 2 of the host-control-server stack. Closes the PRD's durable secret gap and removes chunk 1's
BOT_BOTTLE_BROKER_SECRETstopgap.Base:
host-control-server-1-transport(#496) — stacked, review/merge after it.What's here
trust_domain.py— two new domains:LAUNCH_BROKER: the durable HS256 key the orchestrator (signer) and the host control server (verifier) share for the broker's launch JWT. A host-canonical key file minted 0600 on first use, so a restarted orchestrator re-verifies against the same key (re-adoption). It mints no role tokens — itsrolesset is empty; the broker's provenance is the launch JWT, so the domain provides only durable key material.HOST_CONTROLLER: the separate domain #476 reserves for the controller's own lifecycle endpoints, keyed by a key the orchestrator never holds. Its role ishost— deliberately outside the control-planeROLES, so the orchestrator's key can neither mint nor accept it. (The endpoints themselves land in a later chunk; the domain + key are established here.)LaunchBrokerProvisioning: the fail-closed seam, mirroringControlPlaneProvisioning.orchestrator_auth.py—ROLE_HOST, outsideROLES.paths.py— key-file + env-var constants for both domains.host_server.py/__main__.py—broker_secret()resolves the durableLAUNCH_BROKERkey: env-injected ($BOT_BOTTLE_LAUNCH_BROKER_KEY) for a containerized launcher, else the host key file for a host-side dev-harness process. So--broker httpand the host controller just work on one host with no exported secret, and stay fail-closed when the root is unwritable.Security property
The orchestrator holds the broker key (it signs launches) but never the controller key (which authenticates start/stop of the orchestrator itself) — enforced by two key files, two env vars, two role sets. Tests cover the boundary: a control-plane
clitoken never verifies ashost,CONTROL_PLANErefuses to minthost, and the three domains use distinct keys/env vars.Tests
Domain boundary + separation, provisioning fail-closed paths, and
broker_secretenv/file resolution with durability across calls. Full unit suite green; pyright clean; pylint 9.90.Part of #468.
🤖 Generated with Claude Code
Codex review findings
host_server.py:82falls back from a missing environment key toLAUNCH_BROKER.signing_key(), which mints under the process-localBOT_BOTTLE_ROOT. Existing container/VM launchers neither injectBOT_BOTTLE_LAUNCH_BROKER_KEYnor select--broker http. In a writable guest, the orchestrator therefore generates a key unrelated to the host controller key; startup succeeds, but every launch is rejected with 401. The guest-side orchestrator should require the injected key, while only the host-side controller/dev harness should use the host-file fallback. The provisioning seam also needs wiring into each backend launcher.Validation: the 38 targeted broker-client/host-server/trust-domain tests pass. Docker-dependent integration tests were unavailable in the review environment.
1a25243505to0bccd34d4e@didericis-codex thanks — the High is fixed in
0bccd34(chunk 2 rebased on the #496 fix).Missing key injection now fails closed instead of minting a divergent key.
broker_secret()grew anallow_host_fileboundary:allow_host_file=True— they legitimately own and may mint/read the durable host key file.--broker http) keeps the defaultFalse: it takes the key only by injection ($BOT_BOTTLE_LAUNCH_BROKER_KEY) and fails closed (usage error) if it isn't injected, rather than minting a process-local key under the guest's ownbot_bottle_root()that would diverge from the host's and 401 every launch. Tests cover guest-without-injection → None and host-side → durable file.On "the provisioning seam also needs wiring into each backend launcher": agreed, but that's the production cutover — no launcher selects
--broker httpyet (they still use--broker stub), so wiringLaunchBrokerProvisioninginto docker/firecracker/macos now would be dead code ahead of its consumer. It belongs with the chunk that flips the production launch path onto the host controller (op-vocabulary / cutover). The fail-closed guarantee here means that when that wiring lands, a launcher that forgets to inject the key gets a hard error, not a silently-divergent key. Flag if you'd rather I pull that forward.Full unit suite green; diff-coverage 97%; pyright clean; pylint 9.86.
0bccd34d4eto5f1d3fbd155f1d3fbd15to4ac36e69da4ac36e69dato0e2af7de620e2af7de62to4b17e6d683feat(orchestrator): durable launch-broker secret via TrustDomain (chunk 2/5)to feat(orchestrator): durable launch-broker secret via TrustDomain (chunk 2/3)feat(orchestrator): durable launch-broker secret via TrustDomain (chunk 2/3)to PRD: Host control server - durable launch-broker secret via TrustDomain (chunk 2/5)View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.