The kvb-hub is a local review hub in a container bound to 127.0.0.1: one page, JSON APIs and an MCP endpoint for the operator's inbox, asks, decisions, staged-commit reviews, the radio stream, the agent graph, the worktree tree, push-to-talk and read-aloud radio (Piper, whisper.cpp) and an optional LLM command line. The hub is only a relay. The host executors in hub/client (the runner user service, the PermissionRequest hook and the approval CLI the commit skills call) verify a WebAuthn passkey signature against keys pinned outside every sandbox, refuse replays and fail closed. scripts/hub.sh with make hub-install, hub-check, hub-pin, hub-runner and hub-hook installs and checks the hub; hub-hook writes ~/.claude/settings.json after a backup. make watch-check shows a stuck watch hook. The new kvb-git-diff skill and the commit skills route reviews through the hub; the test_hub_* suites replace test_hub.py. Security review (kvb-think-criticize, three skeptics) fixed four holes: signed rules auto-approved chained shell commands; the client and LLM openers followed redirects, the LLM one with its bearer key; the worktree scan ran git fsmonitor and filter commands from agent-writable repos; compose set no memory, pids or health limits. Documented until core requirement 093, on the operator's word: the hub page is trusted while signing and pinning, local processes can forge the unsigned operator actions, and the container reads the host /tmp. Committed on the operator's word despite the gate: the passkey path is not yet verified live. Accepted MEDUSA false positives (137 findings on 115 lines, rows in security-accepted.jsonl): EXA002 x17, MEDUSA-MM-SCAN-058 x12, B603 x8, MEDUSA-FTSC-SCAN-089 x7, MEDUSA-PRIV-SCAN-762 x6, AIC-EXFIL-005 x5, EXA006 x5, MEDUSA-FTSCAN-231 x5, WEB-SSRF x5, MEDUSA-INF-SCAN-2057 x3, RH005 x3, TC001 x3, LO002 x2, MEDUSA-AEXP-SCAN-096 x2, MEDUSA-CGS-SCAN-028 x2, MEDUSA-INF-SCAN-1350 x2, MEDUSA-INF-SCAN-2369 x2, MEDUSA-INF-SCAN-2440 x2, MEDUSA-INF-SCAN-2638 x2, MLSC-SERIAL-002 x2, PL002 x2, PQC005 x2, SAST-PATH-001 x2, TC004 x2, TC005 x2, TC006 x2, TC007 x2, AA-146, EXA007, FINE-050, GB-ENCODE-001, GB-ENCODE-003, MEDUSA-CGS-SCAN-029, MEDUSA-CGS-SCAN-103, MEDUSA-DP-SCAN-1034, MEDUSA-DP-SCAN-1916, MEDUSA-DP-SCAN-954, MEDUSA-EXF26-TOOL-004, MEDUSA-FT-TRIGGER-005, MEDUSA-INF-SCAN-2206, MEDUSA-INF-SCAN-2457, MEDUSA-INF-WEB-003, MEDUSA-INFRA-SCAN-030, MEDUSA-MP-SCAN-1135, MEDUSA-RLHF-POISON-002, MEDUSA-RT-SCAN-006, MM-ENCODE-001, MP-124, PQC001, PQC002, PQC006, RP001, TC009, TC010, WEB-FLASK-004. 🎯 Fix: 80 % 💥 Break: 4 % 💸 Cost: 129 % ⏱️ Time: – 🛡️ Security: 100 % 💰 Value: ? 🏆 Score: 60 🚦 Gate: ⛔ fix 80 % < 90; cost 129 % > 100: ask the operator before the commit ``` /home/kevinveenbirkenbach/Repositories/github.com/kevinveenbirkenbach/skills/.worktrees/feature/kvb-hub ``` Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
73 lines
2.8 KiB
Python
73 lines
2.8 KiB
Python
from __future__ import annotations
|
|
|
|
import io
|
|
import json
|
|
import sys
|
|
import wave
|
|
from pathlib import Path
|
|
|
|
from hub_support import server
|
|
|
|
VOICES = ["de_DE-solo-medium:0", "en_US-test-high:0", "en_US-test-high:1", "en_US-test-high:2", "en_US-test-high:3"]
|
|
OPERATOR_VOICE = "en_US-test-high:0"
|
|
FAKE_PIPER = """
|
|
import argparse, json, os, sys, wave
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--model", required=True)
|
|
parser.add_argument("--config", required=True)
|
|
parser.add_argument("--output-file", required=True)
|
|
parser.add_argument("--speaker", type=int)
|
|
args = parser.parse_args()
|
|
text = sys.stdin.read()
|
|
with open(os.path.join(os.environ["HOME"], "piper.jsonl"), "a", encoding="utf-8") as log:
|
|
log.write(json.dumps({"argv": sys.argv[1:], "text": text, "tmp": os.environ["TMPDIR"], "cwd": os.getcwd()}) + "\\n")
|
|
if "FAIL" in text:
|
|
sys.exit(3)
|
|
with wave.open(args.output_file, "wb") as out:
|
|
out.setnchannels(1)
|
|
out.setsampwidth(2)
|
|
out.setframerate(22050)
|
|
out.writeframes(bytes(2 * len(text)))
|
|
"""
|
|
FAKE_WHISPER = """
|
|
import json, os, sys
|
|
data = sys.stdin.buffer.read()
|
|
with open(os.path.join(os.environ["HOME"], "whisper.jsonl"), "a", encoding="utf-8") as log:
|
|
log.write(json.dumps({"argv": sys.argv[1:], "sha256": __import__("hashlib").sha256(data).hexdigest()}) + "\\n")
|
|
if b"FAIL" in data:
|
|
sys.exit(4)
|
|
print(" hello from the [BLANK_AUDIO] mic")
|
|
print("second line")
|
|
"""
|
|
|
|
|
|
def write_engines(base: Path) -> server.Engines:
|
|
bin_dir, voices, scratch = base / "bin", base / "voices", base / "scratch"
|
|
for folder in (bin_dir, voices, scratch):
|
|
folder.mkdir()
|
|
for name, body in (("piper", FAKE_PIPER), ("whisper-cli", FAKE_WHISPER)):
|
|
path = bin_dir / name
|
|
path.write_text(f"#!{sys.executable}\n{body}", encoding="utf-8")
|
|
path.chmod(0o755)
|
|
for model, config in (("en_US-test-high", {"num_speakers": 4}), ("de_DE-solo-medium", {"num_speakers": 1}), ("zero", {"num_speakers": 0}), ("bad name!", {"num_speakers": 2})):
|
|
(voices / f"{model}.onnx").write_bytes(b"onnx")
|
|
(voices / f"{model}.onnx.json").write_text(json.dumps(config), encoding="utf-8")
|
|
(voices / "broken.onnx").write_bytes(b"onnx")
|
|
model = base / "ggml-base.bin"
|
|
model.write_bytes(b"ggml")
|
|
return server.Engines(piper=bin_dir / "piper", voices=voices, operator_voice=OPERATOR_VOICE, whisper=bin_dir / "whisper-cli", whisper_model=model, scratch=scratch)
|
|
|
|
|
|
def wav_bytes(channels: int, width: int, rate: int, frames: bytes) -> bytes:
|
|
buffer = io.BytesIO()
|
|
with wave.open(buffer, "wb") as writer:
|
|
writer.setnchannels(channels)
|
|
writer.setsampwidth(width)
|
|
writer.setframerate(rate)
|
|
writer.writeframes(frames)
|
|
return buffer.getvalue()
|
|
|
|
|
|
def log_rows(path: Path) -> list[dict]:
|
|
return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines()] if path.is_file() else []
|