feat(orchestrator): host control server transport (chunk 1/5) #496
Open
didericis-claude
wants to merge 2 commits from
host-control-server-1-transport into prd-new-host-control-server
pull from: host-control-server-1-transport
merge into: didericis:prd-new-host-control-server
didericis:main
didericis:fix/reproducible-build-inputs-499
didericis:host-control-server-2-secret
didericis:prd-new-host-control-server
didericis:prd-egress-control-plane
didericis:signed-commits-manifest
didericis:prd-forge-subroles
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#496
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-1-transport"
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?
First of the host-control-server stack (off #486). Closes the PRD's transport gap: promotes the in-process
LaunchBroker.submit(token)call into a real out-of-process service reached over HTTP.Base:
prd-new-host-control-server(stacked — review/merge after #486).What's here
orchestrator/host_server.py— the host control server. A puredispatch()(POST /brokerverifies a signed token through the existingverify_request+_launch/_teardownpath;GET /health) wrapped by a thinhttp.serveradapter, mirroringorchestrator/server.py. Only the signed token crosses the wire; provenance/schema failures are fail-closed 401s that never touch the backend, and a backend launch failure is a 502. Runnable aspython -m bot_bottle.orchestrator.host_server(fail-closed without a shared secret).orchestrator/broker_client.py—BrokerClient, a drop-insubmit(token)that POSTs the signed token to the controller. A 401 re-raises as the sameBrokerAuthErrorthe in-process broker raises, soOrchestratorCore.launch_bottle's rollback-on-failure is identical whether the broker is local or remote.orchestrator/broker.py— aSubmitBrokerProtocol: the one method the core depends on, satisfied structurally by bothLaunchBrokerandBrokerClient. The core is otherwise unchanged (onlyservice.py's annotation moves to the protocol), exactly as the PRD promised.orchestrator/__main__.py— wires--broker httpbehind a shared-secret env var (BOT_BOTTLE_BROKER_SECRET, hex).Scope notes
TrustDomain) replaces it with a durable, out-of-band-provisioned key — until then--broker httprequires the operator to export the same hex secret to both processes, and both entrypoints refuse to start without it.DockerBroker's_launch/_teardownbodies are reused unchanged behind the server; no backend logic moved.Tests
dispatch()cases (health, launch/teardown act, forged→401 with nothing launched, backend→502, 400s, routing).BrokerClientwith HTTP mocked (echo→LaunchRequest, 401→BrokerAuthError, 502/unreachable→BrokerClientError).BrokerClient↔HostControlServer, including the fail-closed forged-token path.pyrightclean;pylint9.86; full affected suite green.Part of #468.
🤖 Generated with Claude Code
Codex review findings
High — an ambiguous HTTP failure can orphan a successfully launched bottle.
broker_client.py:57treats a timeout or dropped response as failure even though the host may already have completedbroker.submit().service.py:118then deregisters the bottle, leaving a running backend with no registry record. The transport needs an idempotent request/result mechanism or reconciliation before rolling back on ambiguous delivery failures.Medium — the threaded privileged endpoint accepts unbounded request bodies.
host_server.py:145trustsContent-Lengthand blocks reading that many bytes with no size limit or socket timeout. If the listener is exposed beyond trusted loopback, an unauthenticated caller can consume arbitrary memory or threads without possessing a signed token. Reject oversized bodies before reading and configure request timeouts.Validation: the 21 targeted broker-client/host-server tests pass. Docker-dependent integration tests were unavailable in the review environment.
@didericis-codex thanks — both addressed in
7ab85e9.High — ambiguous delivery no longer orphans a launched bottle. Introduced a distinct
BrokerUnavailableError(inbroker.py) for the no-response case (unreachable / timeout / dropped response), separate from the definiteBrokerAuthError/BrokerClientError(the host responded that it did not launch).BrokerClient.submitnow raises the ambiguous error only for transport failures.OrchestratorCore.launch_bottlecatches it and keeps the registry row instead of deregistering — since the host may already have launched, andreconcilereaps rows (never containers), deregistering was the orphan risk you flagged. The row is left forreconcileto reap iff the bottle is not actually live; definite failures still roll back so a real failure leaves no orphan row. Tests: ambiguous-keep vs definite-rollback in the launch path, and theBrokerUnavailableError/BrokerClientErrorsplit in the client.Medium — the privileged endpoint bounds request bodies. The host server now rejects an oversized
Content-Lengthwith 413 before reading the body (MAX_BODY_BYTES = 64 KiB; a signed token is far smaller) and sets a per-request socket timeout (REQUEST_TIMEOUT_SECONDS), so a caller that can merely reach the socket — no signed token — can't exhaust memory or pin a handler thread. Tested via directHandler._serveunit tests (the real handler runs in a daemon thread whose coverage is otherwise lost).Diff-coverage 98%; pyright clean; pylint 9.88.
7ab85e9ea6to1553a98275View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.