feat(orchestrator): slice 2 — launch lifecycle + signed launch-broker (#352)
lint / lint (push) Successful in 2m2s
test / unit (pull_request) Successful in 57s
test / integration (pull_request) Successful in 18s
test / coverage (pull_request) Successful in 1m6s

Second slice of PRD 0070, still a backend-neutral dev-harness:

  * orchestrator/broker.py — the launch-broker contract. A LaunchRequest is
    structured (static ids/flags only — bottle id, pool slot, a
    content-addressed image_ref; never a path/argv) and signed as a compact
    HS256 JWT so the broker verifies PROVENANCE before acting: a compromised
    co-located component can't forge a launch without the shared secret.
    verify_request is fail-closed (bad sig / malformed / off-schema -> raise).
    Stdlib only (no runtime deps). Ships a StubBroker that records verified
    requests for the harness/tests.
  * orchestrator/service.py — the Orchestrator: owns the registry and brokers
    the lifecycle. launch_bottle mints the bottle + sends a signed launch,
    rolling the registry entry back if the launch fails (no orphans);
    teardown_bottle brokers teardown then deregisters; attribute delegates.
  * control_plane.py — POST /bottles now launches, DELETE tears down (both go
    through the Orchestrator + broker). dispatch/server take an Orchestrator.
  * __main__.py wires an ephemeral secret + StubBroker for the harness.

Tests: broker sign/verify round-trip, tamper/wrong-secret/malformed/off-schema
rejection, StubBroker fail-closed; Orchestrator launch->registry->attribute,
teardown, rollback-on-broker-failure; control-plane updated for launch/teardown.
Full suite green (only the pre-existing /bin/sleep errors); harness does
launch -> attribute -> teardown over HTTP.

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 14:42:35 -04:00
parent 6847fdf0ab
commit 9226d45041
8 changed files with 505 additions and 53 deletions
+10 -1
View File
@@ -12,11 +12,14 @@ container packaging. Wrapping this exact service in a backend-native unit
from __future__ import annotations
import argparse
import secrets
from pathlib import Path
from .. import log
from .broker import StubBroker
from .control_plane import make_server
from .registry import RegistryStore, default_db_path
from .service import Orchestrator
def main(argv: list[str] | None = None) -> int:
@@ -33,7 +36,13 @@ def main(argv: list[str] | None = None) -> int:
registry = RegistryStore(args.db)
registry.migrate()
server = make_server(registry, host=args.host, port=args.port)
# Dev-harness wiring: an ephemeral signing secret + a stub broker that
# records launches instead of starting anything. A real backend broker
# (docker, then firecracker) drops in here later.
secret = secrets.token_bytes(32)
orchestrator = Orchestrator(registry, StubBroker(secret), secret)
server = make_server(orchestrator, host=args.host, port=args.port)
bound_host, bound_port = server.server_address[0], server.server_address[1]
log.info(
"orchestrator control plane listening",