Deployed on corviduo-dev, co-located with the worldtree-personal muninn
watcher. ingestion_root=/data/state/ingestion (shared state volume, byte-
identical to the watcher); runs as uid 1000 to write the queue; staging
bound :ro at the ratified /mnt/muninn-staging/mimir-inbox (local placeholder
until the shared mount + mimir-inbox writer land). Boot verified: /ping
{"service":"ok"}, /health watcher.running=true (byte-identity proven).
Image built out-of-band with the Gitea read token as a BuildKit secret.
Real config (bearer keys) lives on-server at /opt/docker/conf 0600.
2.9 KiB
muninn-gate
WG-internal HTTP front door for the Muninn ingestion queue (Worldtree #377).
Path-addressed submit, list/status, cancel/retry, watcher-liveness health.
Upstream: vh/muninn-gate (no Dockerfile there — this stack owns containerization).
Where it runs
corviduo-dev (10.250.50.152), co-located with the worldtree-personal muninn
watcher (worldtree-personal-worldtree-muninn-1). infra-ops-managed stack,
separate from the worldtree CI/CD compose. Bind: 10.250.50.152:8090, network_mode: host.
Key wiring (why it's shaped this way)
ingestion_root: /data/state/ingestion— theworldtree-personal_worldtree-statevolume, mounted at/data/state. Byte-identical path to the watcher's view; the dispatch records absolute paths and both processes read each other's. Acceptance:/health→watcher.running: trueproves this byte-identity (gate is reading the heartbeat the watcher writes).no_heartbeatwith the watcher up = root mismatch.user: "1000:1000"— the ingestion dir isvh:vh 0755; the gate must run as uid 1000 to write the queue (non-root, least-privilege). Overrides the image user.- staging —
/mnt/muninn-staging/mimir-inbox, same absolute path bound:roin BOTH the gate and the watcher (worldtree-dev's b162). Currently a LOCAL placeholder dir; becomes the shared mount when mimir-inbox (the writer) lands. No gate-side check can tell a real share from an empty dir (docker fabricates a missing bind source) — the guard is operational: confirm the host mount exists before wiring/ repointing a bind. Repoint needs a restart (config is read once at boot). - health probe uses
/ping, not/health—/healthis always-200 by design (watcher-down is report content, not a status code), so it must never be the liveness probe or it would restart the wrong container.
Config (single-writer, infra-ops)
Real config with bearer-key secrets lives on the server at
/opt/docker/conf/muninn-gate/muninn-gate.yaml (1000:1000, 0600, gitignored).
Redacted schema: conf/muninn-gate.example.yaml. Schema is CLOSED — any unknown
field is a boot failure. Keys are flat-scope (read | submit | control, no
inheritance); name is the non-secret submitted_by identity.
Build + deploy
Image builds out-of-band — the Gitea read token (for muninn-dispatch==0.1.4 from
the internal index) rides as a BuildKit secret, never a layer:
DOCKER_BUILDKIT=1 docker build --secret id=gitea_pw,src=<gitea-read-token> -t muninn-gate:0.0.14 .
docker compose up -d # after installing the config to /opt/docker/conf/muninn-gate/
Status
Booted + healthy 2026-07-31; /ping + /health (watcher:running:true,
ingestion_root_writable:true) verified. SUBMIT deferred — returns not_found
against the placeholder staging until the real shared mount + mimir-inbox writer
exist; then repoint + the one-file path-agreement probe + the real acceptance run.