Files
esh-pfi-infrastructure/stacks/mimir-inbox/README.md
T
vh 4b54a32d64 feat(mimir-inbox): book-ingestion UI stack on corviduo-dev:8091 (#377)
WG-internal FastAPI+HTMX front end for large-document ingestion into the
Muninn KB, over the muninn-gate API (browser -> mimir-inbox -> staging ->
path-addressed POST /jobs). Co-located on corviduo-dev with the gate (:8090)
and the worldtree-personal muninn watcher per the operator's 2026-08-01
co-location ruling (reversing the earlier off-box/NFS plan; worldtree-dev
approved the box placement).

- Dockerfile: python:3.11-slim + uv sync --no-dev --frozen (--no-dev is
  load-bearing; the dev group's muninn-dispatch path source is absent in-image
  and INV-MI-7 forbids importing it). Single-stage by design — src/ stays in
  the final image (uv installs the project editable-linked to src/).
- compose.yaml: uid 1000, host-net bind 10.250.50.152:8091, staging :rw,
  TCP-liveness healthcheck (deliberately not coupled to gate reachability).
- Built from vh/mimir-inbox HEAD c8ab38f; deployed + healthy.

Records the open-in-place claim semantics (worldtree-dev, runner.py:362-367)
and the INV-MI-19 retention rule (staged files persist until job terminal;
gate retry returns a false-200 on a swept source) in persistent memory.
2026-08-01 14:31:14 -07:00

3.6 KiB

mimir-inbox

UI front end for large-document / novel / book ingestion into the Muninn KB — server-side FastAPI + HTMX over the muninn-gate API (#377). The submit path is: browser → mimir-inbox /upload (writes to staging) → path-addressed POST /jobs to the gate. Bytes never reach the gate; the gate references the staged file by absolute path, and the watcher opens it in place at claim.

  • Upstream: git@gitea.phasefinal.com:vh/mimir-inbox.git (infra-ops owns containerization — no Dockerfile upstream).
  • Host: corviduo-dev 10.250.50.152:8091, co-located with muninn-gate (:8090) and the worldtree-personal muninn watcher. Operator ruled co-location 2026-08-01 (reversing the earlier off-box/NFS plan); worldtree-dev approved the box placement.
  • Posture: LAN/WG-internal, no TLS, no public exposure (same as the gate). No datastore, no migrations, no writable path other than the staging root — a restart loses nothing.

Why co-located (not off-box/NFS)

The watcher opens the staged file in place at claim (core/muninn/runner.py), so staging's only hard constraint is path identity — the writer, gate, and watcher must resolve the same absolute string to the same bytes. Co-location on corviduo-dev buys that outright with one local filesystem and sidesteps the NFS failure modes (path-identity drift, TOCTOU widening, stale handles, a hung mount blocking the async staging check on mimir-inbox's event loop). Multi-host stays viable later behind a same-path shared mount, but co-location is the cheapest way to path identity.

Retention rule (load-bearing — INV-MI-19)

Because the watcher opens the file in place, a staged file must persist from submit until the job is terminal (complete, or failed-and-not-retried) — retry re-runs the structure phase, which re-opens the same staged path. Deleting on 201-submit would kill every retried job at claim with a not_found that looks exactly like a namespace mismatch. MIMIR_INBOX_STAGING_RETENTION_DAYS is the retry window, not a tidiness knob; do not tune it down without telling mimir-dev.

Config

All runtime config is environment variables — no config file. The real values (including the bearer-key secret) live in a gitignored .env next to compose.yaml on the server (0600); the repo carries .env.example. Two DEV-ONLY drift-guard vars (WORLDTREE_CONFIG_PATH, MUNINN_GATE_REPO) must not be set in the container.

Deploy

# 1. Fetch the source at the pinned tag/HEAD onto corviduo-dev, build the image
#    (all deps public PyPI; uv sync --no-dev is load-bearing — see Dockerfile):
docker build -t mimir-inbox:0.0.1 .

# 2. Provision the shared staging root (mimir-inbox-writable, gate+watcher :ro):
sudo install -d -o 1000 -g 1000 -m 0755 /mnt/muninn-staging/mimir-inbox

# 3. Drop the real .env (0600) next to compose.yaml with the bearer key filled
#    from the infra-ops key drop (~/.config/muninn-gate/mimir-inbox-env.sh).

# 4. Bring it up:
docker compose up -d

# 5. muninn-dev's one-file path-agreement probe, then the acceptance run.

Operational guard: docker fabricates a MISSING bind source as an empty dir that passes checks — confirm /mnt/muninn-staging/mimir-inbox exists on the host before up -d (step 2 does this). The container also fails boot if the staging root is not writable (os.access(W_OK)), so a perms miss surfaces loudly.

Health

GET /health-banner is the HTMX health surface (reflects gate reachability). The container healthcheck is a TCP-connect liveness probe only — it must not couple to the gate's state, so a gate outage never restarts mimir-inbox.