786462ac9c
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.
57 lines
2.9 KiB
Markdown
57 lines
2.9 KiB
Markdown
# 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`** — the `worldtree-personal_worldtree-state`
|
|
volume, 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: true` **proves** this byte-identity
|
|
(gate is reading the heartbeat the watcher writes). `no_heartbeat` with the
|
|
watcher up = root mismatch.
|
|
- **`user: "1000:1000"`** — the ingestion dir is `vh: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 `:ro` in
|
|
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`** — `/health` is 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:
|
|
|
|
```bash
|
|
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.
|