diff --git a/dockerfiles/Dockerfile.hermes-switchyard-brokers b/dockerfiles/Dockerfile.hermes-switchyard-brokers new file mode 100644 index 000000000..1e848532f --- /dev/null +++ b/dockerfiles/Dockerfile.hermes-switchyard-brokers @@ -0,0 +1,18 @@ +# dockerfiles/Dockerfile.hermes-switchyard-brokers +FROM python:3.13-slim@sha256:9662417aace5ae7b8e2609cce472b72a8958e134ba372808abe9cc1a0c0125e6 + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +RUN pip install --no-cache-dir httpx==0.28.1 \ + && groupadd --gid 10000 switchyard \ + && useradd --uid 10000 --gid 10000 --home-dir /nonexistent --no-create-home switchyard + +COPY --chown=10000:10000 services/hermes/scripts/claude_oauth_broker.py /opt/brokers/claude_oauth_broker.py +COPY --chown=10000:10000 services/hermes/scripts/worker_route_broker.py /opt/brokers/worker_route_broker.py +COPY --chown=10000:10000 services/hermes/scripts/routing_catalog.py /opt/brokers/routing_catalog.py + +RUN python -m py_compile /opt/brokers/*.py + +USER 10000:10000 +WORKDIR /opt/brokers diff --git a/services/hermes/switchyard-deployment.yaml b/services/hermes/switchyard-deployment.yaml index a0fd975ba..d2462b865 100644 --- a/services/hermes/switchyard-deployment.yaml +++ b/services/hermes/switchyard-deployment.yaml @@ -22,7 +22,7 @@ spec: labels: app: hermes-switchyard annotations: - ai.bstein.dev/config-rev: "20260811-switchyard-authority-v12" + ai.bstein.dev/config-rev: "20260812-switchyard-authority-v13" prometheus.io/scrape: "true" prometheus.io/port: "9005" prometheus.io/path: /metrics @@ -127,10 +127,10 @@ spec: - name: tmp mountPath: /tmp - name: claude-oauth-broker - image: registry.bstein.dev/bstein/hermes-agent@sha256:9b4c00a25fd08f0df3bcb800d755bd48576fb65af5ab0df40058fbb78a147e43 + image: registry.bstein.dev/bstein/hermes-switchyard-brokers@sha256:ee7e95e060ef8083da505162d7e9030daba15fdd828cc047bbcbe6aa409d2083 imagePullPolicy: IfNotPresent command: - - /opt/hermes/.venv/bin/python + - python - /opt/coordinator/claude_oauth_broker.py env: - name: HERMES_CLAUDE_OAUTH_TOKEN_FILE @@ -184,10 +184,10 @@ spec: mountPath: /routing-catalog readOnly: true - name: worker-route-broker - image: registry.bstein.dev/bstein/hermes-agent@sha256:9b4c00a25fd08f0df3bcb800d755bd48576fb65af5ab0df40058fbb78a147e43 + image: registry.bstein.dev/bstein/hermes-switchyard-brokers@sha256:ee7e95e060ef8083da505162d7e9030daba15fdd828cc047bbcbe6aa409d2083 imagePullPolicy: IfNotPresent command: - - /opt/hermes/.venv/bin/python + - python - /opt/coordinator/worker_route_broker.py ports: - name: worker-route diff --git a/testing/tests/test_hermes_chat_quality.py b/testing/tests/test_hermes_chat_quality.py index f7f3b08af..2c3f44ff7 100644 --- a/testing/tests/test_hermes_chat_quality.py +++ b/testing/tests/test_hermes_chat_quality.py @@ -639,6 +639,27 @@ def test_claude_broker_exposes_capacity_exhaustion_as_retryable(monkeypatch): assert module._normalized_upstream_status(403, exhausted) == 403 +def test_switchyard_brokers_use_the_small_dedicated_image(): + """Control-plane brokers must not pull the full multi-gigabyte agent image.""" + dockerfile = (ROOT / "dockerfiles" / "Dockerfile.hermes-switchyard-brokers").read_text() + assert "httpx==0.28.1" in dockerfile + assert "claude_oauth_broker.py" in dockerfile + assert "worker_route_broker.py" in dockerfile + assert "routing_catalog.py" in dockerfile + + deployment = _documents(HERMES / "switchyard-deployment.yaml")[0] + containers = { + container["name"]: container + for container in deployment["spec"]["template"]["spec"]["containers"] + } + expected = ( + "registry.bstein.dev/bstein/hermes-switchyard-brokers@" + "sha256:ee7e95e060ef8083da505162d7e9030daba15fdd828cc047bbcbe6aa409d2083" + ) + assert containers["claude-oauth-broker"]["image"] == expected + assert containers["worker-route-broker"]["image"] == expected + + def test_image_broker_returns_bytes_and_removes_owner_cache(tmp_path: Path, monkeypatch): """The broker must not retain a family user's generated image.""" broker_path = HERMES / "scripts" / "image_broker.py"