35c6ff5a71
Persist reminder presentations and retry state, atomically complete collapsed deliveries, fall back across away reaches, and block permanent failures visibly (V-715, V-678). Fail closed when enabled integrations lack credentials and keep remote arms explicitly dark (V-691). Give mavweb one sanitized, request-correlated error contract (V-689). Owner explicitly requested direct commits to master.
221 lines
7.5 KiB
Markdown
221 lines
7.5 KiB
Markdown
# Start Commands
|
|
|
|
*Last verified: 2026-08-07 @ a4630b9. Living doc: correct it in place, do not append.*
|
|
|
|
All commands assume `ROOT=/home/kami/apps/Maven` and the local Go toolchain at `$ROOT/deps/go/go/bin/go`.
|
|
|
|
## Prerequisites
|
|
|
|
```bash
|
|
export ROOT=/home/kami/apps/Maven
|
|
export CGO_CFLAGS="-I$ROOT/deps/include -I$ROOT/deps/whisper.cpp/ggml/include"
|
|
export CGO_LDFLAGS="-L$ROOT/deps/lib -Wl,-rpath,$ROOT/deps/lib"
|
|
export LD_LIBRARY_PATH="$ROOT/deps/lib"
|
|
export PATH="$ROOT/deps/go/go/bin:$PATH"
|
|
```
|
|
|
|
## Build everything
|
|
|
|
```bash
|
|
cd "$ROOT"
|
|
go build ./cmd/mavend/
|
|
go build ./cmd/mavsttd/ # needs CGO (whisper.cpp)
|
|
go build ./cmd/mavttsd/ # pure Go
|
|
```
|
|
|
|
## mavend — daemon (core)
|
|
|
|
```bash
|
|
cd "$ROOT"
|
|
./mavend -config mavend.json
|
|
```
|
|
|
|
Config path: `~/.config/maven/mavend.json`. Full example with all options.
|
|
|
|
> The `phraser.model_path` below is an example — point it at whatever GGUF you
|
|
> have locally. The deployed value lives in `deploy/mavend.json`, currently
|
|
> `Qwen3.5-0.8B.Q4_K_M.gguf`; the target is the CPT'd Qwen3-1.7B (#122).
|
|
|
|
```json
|
|
{
|
|
"db_path": "/home/kami/.local/share/maven/maven.db",
|
|
"socket_path": "/run/user/1000/maven/mavend.sock",
|
|
"tick_interval": "60s",
|
|
"repeat_interval": "5m",
|
|
"ntfy": {
|
|
"base_url": "https://ntfy.kvmx.ru",
|
|
"topic": "maven",
|
|
"token": "${NTFY_TOKEN}"
|
|
},
|
|
"phraser": {
|
|
"model_path": "/mnt/hdd1/llms/Qwen3-Maven-1.7B-Q8_0.gguf",
|
|
"bin_path": "/usr/local/bin/llama-server",
|
|
"n_gpu_layers": -1
|
|
},
|
|
"voice": {
|
|
"enabled": true,
|
|
"bind": "127.0.0.1:9100",
|
|
"lang": "ru",
|
|
"embedder": {
|
|
"model_path": "models/embedder/model.onnx",
|
|
"tokenizer_path": "models/embedder/tokenizer.json",
|
|
"lib_path": "deps/onnxruntime-linux-x64-1.17.1/lib/libonnxruntime.so.1.17.1"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Omit the `embedder` block entirely to use the deterministic HashEmbedder floor (no ML, no ONNX runtime dependency). Useful for testing or low-resource setups.
|
|
|
|
`deploy/telegram.env.example` is the canonical inventory for every deployed
|
|
secret, including values whose destination is the root `.env`, the workpc CW2
|
|
environment file, or `deploy/db_key.env`. Copy values only to the destination
|
|
named beside them; never commit the populated files. Maven expands the homesrv
|
|
sink, workstation, and Home Assistant variables from `deploy/telegram.env`.
|
|
|
|
Every written integration block is either explicitly disabled or live. A live
|
|
Telegram, ntfy, LAN workstation model/STT, Home Assistant, ambient, CW2, or
|
|
encrypted-database configuration with an empty credential fails startup. This
|
|
keeps a missing env file from quietly becoming fallback behavior. The deployed
|
|
ntfy is currently `disabled: true`, and the workstation model arm is
|
|
`model_disabled: true`; the separately credentialed CW2 STT arm remains live.
|
|
Remove a dark-state flag only after provisioning that arm's credential.
|
|
|
|
Mint a scoped ntfy token rather than reusing an admin one. It needs write access
|
|
to the `maven` topic and nothing else:
|
|
|
|
```sh
|
|
ntfy access maven maven write-only
|
|
ntfy token add --expires=never maven
|
|
```
|
|
|
|
`disabled: true` keeps a documented ntfy block dark. When enabled, reminders
|
|
try ntfy and fall through to Telegram; delivery attempts record each reach.
|
|
|
|
## mavsttd — STT worker (optional, remote whisper.cpp)
|
|
|
|
Requires `LD_LIBRARY_PATH` to include deps/lib (for libwhisper.so, libggml-vulkan.so).
|
|
|
|
```bash
|
|
cd "$ROOT"
|
|
export LD_LIBRARY_PATH="$ROOT/deps/lib"
|
|
./mavsttd -socket /run/user/$UID/maven/stt.sock -model models/stt/ggml-small.bin
|
|
```
|
|
|
|
Without `-model` it runs as a stub (deterministic, no ML).
|
|
|
|
## mavttsd — TTS worker (optional, remote Piper)
|
|
|
|
Requires `LD_LIBRARY_PATH` to include deps/piper (for Piper's espeak-ng).
|
|
|
|
```bash
|
|
cd "$ROOT"
|
|
export LD_LIBRARY_PATH="$ROOT/deps/piper"
|
|
./mavttsd -socket /run/user/$UID/maven/tts.sock -piper deps/piper/piper -model models/tts/ru_RU-irina-medium.onnx -espeak_data deps/piper/espeak-ng-data
|
|
```
|
|
|
|
Without `-piper` it runs as a stub.
|
|
|
|
`-lexicon deploy/tts-lexicon.json` adds the pronunciation dictionary: a flat
|
|
JSON object of name to Russian spelling, applied to the text just before piper
|
|
reads it. It is how `Vikunja` is said as a word rather than spelled out, and how
|
|
`SearXNG` and `homesrv` are said at all. Off unless the flag is set; a path that
|
|
is set and unreadable stops mavttsd rather than letting it say names wrong in
|
|
silence. Adding a name needs a restart of mavttsd and nothing else.
|
|
|
|
## mavweb — PWA voice bridge (WebSocket ↔ TCP)
|
|
|
|
No CGo, no deps; builds with stock Go.
|
|
|
|
```bash
|
|
cd "$ROOT"
|
|
go build ./cmd/mavweb/
|
|
./mavweb -addr :9200 -voice 127.0.0.1:9100
|
|
```
|
|
|
|
To also receive proactive nudges in-app, pass the ntfy WebSocket subscribe URL
|
|
(the PWA connects to it directly; the auth token stays server-side config):
|
|
|
|
```bash
|
|
./mavweb -addr :9200 -voice 127.0.0.1:9100 \
|
|
-ntfy 'wss://ntfy.kvmx.ru/maven/ws?auth=<base64-token>'
|
|
```
|
|
|
|
`<base64-token>` is a *read*-capable ntfy access token, base64url-encoded
|
|
(ntfy's browser-WS auth: `Bearer tk_...` can't set a header, so ntfy takes it as
|
|
the `?auth=` query param). Without `-ntfy`, the PWA stays voice-only.
|
|
|
|
### presence-signal ingest (`-core`)
|
|
|
|
Pass mavend's IPC socket so mavweb can feed presence via `/api/signal`:
|
|
|
|
```bash
|
|
./mavweb -addr :9200 -voice 127.0.0.1:9100 \
|
|
-core /run/user/1000/maven/mavend.sock
|
|
```
|
|
|
|
- **page_heartbeat** (weak, τ=4min) — the PWA auto-pings every 30s. Nothing to do.
|
|
- **desk_active** (strongest, τ=8min) — a *workstation* signal (hyprland), so it
|
|
can't be a homesrv module. Run `scripts/desk-active.sh` on the PC via a
|
|
systemd-user timer, gated by hypridle (see the script header). Posts over wg.
|
|
- **wg_handshake** (coarse, τ=20min) — still unfed; it's homesrv-local
|
|
(`wg show latest-handshakes`), a natural small poller to add next.
|
|
|
|
Allowlisted keys only; without `-core`, `/api/signal` returns 503 and presence
|
|
stays cold-start `away`.
|
|
|
|
Open http://10.42.0.1:9200/ (or http://voice.kvmx.ru:9200/) on your phone from
|
|
inside the WireGuard tunnel. Tap & hold to speak; release to send; the reply
|
|
plays automatically.
|
|
|
|
## mavpoll — env poller (netdata + uptime-kuma → facts)
|
|
|
|
Thin adapter: reads netdata alarms + kuma monitor status and writes `env` facts
|
|
through core's IPC socket. This is what makes `service_down` (sev4) and
|
|
`netdata_critical` (sev3) rules fire on real data. Runs on homesrv where both
|
|
services live — hit them on localhost, not the public `.kvmx.ru` names.
|
|
|
|
```bash
|
|
cd "$ROOT"
|
|
go build ./cmd/mavpoll/
|
|
# netdata only (kuma disabled until its API key exists):
|
|
./mavpoll -socket /run/user/$UID/maven/mavend.sock -netdata http://127.0.0.1:19999
|
|
# with kuma: create an API key in Kuma → Settings → API Keys, then:
|
|
./mavpoll -socket /run/user/$UID/maven/mavend.sock \
|
|
-netdata http://127.0.0.1:19999 \
|
|
-kuma http://127.0.0.1:3001/metrics -kuma-key <API_KEY>
|
|
```
|
|
|
|
Writes only on value change (append-only, no per-tick churn). `service_down`
|
|
aggregates any monitor reading 0 as "down"; per-service granularity is a later
|
|
add. netdata `-timeout`/`-interval` tunable; defaults 8s / 60s.
|
|
|
|
### nginx (optional)
|
|
|
|
```bash
|
|
sudo cp cmd/mavweb/nginx.conf /etc/nginx/sites-available/voice.kvmx.ru
|
|
sudo ln -sf /etc/nginx/sites-available/voice.kvmx.ru /etc/nginx/sites-enabled/
|
|
sudo nginx -t && sudo systemctl reload nginx
|
|
```
|
|
|
|
## Quick smoke test (stubs, no models)
|
|
|
|
```bash
|
|
cd "$ROOT"
|
|
./mavend -config mavend.json # voice enabled, no stt/tts/embedder config → all stubs
|
|
```
|
|
|
|
## Run all tests
|
|
|
|
```bash
|
|
cd "$ROOT"
|
|
go test ./internal/router/ ./internal/delivery/... ./cmd/mavend/ ./cmd/mavsttd/ ./cmd/mavttsd/
|
|
```
|
|
|
|
## Benchmark
|
|
|
|
```bash
|
|
cd "$ROOT"
|
|
go test -bench=. ./internal/router/ ./cmd/mavsttd/ ./cmd/mavttsd/
|
|
```
|