From bd1e2789eb79955648815b151c18a21ad17f75be Mon Sep 17 00:00:00 2001 From: kami Date: Fri, 3 Jul 2026 18:49:59 +0400 Subject: [PATCH] progress: refresh for SPEC items 1-7; document item-8 deferral The doc was from the initial commit and predated all open-item work. Update the works-end-to-end list (protocol doc, away-fallthrough, mavcaldav, quiet-hours schedule, tools enable/disable, note RAG, passkey step-up), rework the not-built-yet ranking (at-rest crypto, mavweb/mavcaldav tests, systemd), and add the rationale for deferring multi-user. Co-Authored-By: Claude Opus 4.8 --- PROGRESS.md | 87 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/PROGRESS.md b/PROGRESS.md index f7eedba..728854d 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -1,8 +1,11 @@ -## Maven — current state (2026-07) +## Maven — current state (2026-07-03) Consolidated status. The reactive↔proactive core is closed and testable through -the web PWA today. Big untouched capability: **acting on the homelab** (tool -executor). Big untouched infra: **at-rest security** (sqlcipher/passkey). +the web PWA. The SPEC's open items 1–7 are now landed (protocol doc, away-channel +fallthrough, CalDAV poller, quiet-hours schedule, tools enable/disable, note RAG, +passkey step-up); item 8 (multi-user) is deliberately deferred — see the tail. +Big untouched infra remaining: **at-rest security** (sqlcipher) and **systemd/ +container deployment** (still scripts + tmux). ~13.1k LOC + ~5.3k test, 202 tests. ### Access model @@ -22,8 +25,10 @@ executor). Big untouched infra: **at-rest security** (sqlcipher/passkey). rules read. - **Notes / query (semantic recall, sqlite — no chroma):** `note` → embed (the classifier's ONNX embedder) → `notes` table. `query` → embed → brute-force - cosine top-k → confidence-gated answer (below `queryMinScore` 0.55 ⇒ "no note", - not a guess). Verbatim top-hit reply; full top-k phraser-RAG is the follow-up. + cosine top-k → confidence-gated (below `queryMinScore` 0.55 ⇒ "no note", not a + guess). **Note RAG (SPEC item 6):** the gated top-k feed the phraser + (`PhraseQuery`) to compose a natural answer ("вот что я нашла: …") instead of + a verbatim dump; raw-notes fallback on any LLM error. Stub is deterministic. - **Monitoring (`/dash`):** mavweb server-renders presence + recent nudges (by outcome) + recent facts from the append-only store via CoreAPI. Read-only, meta-refresh, no JS. @@ -40,6 +45,25 @@ executor). Big untouched infra: **at-rest security** (sqlcipher/passkey). (PWA auto-pings `/api/signal` every 30s → present when a tab's open). - **Delivery:** ntfy / telegram / voice by `f(severity, presence)`; minimal body on away channels. PWA subscribes to ntfy over **WebSocket** for in-app nudges. +- **Away-channel fallthrough (SPEC item 2):** when the router picks voice but no + live session exists at push time (presence guess was wrong), the dispatcher + reroutes through the AWAY table — sev3→ntfy, sev4→telegram-repeat-til-ack, + sev≤2→drop — instead of silently dropping. Covers nudges + reminders. +- **Calendar busy (SPEC item 3, `mavcaldav`):** new poller queries a self-hosted + **Radicale** CalDAV server on an interval, writes `calendar_busy` + event facts + through CoreAPI (value-change only). The loop gate already consumes `calendar_busy`. +- **Quiet-hours schedule (SPEC item 4):** the gate reads `quiet_hours`; a config + time window (`voice.quiet_hours`, HH:MM, midnight-crossing handled) now sets it + on each tick — in addition to the "тихий режим" voice toggle. Both activate quiet. +- **Client protocol (SPEC item 1):** the voice wire format (length-prefixed JSON + frames) is published in `PROTOCOL.md`, generated from `internal/voice/wire.go` + so third-party clients don't need the Go source. +- **Passkey step-up (SPEC item 7):** `internal/webauthn` does real WebAuthn — + ES256/P-256 register + assert, ecdsa signature verification, rpIdHash + UP/UV + flag binding (UV = the gesture), sign-count regression check. `PasskeySession` + bumps the auth session L2→L3 for a TTL on assert. mavweb serves `/auth/passkey` + (enroll + step-up) + the begin/finish endpoints. Crypto is round-trip tested + (incl. tampered-sig / missing-UV / wrong-origin negatives). - **Stability:** llama-server orphan leak fixed (`Pdeathsig` kills the child on any mavend death); `kill-maven.sh` reaps strays (matches the model, not a bogus `llama-server.*maven` pattern); `start-maven.sh` wires `-core` + poller. @@ -69,22 +93,32 @@ Caveats / gotchas: ### Not built yet (ranked by ROI) -1. **Full note RAG** — today the `query` reply is the verbatim top-hit note. - Follow-up: feed gated top-k to the phraser to compose an answer. -2. **quiet-hours source** — gate reads a `quiet_hours` config fact nothing writes - (defaults not-quiet; restraint unenforced). -3. **Security layer** — sqlcipher at-rest, cold-start unlock, real passkey auth. - Today: plain sqlite, `FloorEnrollment` (any same-uid caller = full L3). +1. **At-rest encryption** — sqlcipher + cold-start unlock. Today the store is + plain sqlite: disk access = full read. Passkey step-up now gates *actions*, + but not the data at rest. +2. **`cmd/mavweb` / `cmd/mavcaldav` tests** — both are untested (0 test files). + mavweb now carries the passkey + tools HTTP flow (the webauthn *crypto* is + tested; the handlers and browser JS are not). Highest-value test gap. +3. **Deployment** — still `start-maven.sh` + tmux. systemd user units (one per + binary, socket-activated, `After=mavend.socket`, `Restart=on-failure`) are + specced, not written. Docker/Podman is a later phase. +4. **Multi-user (SPEC item 8)** — deliberately deferred, see the tail. + +Housekeeping: `go.mod` isn't tidied — `onnxruntime_go` and `coder/websocket` +are used but marked `// indirect`. Passkey credentials are mavweb in-memory +(re-enroll after a mavweb restart — acceptable for single-user, by design). Done since last revision: **act tool executor, store-backed, full flow** (`internal/tool` + `internal/store/tools.go` + `tools` CoreAPI methods). - **Execution:** IntentAct runs the matched fn against the store's ENABLED allowlist. argv, no shell → STT text can't inject. Live store read, so a newly-enabled tool runs without a daemon restart. -- **proposed→enabled:** an act whose verb isn't enabled is scaffolded as a - `proposed` tool (maven suggests). A human enables it (fills argv + destructive) - on the authed **`mavweb /tools`** page — never voice. `EnableTool` sits at - `AuthStepUp` in the policy table (gate lands with the security layer). +- **proposed→enabled→disabled (SPEC item 5):** an act whose verb isn't enabled is + scaffolded as a `proposed` tool (maven suggests). A human enables it (fills argv + + destructive) on the authed **`mavweb /tools`** page — never voice — and can + disable it back to `proposed` (kept in the store, won't run). `EnableTool`/ + `DisableTool` sit at `AuthStepUp`; the gate is now **live** via `PasskeySession`, + so /tools enable requires a passkey assertion at `/auth/passkey` first. - **Confirm turn:** a destructive enabled tool replies "выполнить X? да/нет" and parks; the next utterance (ru/en yes-no) confirms or cancels (90s TTL). - **Config:** `voice.tools` seeds enabled tools at boot (editing mavend.json = @@ -115,6 +149,7 @@ floor); listening modes 2–3 (meeting-record, ambient-derive). - `mavweb` — PWA bridge (HTTP), `/api/ptt` voice, `/api/signal` presence ingest, `/api/ntfy` WS-subscribe config, `/dash` read-only monitoring. - `mavpoll` — env poller (netdata/kuma → facts via CoreAPI). +- `mavcaldav` — CalDAV poller (Radicale → `calendar_busy` + events via CoreAPI). - All behind wg + nginx deny-all; no phone-home. CGo only in `mavsttd`. - Start/stop: `./start-maven.sh [build]`, `./kill-maven.sh`. - Config: `~/.config/maven/mavend.json` (or `mavend.json` in repo root). @@ -127,5 +162,23 @@ floor); listening modes 2–3 (meeting-record, ambient-derive). - `cmd/mavweb/{main.go,dash.html}` — PWA bridge + `/dash` monitoring - `internal/router/{classifier,slots,stage0}.go` — reactive routing + slot parse - `internal/delivery/` — dispatcher + ntfy/telegram/voice sinks -- `internal/auth/` — scope/gate/enrollment (floor today) -- `cmd/mavpoll/`, `scripts/desk-active.sh` — env + presence producers +- `internal/auth/` — scope/gate/policy; `FloorEnrollment` (same-uid = device + trust) + `webauthn.PasskeySession` (real step-up for L3) +- `internal/webauthn/`, `cmd/mavweb/webauthn.go` — passkey register/assert +- `cmd/mavcaldav/`, `cmd/mavpoll/`, `scripts/desk-active.sh` — env producers + +### Why multi-user (SPEC item 8) is deferred + +Not neglect — the one item where doing nothing now beats doing something: + +- **No second user exists yet** (the "gf phase"). Building per-user partitioning + now means code exercised by zero users and validated by nobody — YAGNI. +- **The append-only schema makes it a migration, not a rewrite.** No row is ever + mutated, so adding `facts/notes/reminders.user_id` later is add-columns + + backfill-to-"kami" — no reshaping, no dual-write window. Deferral is cheap. +- **The hard part is speaker attribution, and it needs the second voice.** A + voice-print discriminator (kami vs gf vs unknown) can't be trained or tuned + with one voice in the house. Plumbing before the model is pipe with no water. +- **It's fenced deliberately** (`DO NOT TOUCH THIS PHASE` in SPEC.md) so an + autonomous agent doesn't add `user_id` columns while touching the store and + commit us to a schema before the constraints that shape it exist.