Files
Maven/PROGRESS.md
T
2026-07-03 00:32:48 +02:00

7.8 KiB
Raw Blame History

Maven — current state (2026-07)

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).

Access model

  • Phone → needs the wg tunnel to reach homesrv (no homesrv DNS otherwise; raw IP or a DNS tweak can bypass, not the default).
  • PC → uses homesrv DNS, resolves the domains over local-net, no wg needed.
  • nginx + ufw both scope to 10.42.0.0/24 (wg) + 192.168.1.0/24 (LAN), deny all else.
  • Surface in use now: the web PWA (mavweb). Voice PTT + in-app nudges both ride it.

Works end-to-end (tested)

  • Reactive voice: PWA record → Whisper STT (mavsttd) → ONNX classifier → LFM 2.5-1.2B phraser (llama-server subprocess) → Piper TTS (mavttsd) → reply. HTTP POST path (mobile-Chrome drops WS for the audio).
  • Capture: fact (EN and RU — root-substring recognizers) + reminder persist through CoreAPI (source=tap:voice). This is the substrate the care 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.
  • 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.
  • Proactive loop: 60s dumb ticker, pure predicates over a State snapshot, universal gate (quiet-hours/presence/cooldown/snooze/calendar), one-nudge-per- tick max-severity, reminders (gate-bypassing), sev4 repeat-til-ack, feedback auto-tuner (outcome ratio → bounded cooldown, persisted as source=feedback).
  • Rules: water/meal/break (sev12 care), service_down (sev4, poll:uptimekuma), netdata_critical (sev3, poll:netdata).
  • Env facts (mavpoll): netdata alarms → netdata_alarm (fires immediately on a real CRITICAL); kuma monitor_status → service_down. Writes only on value-change (no append-only churn).
  • Presence: noisy-OR decay + Schmitt hysteresis. Live via page_heartbeat (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.
  • 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.

Wired but needs a deploy action (not code)

  • desk_active (strongest presence signal) — scripts/desk-active.sh runs on the desk PC (hypridle-gated systemd timer), posts over wg to mavweb.
  • Kuma service_down — needs an API key created in Kuma → Settings → API Keys, passed to mavpoll -kuma-key.

Caveats / gotchas:

  • desk_active is a workstation deploy, not code — 0 facts ever written; presence runs on page_heartbeat alone (dash reads "away"/"never at desk"). scripts/desk-active.sh
    • a hypridle-gated maven-desk timer must be installed on the desk PC (not homesrv).
  • Notes recall needs the ONNX embedder — under the HashEmbedder floor, cosine is lexical (token overlap), not semantic; scores are low, so most RU commands sit under the 0.35 route threshold and clarify. Configure voice.embedder for confident recall+routing. (The floor now at least tokenizes Cyrillic — see below — so it ranks correctly, just weakly.)
  • Switching the embedder model silently breaks old notes — different dim ⇒ cosine 0 ⇒ they stop matching; brute-force can't re-embed. Re-embed on a model change.
  • wg_handshake is OFF and should stay off — in this topology the phone only runs wg when outside, so a fresh handshake means AWAY, not here. The mavpoll -wg flag exists (defaults "") and could later back the spec's "away override" by flipping the sign; as a presence-here signal it's inverted. desk_active + page_heartbeat cover home presence.

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).

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).
  • 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 = the human enable act); mavweb enables ad-hoc ones on top.
  • Russian: fixed grammar in reply strings + seed files; maven's self- reference is feminine ("she") — maven-persona-gender.

Also fixed:

  • HashEmbedder was blind to Cyrillic (tokenize iterated bytes, kept only a-z0-9) → every RU utterance embedded to the zero vector → cosine 0 across all intents → misrouted to act (alphabetical tie-break). Now rune-based (unicode.IsLetter). This was the real cause of "Найди заметку" (a query) landing in notes; added note-retrieval query seeds too.
  • Notes are now browsable on /dashRecentNotes plumbed through the store + CoreAPI; voice-captured notes were previously only reachable via semantic query. Earlier: notes/query recall, /dash monitoring, wg_handshake poller (NO-OP).

Future / logged, not now

Personality prompt; custom TTS voice training (kami-picked voice, replaces irina floor); listening modes 23 (meeting-record, ambient-derive).

Services & layout

  • mavend (core, IPC unix socket) — store + loop + phraser; the only key-holder.
  • mavsttd / mavttsd — STT/TTS worker modules (unix sockets).
  • 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).
  • 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).

Key files

  • cmd/mavend/{main,tick,voice}.go — daemon wiring, loop driver, voice handler
  • internal/loop/{loop,rules,gather,feedback}.go — proactive engine
  • internal/store/ — append-only facts/reminders/nudges/presence/notes
  • 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