diff --git a/configs/svos-apps/judge.toml b/configs/svos-apps/judge.toml new file mode 100644 index 0000000..9d349e5 --- /dev/null +++ b/configs/svos-apps/judge.toml @@ -0,0 +1,31 @@ +# /config/apps/judge.toml on nh3-svos — app-judge 0.0.1 (U19), from apps/judge/examples/judge.toml at v0.0.16. +# Prime 2026-10-18: drafting on gen-large (local, free); tasks may run on the six local chat models (the hermes runtime's +# `models` in instance.toml). The gateway key (vault nh3-svos/svos-judge-gateway-key, LiteLLM alias svos-app-judge) is +# scoped to exactly those six, so the /tasks model select offers exactly them. + +[gateway] +base = "http://10.250.50.70:4000/v1" +credential_env = "SVOS_JUDGE_GATEWAY_KEY" # /etc/svos/judge.env (root 0600); a project-scoped key, never the shared one + +[propose] +model = "gen-large" # hosted_vllm qwen3.8-flash on fv-ml1 :8022 — free; step up only on Prime's call +max_tokens = 4000 + +[admit] +scan_app = "app-terminal" + +[[routes]] +role = "*" +runtime = "hermes" +model = "gen-large" + +[limits] +models_seconds = 10 +propose_seconds = 120 +admit_seconds = 8 +route_seconds = 5 +catalog_seconds = 30 +concurrency = 8 +reply_bytes = 1048576 +brief_bytes = 262144 +health_seconds = 5 diff --git a/configs/svos-apps/svos-judge.service b/configs/svos-apps/svos-judge.service new file mode 100644 index 0000000..f425b46 --- /dev/null +++ b/configs/svos-apps/svos-judge.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/judge.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 Judge app (app-judge, U19): model catalogue, drafted checks, admission and routing for task seats +After=svos-kernel.service +Requires=svos-kernel.service +ConditionPathExists=/etc/svos/judge.env +[Service] +Type=simple +User=svc-svos +Group=svc-svos +UMask=0077 +WorkingDirectory=/var/lib/svos +EnvironmentFile=/etc/svos/judge.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:8409 +ExecStart=/opt/svos/current/apps-env/bin/svos-judge +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/playbooks/svos-releases/nh3-svos-b51aa5e-v0.0.16.yaml b/playbooks/svos-releases/nh3-svos-b51aa5e-v0.0.16.yaml new file mode 100644 index 0000000..0cb5644 --- /dev/null +++ b/playbooks/svos-releases/nh3-svos-b51aa5e-v0.0.16.yaml @@ -0,0 +1,98 @@ +# nh3-svos -> SVOS v0.0.16 (b51aa5e): kernel 0.0.16, UI 0.0.20, NEW app-judge 0.0.1 (U19; contract § Deployment). +# Prime 2026-10-18 ~0933 "go with your picks, cut over now": drafting model gen-large; tasks may run on the six local +# chat models. PRE-REQS (done before this playbook): the stage playbook; /root/svos-judge-enroll (mint-secret, root 0600); +# /etc/svos/judge.env holding SVOS_JUDGE_GATEWAY_KEY (LiteLLM svos-app-judge, vault nh3-svos/svos-judge-gateway-key). +# This playbook: install unit + judge.toml + quiesce entry; stop; backup; switch; config ([[apps]] app-judge, hermes +# `models`); start; enrol (credential appended to judge.env + printed NOWHERE); start the judge; front door. +# AFTER: Prime's three owner grants (svos-dev's console snippet), then [tasks].judge = "app-judge" + reload. +steps: + - name: Pre-reqs present + sudo: true + shell: test -f /opt/svos/releases/b51aa5e/apps-env/bin/svos-judge && test -s /root/svos-judge-enroll && grep -q '^SVOS_JUDGE_GATEWAY_KEY=' /etc/svos/judge.env && ! grep -q '^SVOS_APP_CREDENTIAL=' /etc/svos/judge.env + changed_when: "false" + - {name: judge unit, upload: {src: configs/svos-apps/svos-judge.service, dest: /etc/systemd/system/svos-judge.service, mode: "0644"}, sudo: true} + - {name: judge config, upload: {src: configs/svos-apps/judge.toml, dest: /var/lib/svos/config/apps/judge.toml, mode: "0600"}, sudo: true} + - name: judge.toml owned by svc-svos; quiesce hook lists svos-judge; daemon-reload + sudo: true + shell: | + set -e + chown svc-svos:svc-svos /var/lib/svos/config/apps/judge.toml + h=/etc/qemu/fsfreeze-hook.d/50-svos + grep -q "svos-judge.service" $h || { cp -p $h $h.pre-judge; sed -i "s/'svos-postoffice.service', 'svos-voice.service')/'svos-postoffice.service', 'svos-voice.service', 'svos-judge.service')/" $h; } + grep -n "svos-judge.service" $h; python3 -m py_compile $h 2>/dev/null || python3 -c "import ast,sys; ast.parse(open('$h').read())" + systemctl daemon-reload + - name: Stop the local apps then the kernel + sudo: true + shell: systemctl stop svos-voice svos-knowledge svos-interface svos-kernel; systemctl is-active svos-kernel svos-interface svos-knowledge svos-voice | tr '\n' ' '; echo + - name: Offline backup to /var/lib/svos/backup/pre-b51aa5e + sudo: true + shell: | + set -e + d=/var/lib/svos/backup/pre-b51aa5e + runuser -u svc-svos -- install -d -m 0700 $d + runuser -u svc-svos -- python3 -c " + import sqlite3 + s = sqlite3.connect('/var/lib/svos/state/svos.db'); t = sqlite3.connect('$d/svos.db'); s.backup(t); t.close(); s.close() + c = sqlite3.connect('file:$d/svos.db?mode=ro', uri=True); print('integrity', c.execute('pragma integrity_check').fetchone()[0], 'user_version', c.execute('pragma user_version').fetchone()[0])" + runuser -u svc-svos -- cp -p /var/lib/svos/config/instance.toml /var/lib/svos/config/instance.toml.pre-b51aa5e + - name: Switch current to b51aa5e + sudo: true + shell: ln -sfn /opt/svos/releases/b51aa5e /opt/svos/current.new && chgrp -h svc-svos /opt/svos/current.new && mv -T /opt/svos/current.new /opt/svos/current && readlink /opt/svos/current + - name: Config - [[apps]] app-judge (enrollment sha256 from the minted secret) + the hermes runtime's models; the new kernel must accept it (else restore + 57d90d6) + sudo: true + shell: | + sha=$(sed -nE 's/^sha256 ([0-9a-f]{64})$/\1/p' /root/svos-judge-enroll); test ${#sha} -eq 64 + runuser -u svc-svos -- python3 - "$sha" <<'PY' + import os, sys + sha = sys.argv[1] + p = "/var/lib/svos/config/instance.toml"; s = open(p).read() + assert 'slug = "app-judge"' not in s + anchor = '[[apps]]\nslug = "app-knowledge"\n' + assert s.count(anchor) == 1 + add = ('[[apps]] # U19 (v0.0.16, 2026-10-18): the judge; enrolled once, unit svos-judge\n' + 'slug = "app-judge"\nname = "Judge"\nbase_url = "http://127.0.0.1:8409"\nenrollment_sha256 = "' + sha + '"\n\n') + s = s.replace(anchor, add + anchor) + old = 'task_toolsets = ["terminal", "file"]\n' + assert s.count(old) == 1 + s = s.replace(old, old + '# 2026-10-18 (Prime): the chat models a task may run on (the /tasks select = the judge key\'s catalogue ∩ this list)\n' + 'models = ["gen-large", "gen", "gen-reasoning", "gen-small", "sec", "sec-reasoning"]\n') + t = p + ".tmp"; fd = os.open(t, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) + with os.fdopen(fd, "w") as f: f.write(s) + os.replace(t, p); print("edited") + PY + if runuser -u svc-svos -- /opt/svos/releases/b51aa5e/kernel-env/bin/svos-kernel check-config --data-root /var/lib/svos; then exit 0; fi + runuser -u svc-svos -- cp -p /var/lib/svos/config/instance.toml.pre-b51aa5e /var/lib/svos/config/instance.toml + ln -sfn /opt/svos/releases/57d90d6 /opt/svos/current.new && chgrp -h svc-svos /opt/svos/current.new && mv -T /opt/svos/current.new /opt/svos/current + systemctl start svos-kernel.service svos-interface svos-knowledge svos-voice; echo "RESTORED config + 57d90d6"; exit 1 + - name: Start the kernel then the local apps + sudo: true + shell: | + t0=$(date +%s.%N); systemctl start svos-kernel.service; rc=$?; t1=$(date +%s.%N) + echo "kernel start rc=$rc after $(python3 -c "print(round($t1-$t0,2))") s" + systemctl start svos-interface svos-knowledge svos-voice + sleep 8; curl -s http://127.0.0.1:8400/health; echo + - name: Enrol app-judge once; append the credential to judge.env (never printed); vault copy is done from nh3-dev + sudo: true + shell: | + set -e + grep -q '^SVOS_APP_CREDENTIAL=' /etc/svos/judge.env && { echo "already enrolled"; exit 0; } + tok=$(sed -nE 's/^token (sve_[A-Za-z0-9_-]+)$/\1/p' /root/svos-judge-enroll) + body=$(printf 'Authorization: Bearer %s\n' "$tok" | curl -s -X POST -H @- http://127.0.0.1:8400/auth/enroll) + unset tok + printf '%s' "$body" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['credential'].startswith('svc_'), 'no credential'; print('SVOS_APP_CREDENTIAL=' + d['credential'])" >> /etc/svos/judge.env + printf '%s' "$body" | python3 -c "import sys,json; d=json.load(sys.stdin); print('enrolled principal', d.get('principal'), 'kind', d.get('kind'))" + unset body; chmod 600 /etc/svos/judge.env; grep -oE '^[A-Z_]+=' /etc/svos/judge.env | tr '\n' ' '; echo + - name: Start the judge (its first manifest pins without a card), then the front door + sudo: true + shell: | + systemctl enable --now svos-judge; sleep 6; systemctl is-active svos-judge + journalctl -u svos-judge --since "-60s" --no-pager -o cat | grep -iE "regist|manifest|pin|health|degraded|error" | tail -6 | cut -c1-200 + /usr/local/sbin/svos-front-inventory 2>&1 | grep -E 'GENERATED|updated|root \*|path /assets' | head -4 +verify: + - name: Health schema 14 ok, nothing down + shell: h=$(curl -s http://127.0.0.1:8400/health); echo "$h"; echo "$h" | grep -q '"status":"ok"' && echo "$h" | grep -q '"down":0' + changed_when: "false" + - name: Front door inventory current + sudo: true + shell: /usr/local/sbin/svos-front-inventory --check + changed_when: "false"