feat(nh3-svos): SVOS voice (app-voice) live — /voice route before Basic auth (lease bearer), unit + quiesce hook; app credentials vaulted
This commit is contained in:
@@ -12,7 +12,7 @@ import tempfile
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
SERVICES = ('svos-kernel.service', 'svos-interface.service', 'svos-terminal.service',
|
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:
|
class Quiesce:
|
||||||
def __init__(self, directory, *, run=None):
|
def __init__(self, directory, *, run=None):
|
||||||
|
|||||||
@@ -13,3 +13,4 @@
|
|||||||
- 2026-10-07 (a737a06 refresh): `SERVICES` gained `svos-terminal`, `svos-knowledge` and `svos-postoffice` before
|
- 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
|
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.
|
(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.
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ class QuiesceTests(unittest.TestCase):
|
|||||||
spec=importlib.util.spec_from_loader('svos_quiesce',loader)
|
spec=importlib.util.spec_from_loader('svos_quiesce',loader)
|
||||||
self.module=importlib.util.module_from_spec(spec);spec.loader.exec_module(self.module)
|
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'),
|
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
|
self.calls=[];self.stop_fail=False;self.start_fail=False;self.dirty_stop=False
|
||||||
def run(args,**kwargs):
|
def run(args,**kwargs):
|
||||||
self.calls.append(args)
|
self.calls.append(args)
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -65,6 +65,19 @@ https://svos.phasefinal.com:443 {
|
|||||||
root * /opt/svos/current/static
|
root * /opt/svos/current/static
|
||||||
file_server
|
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 <lease secret>` (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
|
# 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
|
# 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.
|
# comments only when there is no previous release.
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ Every vLLM seat on a shared card (GPU 0, GPU 1) has byte-pinned KV (`--kv-cache-
|
|||||||
|
|
||||||
## Recent decisions
|
## 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 <lease>` there (`web/src/lib/voice.ts`) and Basic auth would reject it. All four app credentials are now vaulted as `nh3-svos/svos-<app>-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]` **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]` **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`
|
- `[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`
|
||||||
|
|||||||
@@ -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-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-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-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) |
|
| app-miranda, svos-agent | — | — | packages in apps-env only (U13) |
|
||||||
Credentials `/etc/svos/<app>.env` (root 0600); units in `configs/svos-apps/`; the quiesce hook lists all three new units.
|
Credentials `/etc/svos/<app>.env` (root 0600), vaulted as `nh3-svos/svos-<app>-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
|
- **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
|
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
|
page's auth). `/verbs/*` and `/health` are refused unless they come from the box itself. The kernel's signed calls
|
||||||
|
|||||||
Reference in New Issue
Block a user