# Handoff: add the Matrix intake bot as a spark-control dashboard card **Do this work in the `spark-control` repo (`~/Projects/spark-control`), in a separate session.** This repo (ten31-database) only owns the bot + its container; the dashboard card is driven entirely by spark-control code. Prereq (DONE 2026-06-17): the bot already runs as a docker container named **`matrix-intake`** on the Spark (`spark-32d0`, user `modelo`), via `docker-compose.yml` at this repo's root. spark-control reaches it over the **same SSH channel it already uses for `matrix-bridge`** (`modelo@spark-32d0`) — no new key/host needed. The card is a near-exact clone of the existing `matrix-bridge` card. Mirror that, with **three deltas** (below). File paths/line numbers are from the 2026-06-17 review; reconfirm against the current code. ## Deltas from matrix-bridge (do NOT copy these blindly) 1. **Branch is `main`, not `master`.** The Update button runs `git reset --hard origin/` — it MUST be `main` for ten31-database, or Update silently resets to the wrong/empty ref. 2. **Project dir is `/home/modelo/ten31-database`** (the CRM monorepo clone), not `~/matrix-intake`. 3. **Coupling caveat:** because the bot lives in the CRM monorepo, the Update one-liner does `git reset --hard origin/main` on the **whole CRM clone**. Safe today (`.env` is gitignored, the clone has no needed local edits), but this is exactly the blast-radius smell that motivates eventually extracting the bot to its own repo (logged in ten31-database `ROADMAP.md`). If that extraction happens first, point dir/branch/remote at the new repo instead. ## Edits in spark-control (mirror the matrix-bridge wiring) 1. **`image/app/config.py`** (matrix-bridge entry ~lines 99–111): add `matrix_intake_host` (default `spark2_host`), `matrix_intake_user`, `matrix_intake_container` (default `"matrix-intake"`), `matrix_intake_dir` (default `"/home/modelo/ten31-database"`), `matrix_intake_branch` (default **`"main"`**), each with a `MATRIX_INTAKE_*` env fallback. 2. **`image/app/services.py`** (matrix-bridge ServiceDef ~lines 95–102): add a `"matrix-intake": ServiceDef(name="matrix-intake", kind="bot", host=…, user=…, container=…, port=0)` entry. `port=0` → judged by docker state alone (no HTTP probe), same as matrix-bridge. 3. **`image/app/matrix_intake.py`** (new): copy `matrix_bridge.py`, rename `matrix_bridge`→`matrix_intake` throughout. The Update command (`build_update_command`) must produce: `cd /home/modelo/ten31-database && git fetch origin && git reset --hard origin/main && docker compose up -d --build`. 4. **`image/app/server.py`**: (a) add `"matrix-intake"` to the `service_action` whitelist (~line 621); (b) `from .matrix_intake import MatrixIntakeManager` + instantiate `matrix_intake = MatrixIntakeManager(settings)` (~line 47); (c) add the 4 endpoints mirroring matrix-bridge: `POST /api/matrix-intake/update`, `GET …/update/{job_id}`, `GET …/update/{job_id}/stream`, `GET /api/matrix-intake/logs`. 5. **`image/app/static/app.js`** — THE RISKY EDIT. The Update/View-logs handlers are hardcoded to matrix-bridge (`data-mb-update`, `onMatrixBridgeUpdate`, `/api/matrix-bridge/...`). Generalize them to dispatch by the card's bot name (e.g. read `name` off the card, call `/api//update` and `/api//logs`). Start/Stop/Restart are already generic. **Regression-check that the existing matrix-bridge card still updates + tails logs after this change.** 6. **`package/startos/fileModels/sparkConfig.yaml.ts`** (~lines 27–32): add `matrix_intake_user: z.string().catch('')`. 7. **`package/startos/main.ts`** (~line 68): inject `MATRIX_INTAKE_USER: cfg.matrix_intake_user`. 8. **(optional) `package/startos/actions/configureSparks.ts`**: add the intake-bot SSH-user field to the form. ## Deploy + ops - spark-control is itself an s9pk: **bump its version, rebuild, reinstall** per spark-control's own packaging docs (don't forget — same "0.4.x ignores same-version" rule). - One-time: run **Configure Sparks** → set the intake bot's SSH user to `modelo` (same as matrix-bridge → key already authorized). The card appears once the `matrix-intake` container exists and the user is set; it hides itself if the container is absent or the user is blank. - Status pill = `docker inspect matrix-intake .State.Status` (running→Healthy). No Matrix-liveness check — a running-but-silent bot still shows Healthy (same limitation as matrix-bridge). ## Done when The dashboard shows a `matrix-intake` card alongside `matrix-bridge` with a Healthy pill and working Update / Start / Restart / Stop / View-logs buttons — and the matrix-bridge card is unregressed.