# 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 ```bash # 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.