diff --git a/configs/qemu-ga/svos/50-svos b/configs/qemu-ga/svos/50-svos index 18fa8c3..3abfb8c 100755 --- a/configs/qemu-ga/svos/50-svos +++ b/configs/qemu-ga/svos/50-svos @@ -12,7 +12,7 @@ import tempfile import time SERVICES = ('svos-kernel.service', 'svos-interface.service', 'svos-terminal.service', - 'svos-knowledge.service', 'svos-postoffice.service') + 'svos-knowledge.service', 'svos-postoffice.service', 'svos-voice.service') class Quiesce: def __init__(self, directory, *, run=None): diff --git a/configs/qemu-ga/svos/README.md b/configs/qemu-ga/svos/README.md index 0b3eca6..1982e15 100644 --- a/configs/qemu-ga/svos/README.md +++ b/configs/qemu-ga/svos/README.md @@ -13,3 +13,4 @@ - 2026-10-07 (a737a06 refresh): `SERVICES` gained `svos-terminal`, `svos-knowledge` and `svos-postoffice` before they were started. The hook restores only the units that were active at freeze, so a declared-but-stopped app (postoffice until its bridge handle exists) is recorded as inactive and left stopped. +- 2026-10-08: `svos-voice` added (Prime: voice on), before it started. diff --git a/configs/qemu-ga/svos/test_quiesce.py b/configs/qemu-ga/svos/test_quiesce.py index fd3be13..9ce2475 100644 --- a/configs/qemu-ga/svos/test_quiesce.py +++ b/configs/qemu-ga/svos/test_quiesce.py @@ -10,7 +10,8 @@ class QuiesceTests(unittest.TestCase): spec=importlib.util.spec_from_loader('svos_quiesce',loader) self.module=importlib.util.module_from_spec(spec);spec.loader.exec_module(self.module) self.states={'svos-kernel.service':('active','success'),'svos-interface.service':('inactive','success'), - 'svos-terminal.service':('inactive','success'),'svos-knowledge.service':('inactive','success'),'svos-postoffice.service':('inactive','success')} + 'svos-terminal.service':('inactive','success'),'svos-knowledge.service':('inactive','success'),'svos-postoffice.service':('inactive','success'), + 'svos-voice.service':('inactive','success')} self.calls=[];self.stop_fail=False;self.start_fail=False;self.dirty_stop=False def run(args,**kwargs): self.calls.append(args) diff --git a/configs/svos-apps/svos-voice.service b/configs/svos-apps/svos-voice.service new file mode 100644 index 0000000..6fdc920 --- /dev/null +++ b/configs/svos-apps/svos-voice.service @@ -0,0 +1,30 @@ +# eshpfi configs/svos-apps/ (infra-ops, 2026-10-07, a737a06 refresh), modelled on svoperatingsystem +# deployment/nh3-svos/svos-interface.service. The credential is /etc/svos/voice.env (root 0600, SVOS_APP_CREDENTIAL), +# minted by enrollment on the private kernel listener. Listed in the qemu-ga quiesce hook (configs/qemu-ga/svos/50-svos). +[Unit] +Description=SVOS Voice app (app-voice, U12) +After=svos-kernel.service +Requires=svos-kernel.service +ConditionPathExists=/etc/svos/voice.env +[Service] +Type=simple +User=svc-svos +Group=svc-svos +UMask=0077 +WorkingDirectory=/var/lib/svos +EnvironmentFile=/etc/svos/voice.env +Environment=SVOS_KERNEL_URL=http://127.0.0.1:8400 +Environment=SVOS_DATA_ROOT=/var/lib/svos +Environment=SVOS_APP_LISTEN=127.0.0.1:8402 +ExecStart=/opt/svos/current/apps-env/bin/svos-voice +Restart=on-failure +RestartSec=3 +TimeoutStopSec=20 +KillMode=control-group +NoNewPrivileges=yes +ProtectSystem=strict +ProtectHome=yes +PrivateTmp=yes +ReadWritePaths=/var/lib/svos +[Install] +WantedBy=multi-user.target diff --git a/configs/svos-front/Caddyfile b/configs/svos-front/Caddyfile index 663e446..4b0af11 100644 --- a/configs/svos-front/Caddyfile +++ b/configs/svos-front/Caddyfile @@ -65,6 +65,19 @@ https://svos.phasefinal.com:443 { root * /opt/svos/current/static file_server } + # Voice (U12, app-voice on 127.0.0.1:8402; Prime 2026-10-08 "voice on"). BEFORE basic_auth on purpose: the browser + # calls /voice/* with `Authorization: Bearer ` (web/src/lib/voice.ts), minted by a signed kernel open + # that itself goes through /api behind Basic auth. Basic auth here would reject the bearer, and the kernel's + # Authorization strip below would drop it. The lease IS the auth; the app refuses /voice without it. The /voice + # prefix is preserved upstream, Authorization passes untouched, no buffering (streamed audio), cancellation + # reaches the listener (client disconnect closes the upstream). Engines stay private (fv-ml1 :8300, irv-ml1 :8198). + @voice path /voice /voice/* + handle @voice { + reverse_proxy 127.0.0.1:8402 { + flush_interval -1 + } + } + # Previous release's hashed assets across a rollout (svos-dev 2026-10-05): open workers restore evicted inventory # credential-free. Generated by the same tool: a matcher + handle rooted in THAT release's static dir, or # comments only when there is no previous release. diff --git a/persistent-memory.md b/persistent-memory.md index 2777fce..576fe66 100644 --- a/persistent-memory.md +++ b/persistent-memory.md @@ -187,6 +187,7 @@ Every vLLM seat on a shared card (GPU 0, GPU 1) has byte-pinned KV (`--kv-cache- ## Recent decisions +- `[2026-10-08]` **SVOS voice ON (Prime, via svos-dev): app-voice on 127.0.0.1:8402, apps up 4 + terminal degraded; Connect → "Voice connected", Enable lists the gateway's 30 voices (Chromium over the LAN door).** `/voice/*` is routed BEFORE Basic auth, because the browser sends `Authorization: Bearer ` there (`web/src/lib/voice.ts`) and Basic auth would reject it. All four app credentials are now vaulted as `nh3-svos/svos--credential`. → `servers/nh3-svos/README.md` - `[2026-10-08]` **SVOS served on the LAN too (Prime):** Caddy binds `10.100.50.43:443` as well as `100.64.0.9:443`; the internal AdGuards answer `svos`/`svos-terminal.phasefinal.com` → `10.100.50.43` (MagicDNS still answers 100.64.0.9). Verified from LAN hosts at all three sites. → `servers/nh3-svos/README.md` - `[2026-10-08]` **fv-ml1 is OFF the mesh again, by design**: its tailscaled is disabled, and the break-glass watchdog joins only on fleet loss (09-15 design, archived). The 0030 reboot closed a door that had been left open, so mesh-side tests need another peer, not fv-ml1. The watchdog timer is active. - `[2026-10-08]` **heid's runaway sweep killed on Prime's order: two `heidnotes/sweep.py` processes had written ~76 GB into a deleted-but-open file; nh3-dev root 86% → 66%** — the sweep read its own output. heid fixed it at the source. → `persistent-memory.d/2026-10-08-heid-runaway-sweep-killed.md` diff --git a/servers/nh3-svos/README.md b/servers/nh3-svos/README.md index 2d14d20..fd374b6 100644 --- a/servers/nh3-svos/README.md +++ b/servers/nh3-svos/README.md @@ -39,8 +39,9 @@ configuration, enrollment and acceptance. | app-knowledge | 127.0.0.1:8405 | svos-knowledge | up; Albok token `01M4D22HHJ7AJHX30WPDE02DFC`, vault `nh3-svos/albok-token-knowledge`, grants per A111 `read:fleet/memory`, `read:fleet/stash`, `read:personal/notes` (no wildcard, no restricted wing, no class grant) | | app-terminal | 127.0.0.1:8404, base_url **https://svos-terminal.phasefinal.com** | svos-terminal | **degraded by design**: no zellij and no fleet session on this box | | app-postoffice | 127.0.0.1:8406 | svos-postoffice (installed, NOT enabled) | declared + enrolled, stopped until Prime declares the bridge handle; `handle = "svos"` in its config is the example's placeholder | + | app-voice | 127.0.0.1:8402, front door `/voice/*` (same origin, BEFORE Basic auth; the lease bearer is the auth) | svos-voice | up since 2026-10-08 (Prime: voice on); engines in `config/apps/voice.toml`: STT Parakeet `http://10.251.50.54:8300`, TTS fleet gateway `http://irv-ml1.nh3.internal:8198`; owner grants `app:app-voice` write (autonomous) + read | | app-miranda, svos-agent | — | — | packages in apps-env only (U13) | - Credentials `/etc/svos/.env` (root 0600); units in `configs/svos-apps/`; the quiesce hook lists all three new units. + Credentials `/etc/svos/.env` (root 0600), vaulted as `nh3-svos/svos--credential` (terminal, knowledge, postoffice, voice; 2026-10-08); units in `configs/svos-apps/`; the quiesce hook lists every app unit. - **Terminal origin:** `svos-terminal.phasefinal.com` = headscale MagicDNS extra record → 100.64.0.9, its own Caddy site (LE via DNS-01). **No Basic auth** (Chromium cancels Basic prompts in cross-origin frames; the lease is the page's auth). `/verbs/*` and `/health` are refused unless they come from the box itself. The kernel's signed calls