diff --git a/AGENTS.md b/AGENTS.md index 1325a20..3e7d461 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,12 +55,12 @@ Subsystem guidance lives in `docs/guides/` and loads when matching files are tou ## Current state -- **Working (v0.20.0:0, installed and serving):** swap dashboard; chat / transcribe / diarize(+chunk) / TTS proxies; embeddings + rerank + hybrid search (Qdrant); `/scrub` + `/rehydrate`; label-merge incl. dual-channel; per-Spark SSH-key copy + WireGuard `VPN ` hardware-card badge. Spark 2 audio stack healthy. Security hardening (v0.19.0:0 — shellsafe SSH-injection guard, Qdrant path-injection, same-origin CSRF guard) shipped and stable; evidence in `EVALUATION.md`. -- **matrix-bridge tile (v0.21.0:0 — code complete, NOT yet built/installed):** new "matrix-bridge" service tile (kind `bot`) on the Always-on services panel. Status badge (docker-state only — no HTTP health port), Restart/Stop/Start (generic `/api/services` path), **Update** (streamed `git fetch && git reset --hard origin/ && docker compose up -d --build` via `app/matrix_bridge.py`, 25-min cap, fail-loud), **View logs** (`docker logs --tail 100`). Driven as a dedicated SSH user **directly** (no `sudo -iu` — spark2 has no passwordless sudo). The user is a **blank-default "Configure Sparks" field** (`matrix_bridge_user`); blank → service unconfigured → tile hidden (keeps the shared package portable, no hardcoded username). Host reuses `spark2_host`; container/dir/branch are env-overridable defaults (`matrix-bridge` / `~/matrix-bridge` / `master`). Tile also auto-hides when the container is absent. New endpoints: `POST /api/matrix-bridge/update` (+`/{id}`, `/{id}/stream`), `GET /api/matrix-bridge/logs`. **Owner prereq before Update works:** convert `~/matrix-bridge` to a Gitea clone, and authorize the package key for `modelo` unless `spark2_user == modelo`. Then bump-build-install (`cd package && make x86 && make install` — restarts live service, get go/no-go). +- **Working (v0.21.0:1, installed and serving):** swap dashboard; chat / transcribe / diarize(+chunk) / TTS proxies; embeddings + rerank + hybrid search (Qdrant); `/scrub` + `/rehydrate`; label-merge incl. dual-channel; per-Spark SSH-key copy + WireGuard `VPN ` hardware-card badge. Spark 2 audio stack healthy. Security hardening (v0.19.0:0 — shellsafe SSH-injection guard, Qdrant path-injection, same-origin CSRF guard) shipped and stable; evidence in `EVALUATION.md`. +- **matrix-bridge tile (v0.21.0:1 — installed & verified 2026-06-16):** "matrix-bridge" service tile (kind `bot`) on the Always-on services panel. Status badge (docker-state only — no HTTP health port), Restart/Stop/Start (generic `/api/services` path), **Update** (streamed `git fetch && git reset --hard origin/ && docker compose up -d --build` via `app/matrix_bridge.py`, 25-min cap, fail-loud), **View logs** (`docker logs --tail 100`). Driven as a dedicated SSH user **directly** (no `sudo -iu` — spark2 has no passwordless sudo). The user is a **blank-default "Configure Sparks" field** (`matrix_bridge_user`, set to `modelo`); blank → service unconfigured → tile hidden (portable, no hardcoded username). Host reuses `spark2_host` (= `192.168.1.87` = the bot's box `spark-32d0`); container/dir/branch env-overridable defaults. Verified live: badge Healthy, Update (rebuild+recreate), Restart, View logs all work. Note: a fast `docker restart` won't visibly flip the badge (status re-checked only after the command returns, by which point it's back up — matches the sub-5s-poll-miss limit). **Ops dependency:** the Update button runs `git fetch` as `modelo` on the Spark, which relies on `modelo`'s `~/.ssh/config` pinning the Gitea deploy key (`Host immense-voyage.local … IdentityFile ~/.ssh/id_ed25519` + `IdentitiesOnly yes`) — without it another key is offered first and Gitea denies (publickey). Don't remove that block. New endpoints: `POST /api/matrix-bridge/update` (+`/{id}`, `/{id}/stream`), `GET /api/matrix-bridge/logs`. **Owner prereq before Update works:** convert `~/matrix-bridge` to a Gitea clone, and authorize the package key for `modelo` unless `spark2_user == modelo`. Then bump-build-install (`cd package && make x86 && make install` — restarts live service, get go/no-go). - **Tests:** offline pytest harness in `image/tests/` — `cd image && .venv/bin/python -m pytest` (70 passing). Covers `build_launch_command` (incl. the shell-injection round-trip), the transcript↔diarizer label-merge, the `shellsafe` validators, and `matrix_bridge.build_update_command` (+ phase detection). Mock-heavy swap/proxy tests deliberately skipped (low ROI). Redaction + live-audio suites remain standalone scripts. - **Signal Engine "flakiness":** diagnosed as *not* a server bug — transient 1–4s unresponsiveness while the single GPU is busy. Client-side remedy (in-flight cap 2 / ceiling 3 / retry-on-timeout+503) drafted and **forwarded to that dev (owner confirmed 2026-06-15)**. Awaiting whether they want the measured concurrency knee. - **Stance (decided, not built):** no public interface / no API-token auth — LAN + WireGuard/Tailscale split-tunnel only; the CSRF guard covers the browser-driven vector. - **Known limits:** `/health` blips while the GPU is busy (mitigated client-side); dual-channel can miss a quiet local word under loud remote bleed; connectivity log misses sub-5s outages between 5s polls; diarizer caps at 4 speakers. - **Infra gotcha (safety):** passwordless sudo is NOT configured on spark2 — design unprivileged probes for any Spark feature (the badge uses `ip`, not `sudo wg show`). spark2 sits on the `starttunnel` WireGuard subnet (`10.59.211.6/24`, survives reboot). Owner declined SSH-key rotation after the 2026-06-12 history scrub (only the key *name* leaked) — don't re-flag. - **Hosting:** self-hosted Gitea — remote `gitea`, branch `master`, over SSH; push after committing. (Wart: commit `8d839e3` is mislabeled `v0.13.0:4` but contains through v0.18.0:0.) -- **Next:** (1) matrix-bridge Phase 3 — owner does the one-time prereqs (Gitea clone of `~/matrix-bridge` + authorize the package key for `modelo`), then bump-build-install v0.21.0:0 and verify the tile flips across a manual `docker stop`/`start` and that Update/Restart/logs work. (2) audio concurrency sweep — only if the Signal Engine dev wants the measured knee; needs owner OK in a quiet window. (3) Otherwise pull from `ROADMAP.md`: local-path/fine-tuned model support (new) or P2 tech-debt. Parakeet long-audio guard is deferred (rationale in ROADMAP). +- **Next:** (1) matrix-bridge Phase 3 — **done & shipped (v0.21.0:1, pushed)**; nothing outstanding unless the dev wants the optional Docker `HEALTHCHECK` follow-up (running-but-disconnected detection, spec §Note). (2) audio concurrency sweep — only if the Signal Engine dev wants the measured knee; needs owner OK in a quiet window. (3) Otherwise pull from `ROADMAP.md`: local-path/fine-tuned model support (new) or P2 tech-debt. Parakeet long-audio guard is deferred (rationale in ROADMAP). diff --git a/image/app/static/app.js b/image/app/static/app.js index 074d4b4..3e425ea 100644 --- a/image/app/static/app.js +++ b/image/app/static/app.js @@ -494,7 +494,7 @@ async function renderServices() { return false; }; const copyIcon = ``; - const hostStr = s.host ? `${s.host}:${s.port}` : ''; + const hostStr = s.host ? (s.port ? `${s.host}:${s.port}` : s.host) : ''; const hostRow = s.host ? `
Host${escapeHtml(hostStr)}
` : `
Hostnot configured
`; diff --git a/package/startos/versions/v0_1_0.ts b/package/startos/versions/v0_1_0.ts index dcca2ab..8da74f4 100644 --- a/package/startos/versions/v0_1_0.ts +++ b/package/startos/versions/v0_1_0.ts @@ -1,10 +1,10 @@ import { VersionInfo, IMPOSSIBLE } from '@start9labs/start-sdk' export const v0_1_0 = VersionInfo.of({ - version: '0.21.0:0', + version: '0.21.0:1', releaseNotes: { en_US: - "v0.21.0:0 — matrix-bridge bot tile. The Matrix bot container on Spark 2 now appears as a tile under \"Always-on services\" with a live status badge (judged by the container itself, since the bot has no health port). Buttons: Update (pulls the latest code, rebuilds the image, and recreates the container — long-running, with a streamed log and a generous timeout), Restart, Stop/Start, and View logs (last 100 lines). Everything fails loud: a non-zero exit or stderr shows in the panel rather than a silent stall. To enable it, set the bot's SSH user (the owner of ~/matrix-bridge, e.g. 'modelo') in the Configure Sparks action — leave it blank and no tile appears, so this stays out of the way on systems that don't run the bot. New endpoints (LAN-only, browser-driven): POST /api/matrix-bridge/update (+ /{id} and /{id}/stream for progress), GET /api/matrix-bridge/logs. One-time setup on the Spark (owner): make ~/matrix-bridge a git clone of your Gitea repo, and — unless that SSH user is the same as your Spark 2 user — authorize this package's SSH public key for it (Show Public Key, then add it to that user's authorized_keys). There is no passwordless sudo on the Spark, so commands run directly as that user rather than via sudo.", + "v0.21.0:1 — matrix-bridge bot tile. The Matrix bot container on Spark 2 now appears as a tile under \"Always-on services\" with a live status badge (judged by the container itself, since the bot has no health port). Buttons: Update (pulls the latest code, rebuilds the image, and recreates the container — long-running, with a streamed log and a generous timeout), Restart, Stop/Start, and View logs (last 100 lines). Everything fails loud: a non-zero exit or stderr shows in the panel rather than a silent stall. To enable it, set the bot's SSH user (the owner of ~/matrix-bridge, e.g. 'modelo') in the Configure Sparks action — leave it blank and no tile appears, so this stays out of the way on systems that don't run the bot. New endpoints (LAN-only, browser-driven): POST /api/matrix-bridge/update (+ /{id} and /{id}/stream for progress), GET /api/matrix-bridge/logs. One-time setup on the Spark (owner): make ~/matrix-bridge a git clone of your Gitea repo, and — unless that SSH user is the same as your Spark 2 user — authorize this package's SSH public key for it (Show Public Key, then add it to that user's authorized_keys). There is no passwordless sudo on the Spark, so commands run directly as that user rather than via sudo.", }, migrations: { up: async ({ effects }) => {},