4b54a32d64
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.
77 lines
3.6 KiB
Markdown
77 lines
3.6 KiB
Markdown
# 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.
|