Porting · porting/2026-05-05-hetzner-python-gate.md Docs Home

Hetzner Python Gate

Date: 2026-05-05

Objective

Turn the remaining Python blocker into a repeatable server-side gate for the Hetzner rebuild.

This gate focuses only on the two Python services:

  • Zweistein Query Engine
  • Zweistein Ingestion Worker

It does not replace the full stack runbook. It gives the riskiest Python/Torch part its own early proof before the remaining application images are built.

What Was Added

  • scripts/hetzner-python-gate.mjs Server-guarded helper for Python preflight, Torch wheel probe, Query Engine build, Ingestion Worker build, import probes, Python subset startup, Compose inspection, and Query Engine health.
  • package.json scripts:
    • hetzner:python:plan
    • hetzner:python:commands
    • hetzner:python:preflight
    • hetzner:python:torch
    • hetzner:python:build:query
    • hetzner:python:build:ingestion
    • hetzner:python:build
    • hetzner:python:imports
    • hetzner:python:up
    • hetzner:python:ps
    • hetzner:python:health

Safety Rules

  • Heavy commands require native Linux x64/amd64.
  • The helper requires at least 40 GiB free disk unless HETZNER_PYTHON_GATE_ALLOW_LOW_DISK=1 is set intentionally.
  • Runtime commands require real ignored Hetzner env files:
    • configs/hetzner/frontend-build.env
    • configs/hetzner/zweistein-query-engine.env
    • configs/hetzner/zweistein-ingestion-worker.env
  • The helper does not print env values, logs, tokens, or database rows.
  • The local Mac can print the plan and command lines, but it is expected to fail hetzner:python:preflight.

Server Workflow

Run this on the Hetzner host after source sync, npm install, and real runtime config:

npm run hetzner:python:plan
npm run hetzner:python:commands

Then run the server gate:

npm run hetzner:python:preflight
npm run hetzner:env:check
npm run hetzner:python:torch
npm run hetzner:python:build:query
npm run hetzner:python:build:ingestion
npm run hetzner:python:imports
npm run hetzner:python:up
npm run hetzner:python:ps
npm run hetzner:python:health

If this passes, continue the full platform stack:

npm run hetzner:stack:build
npm run hetzner:stack:up
npm run hetzner:health:check
npm run hetzner:evidence:collect

Exact Command Shape

The helper prints these server commands through npm run hetzner:python:commands:

docker run --rm --platform linux/amd64 python:3.11-slim python -m pip install --dry-run --index-url https://download.pytorch.org/whl/cpu 'torch==2.10.0+cpu'
docker compose --env-file configs/hetzner/frontend-build.env -f docker-compose.hetzner.yml build query-engine
docker compose --env-file configs/hetzner/frontend-build.env -f docker-compose.hetzner.yml build ingestion-worker
docker compose --env-file configs/hetzner/frontend-build.env -f docker-compose.hetzner.yml run --rm --no-deps --entrypoint python query-engine -c "import main; print('query main import ok')"
docker compose --env-file configs/hetzner/frontend-build.env -f docker-compose.hetzner.yml run --rm --no-deps --entrypoint python ingestion-worker -c "import redis_worker; import message_processor; print('ingestion imports ok')"
docker compose --env-file configs/hetzner/frontend-build.env -f docker-compose.hetzner.yml up -d redis query-engine ingestion-worker
docker compose --env-file configs/hetzner/frontend-build.env -f docker-compose.hetzner.yml ps --format json redis query-engine ingestion-worker
curl -fsS http://127.0.0.1:5001/healthz

Running-State Gate

npm run hetzner:python:ps is a real gate, not just a display command.

It runs Docker Compose with JSON output, parses the returned service rows, and fails unless all three Python-subset services are present and running:

  • redis
  • query-engine
  • ingestion-worker

This is especially important for ingestion-worker, because it is a worker process rather than an HTTP service with its own health endpoint.

Local Verification Evidence

  • node --check scripts/hetzner-python-gate.mjs passed.
  • npm run hetzner:python:plan prints the ordered Python server gate.
  • npm run hetzner:python:commands prints exact Docker and Docker Compose commands.
  • npm run hetzner:python:commands now shows ps --format json redis query-engine ingestion-worker.
  • npm run hetzner:python:ps fails locally as expected before any server state is inspected, because the command is guarded for native Linux x64/amd64.
  • npm run hetzner:python:preflight fails locally as expected:
    • platform is darwin arm64;
    • Docker Compose is missing in the local Docker CLI;
    • free disk is about 7.6 GiB;
    • minimum Python gate disk is 40 GiB;
    • the helper is guarded for native Linux x64/amd64.

Current Boundary

This is still not Python runtime proof.

The actual gate must pass on the Hetzner/Linux host before the full port can be considered complete.