7de0f57fa632a1bf4a3aa619fdf639736478e7a1
262 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7de0f57fa6 |
R5: consent-gated recruitment + per-hop X25519 sealed credentials
Add the in-band, signature-gated consent handshake that governs whether a
node may be recruited into a measured SOR circuit, plus a per-recipient
sealed box so a hop credential decrypts only with the host's X25519 key.
- crypto.rs: seal_to_pubkey/open_sealed (ephemeral X25519 -> HKDF-SHA256 ->
Fernet AEAD), reusing the existing fernet crate (no new symmetric primitive).
- sor/consent.rs: signed ConsentRequest (Ed25519 persona), node_evaluate
(reject unless the signature verifies), CircuitBuilder that recruits a hop
only on an accept it can open; parse_sor_frame with never-panic proptests.
- net.rs: {"_sor":...} control frames are live-only and classified out-of-band
by the SOR layer (parse_sor), never surfaced as chat/app events.
- cmd_chat/sor/consent.py + bridge _sor case: bit-compatible Python mirror;
the agent only observes/classifies consent frames — it never auto-accepts
and stands up no forwarder (forwarding is R4, isolated-engine-only).
Acceptance check (roadmap R5) green both languages: host recruits a hop only
after an explicit accept; a reject leaves no entry; the credential opens only
with the host key (third party cannot); unsigned/forged request -> rejected.
Cross-language KDF parity pinned by a shared known-answer vector.
Rust 75 passed; Python SOR suite 37 passed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
a9308edc5d |
R3: immutable append-only event log sealed into the run manifest
Adds the tamper-evident event stream that makes every DV auditable. Each
measurable moment is appended as one JSON object per line to an append-only
output/sor-runs/<ts>/events.jsonl over the closed vocabulary {consent_request,
consent_accept, consent_reject, circuit_build, hop_add, bridge_forward,
churn_kill, churn_spawn, rebuild_start, rebuild_done}; on close the file is
SHA-256'd and the digest is sealed exactly once into manifest.json (the single
sanctioned None->hash completion of the manifest).
Records carry only metadata (fingerprints, byte counts, latencies, decisions),
never message plaintext, so the zero-knowledge relay property is preserved. The
deterministic replay_fixture_circuit emits a seed-driven event stream with no
engine, socket, or traffic (containment-safe) and is the basis of
instrument-validation gate item 6.
- cmd_chat/sor/events.py: EventLog (append-only, per-record schema),
replay_fixture_circuit, replay_and_seal.
- cmd_chat/sor/provenance.py: seal_manifest (seal-once events SHA + stop ts).
- tests: replay -> schema-valid JSONL whose SHA matches the manifest;
append-only (original prefix byte-identical after further appends);
seed-determinism modulo timestamps; seal-once immutability.
Live emit wiring into the _sor handlers lands with R4/R5.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
7ed23704ea |
R2: run manifest / provenance writer for reproducible, auditable runs
write_manifest() freezes at circuit-experiment start everything needed to
reproduce and audit a SOR run and writes an immutable
output/sor-runs/<ts>/manifest.json: the R1 --sor-seed, topology/selector/churn
schedule id, one persona fingerprint per participating node, worktree git SHA,
isolated engine kind + image digest, pip/cargo dependency freeze, and
start/stop timestamps. The events.jsonl SHA-256 field is reserved for R3.
Provenance only: no forwarder, no engine spawned. Defense-in-depth to keep
containment load-bearing, the schema refuses to record a non-isolated
("local") engine at run or node level; the binding assertion still lands with
the R4 forwarder.
- cmd_chat/sor/provenance.py: Node/RunManifest, node_fingerprint (bit-for-bit
mirror of persona.rs::fingerprint_of), git/deps capture, explicit
dependency-free schema validator.
- hh/src/persona.rs: fingerprint_of known-vector parity test.
- tests: R2 acceptance predicate (manifest present + schema-valid, non-empty
seed, >=1 fingerprint/node), immutability, containment rejection of local
engines, and cross-language fingerprint parity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
c04f9aed23 |
R1: deterministic --sor-seed core for reproducible measurement
Adds the single stochasticity source for the SOR measurement instrument so every later stochastic decision (path selection R4, churn/selector R7, padding jitter R4) is driven by one --sor-seed and is byte-reproducible run-to-run — the R1 acceptance check and instrument-validation gate item 2. - hh/src/sor/mod.rs: SplitMix64 + SHA-256 domain-separated sub-streams + unbiased Lemire bounded sampling -> deterministic select_path/bringup. Pure bookkeeping: no forwarder, no socket, no engine. Isolated-engine containment assertions land with the R4 forwarder. - hh/src/main.rs: `sor-bringup --sor-seed` emits the deterministic circuit-build sequence as JSON (observable reproducibility). - cmd_chat/sor/config.py: bit-for-bit Python mirror so the seed means the same thing on both sides. - Tests (Rust proptest+unit, Python pytest): SplitMix64 known-answer vector, same-seed determinism, seed divergence, bounded/no-panic, and a shared cross-language parity vector asserted identically in both suites. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c9aa4a68ad |
merge: fold benchmark + pseudonymous-attribution into main
CI / rust client (hh) (macos-latest) (push) Has been cancelled
CI / rust client (hh) (ubuntu-latest) (push) Has been cancelled
CI / rust coverage (push) Has been cancelled
CI / python server (3.10) (push) Has been cancelled
CI / python server (3.11) (push) Has been cancelled
CI / python server (3.12) (push) Has been cancelled
CI / headless e2e smoke (push) Has been cancelled
CI / dependency audit (push) Has been cancelled
CI / secret scanning (push) Has been cancelled
Integrate the laptop feature branch (multi-language capability benchmark, model picker, and ESA-style pseudonymous file attribution) onto the current church main (native AI harness, podman/vbox sandboxes, † theme). Only conflict was the help/status line — kept the newer podman grammar + /help, spliced in /export-signed, and aligned attribution strings to the † sigil. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
fcb3cb8684 |
chore(attribution): commit Cargo.lock for ed25519-dalek dep
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
f4180edf61 |
feat(attribution): pseudonymous file attribution (ESA-style)
Adapt Princess_Pi's Encrypt-Share-Attribution scheme so shared files are anonymous but provably attributable. A. In-session: a persistent Ed25519 persona key (~/.config/hack-house/ persona_ed25519) signs every /send and /sendroom offer over the content hash; receivers verify it and see the persona fingerprint. Optional --attest <passphrase> attaches a revealable SHA-512(pass||sha256) commitment. Additive JSON — wire-compatible with the Python client. B. Portable: /export-signed <dir> packages a directory into Princess_Pi's exact ESA 7z (fresh per-round Ed25519 sig over an inner 7z, SHA-512 checksums, bundled verify scripts). Builder embedded from hh/tools/esa/esa_build.sh; verifiable with just bash+7z+ssh-keygen. Tests: 45 pass (3 new persona). ESA archive build + verify-everything.sh + passphrase reveal verified end-to-end. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
7fb3911550 |
feat(bench): multi-language capability benchmark + model picker
Add bench-lang.py + bench/ package: a third benchmark axis answering "which open-source model is best for my workflow?" across Python, JavaScript, Go, Rust and Bash. - MultiPL-E (Go/Rust/JS/Bash) + original HumanEval (Python), loaded via the HF datasets-server REST API with on-disk cache — no datasets/ pyarrow dependency. - Completions go straight to Ollama /api/generate with raw=True so instruct models continue the code instead of replying with prose. - Code runs in rootless, network-less podman (safe default) with a host-toolchain fallback; pass@1/pass@k via the HumanEval estimator. - run/score separation: results persist to a scorecard JSON, then `pick --workflow ops` re-ranks without re-running any model. - Extensible: a new language is one Lang entry; a new workflow is one block in workflows.json. Also fix a --runs grant-persistence bug in bench-sandbox.py: the grant leaked across runs, invalidating the L0-nogrant refusal test on runs 2+. Each run now revokes the ACL and starts ungranted. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c5715ba2e3 |
test(scripts): add E2E AI chat + sandbox code-path benchmarks
bench-ai.py drives the /ai chat path end-to-end (SRP -> Fernet -> WebSocket -> provider), measuring TTFT/total/tok-s per model, with a --direct provider mode that isolates raw model throughput from event-loop contention. bench-sandbox.py benchmarks the /ai <agent> !<task> sandbox code path by playing the room owner on the zero-knowledge relay: it grants drive, sends graded tasks (L0 no-grant refusal, file/script/logic/multistep, destructive gating+confirm, blast-radius cap), captures the agent's injected _sbx:input frames, and grades correctness behind the same destructive guard. Includes REPL-aware exec replay (folds python3 sessions into heredocs), prompt-prefix stripping, model-fail vs replay-limit tagging, --runs N averaging with per-step timing, and auto-bumped timeouts for reasoning models. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c09b428718 |
feat(ai): /grant ai grants drive to every AI agent at once
CI / rust client (hh) (macos-latest) (push) Has been cancelled
CI / rust client (hh) (ubuntu-latest) (push) Has been cancelled
CI / rust coverage (push) Has been cancelled
CI / python server (3.10) (push) Has been cancelled
CI / python server (3.11) (push) Has been cancelled
CI / python server (3.12) (push) Has been cancelled
CI / headless e2e smoke (push) Has been cancelled
CI / dependency audit (push) Has been cancelled
CI / secret scanning (push) Has been cancelled
So the owner never has to name each model. Tracks an ai_agents set (populated from `_ai` typing/stream frames and the "(ai) online" announce, pruned on leave); `/grant ai` intersects it with the live roster and grants all in one ACL broadcast. Help text gains a /grant ai row. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
70d6e26b24 |
feat(ai): prompt-anchor native harness for the fast CPU model
Optimize the native tool-calling loop for qwen2.5:3b on CPU, where it
previously invented paths (/ai/bin/bash), ran scripts it never wrote, and
silently dropped valid actions. Three changes:
- NATIVE_SYSTEM rewritten directive: explicit write→chmod→run workflow,
relative paths only, never run an uncreated file, never guess interpreter
paths, fix the cause on non-zero exit.
- New _sandbox_facts() probe injects LIVE SANDBOX STATE (real cwd, bash
path, current files) into the system prompt so the model anchors to
ground truth instead of guessing.
- OllamaProvider recovers tool calls qwen emits as <tool_call>{json}</…>
TEXT in content (brace-balanced JSON scan), so a correct action isn't lost.
- Bump Ollama timeout 120→240s: the tool turn is non-streaming and a long
write_file can exceed a tighter cap on a contended CPU box.
Live-validated (podman/Kali): 0/3 incoherent → reliable write/run with
self-correction on exit=126 for both single- and multi-script tasks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
0c04ac74ee |
chore(theme): swap ⛧ sigil for † across UI, scripts, docs
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
06a87e8e76 |
docs(readme): Parrot OS docker, Kali podman, AI-acts-in-sandbox
Reflect current backends (local/docker/podman/multipass — Docker=Parrot OS Security, Podman=Kali rootless no-sudo), the backend-first /sbx grammar, and the AI agent's ability to drive the sandbox via /ai <name> !<task> (advisory when ungranted, acting once granted). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
0b1d09f0b5 |
feat(ai): visible native harness — PTY mirror + readable chat
Display-mirror hybrid (docs/plan-harness-visibility.md §2): native tool calls now show up in the shared sandbox terminal again via inert `# `- prefixed comment lines (comment-prefix = anti-double-run/anti-escape), mirroring only each command. Chat de-flooded to opener + final summary. write_file mkdir -p parent dir so relative/absolute paths both work (fixes the regression where script creation silently failed). ui.rs fmt_line returns Vec<Line> splitting on \n so multi-line agent output renders as an indented block instead of one garbled row. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
946df65b72 |
feat(ai): native host-side tool-calling harness (Phase 2)
CI / rust client (hh) (macos-latest) (push) Has been cancelled
CI / rust client (hh) (ubuntu-latest) (push) Has been cancelled
CI / rust coverage (push) Has been cancelled
CI / python server (3.10) (push) Has been cancelled
CI / python server (3.11) (push) Has been cancelled
CI / python server (3.12) (push) Has been cancelled
CI / headless e2e smoke (push) Has been cancelled
CI / dependency audit (push) Has been cancelled
CI / secret scanning (push) Has been cancelled
Implement the bounded native harness from docs/spec-native-harness.md §1.3 and
make it the default granted-!task path. The model runs host-side (no container→
host Ollama hop); only its tool calls exec in the sandbox.
providers.py:
- OllamaProvider.complete_with_tools(system, messages, tools) -> (text, calls):
one non-streaming /api/chat turn with a `tools` schema; parses message.tool_calls
(dict or JSON-string arguments). Caches tool capability (_tools_ok / supports_tools).
- ToolsUnsupported raised when the model rejects `tools` ("does not support tools").
bridge.py:
- NATIVE_SYSTEM + a 3-tool schema (run_shell / write_file / read_file), turn/byte caps.
- _run_native: seed transcript window + task → loop up to max_turns; exec each tool
call in the sandbox, feed captured output back as a `tool` message; stop on a plain
answer or the cap; stream per-call progress to chat. Degrades to _run_simple when the
provider has no complete_with_tools or the model rejects tools.
- _exec_prefix/_exec_capture/_exec_tool: <engine> exec into docker/podman/multipass/local;
paths passed as positional args + content via stdin (no shell interpolation); combined
stdout+stderr byte-capped + time-bounded. run_shell is the only intentional shell.
- Guards: DESTRUCTIVE run_shell commands are blocked (not run — no human in the loop;
use simple + /ai confirm for destructive intent); MAX_COMMANDS budget per task.
- _run_in_sandbox dispatches native|simple; default harness flipped to native.
__main__.py: default harness native (self-degrades to simple, so safe).
Offline-tested: full write/run/read loop on the local backend; destructive block
(rm -rf never executed); ToolsUnsupported → simple fallback. Live Ollama wire
validation deferred to Phase 3 bench (daemon was down). py_compile clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
e49dbca451 |
refactor(ai): strip Goose harness (Phase 1) — native/simple host-side only
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Remove the Goose agentic harness across the codebase per
docs/spec-native-harness.md §3. Goose made N sequential model calls inside the
sandbox (slow on CPU-only hardware) and forced an in-container→host Ollama
gateway that tripped the rootless-Podman slirp4netns loopback bug.
- bridge.py: delete _run_goose/_goose_argv/_goose_present + GOOSE_* consts and
the present-cache; __init__ now takes harness="simple"/max_turns=5; granted
!task runs _run_simple until the native loop lands (Phase 2).
- __main__.py: --harness {native,simple} (was {goose,simple}); drop
--goose-max-turns, add --max-turns; default harness simple.
- app.rs: /ai start accepts native|simple (plain aliases simple) instead of a
bare plain flag; refresh harness comments.
- sbx.rs: remove the in-container Ollama gateway (Docker host-gateway / Podman
slirp4netns host-loopback) and the dk_bootstrap OLLAMA_HOST env — kills the
slirp4netns loopback bug; drop Goose comments.
- bootstrap.sh: drop goose from the prereq probe.
- bootstrap-ai.sh: remove the entire Goose install block, --no-goose flag,
GOOSE_INSTALLER_URL, host config writer, and goose_bin helper.
- sandbox-bootstrap.sh: remove the in-sandbox Goose binary install + config.
- spec-goose-harness.md: banner — harness portion superseded; Podman stays.
cargo check + py_compile clean. No Goose refs remain (headroom/ untouched).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
cf6b0b5b73 |
fix(ai): agent reconnects instead of vanishing; spec the native harness
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
The /ai agent held a single, un-shielded websocket with no retry. Any close — server restart, ping/idle reap, laptop sleep, a transient blip — ended the serve loop, so run_async returned and the process exited silently: the agent dropped from the roster with no /ai stop and no goodbye. - run_async now wraps the connection in a backoff-reconnect loop (1s→30s, resets after a healthy ≥30s session). The server frees our session+name on drop, so each attempt re-runs SRP to mint a fresh token. Only Ctrl-C / process kill (KeyboardInterrupt / CancelledError, how /ai stop ends us) breaks the loop. - _serve shields each frame via _handle_frame so one malformed/poisoned frame — or a handler error — can't unwind the loop; ConnectionClosed and cancellation propagate up to the reconnect loop. - Forgiving keepalive (ping_interval=20, ping_timeout=60) so a heavy CPU-only Ollama generation doesn't trip a false drop. Also adds docs/spec-native-harness.md: replace the heavyweight Goose harness with a lightweight host-side Ollama-native tool-calling loop (model runs host-side, only commands exec in the sandbox — the slirp4netns→host-Ollama bug disappears), and a file-by-file plan to strip all Goose integration. Supersedes the harness portion of spec-goose-harness.md (Podman backend stays). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
f93c8c5e4f |
feat(sandbox): VM library — pointer catalog + local build via /sbx vmlib
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Add an installable-VM catalog that ships pointers only (no multi-GB images in the repo). When a VM is chosen it is BUILT LOCALLY on the caller's own machine — nothing is relayed to the room. - scripts/vbox-library.json: 7-entry manifest (Windows 11, macOS Sonoma, Kali, Parrot, Ubuntu 24.04, Fedora 41, Debian 12) with download pointers, ostype, cpu/mem/disk, and build kind. - scripts/vbox-library.sh: --list / --info / --plan / --install. Build kinds: iso (download/--iso + createvm + boot installer; EFI+TPM for Win11), cloudimg (delegate to vbox-new.sh, unattended), ova (import), manual (pointer-only, e.g. macOS per Apple licensing). Detect-first: --plan changes nothing, install refuses to clobber and rolls back half-built VMs, direct URLs fall back to the page + --iso <path>. - sbx.rs: vbox_library()/library_vm()/vbox_library_install() loaders + running_vms() for live-state markers. - app.rs: /sbx vmlib (catalog ✓installed/↓available), /sbx vmlib <id> (pointer/notes), /sbx vmlib <id> install [--iso path] (local build); /sbx vms now flags running VMs (▶). Registered in SBX_SUBCOMMANDS + usage. - ui.rs: help entry for the VM library. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
15aa2027c4 |
refactor(sandbox): remove container browser-GUI (noVNC), keep vbox VM GUI
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
The docker/podman headless-container desktop (XFCE + TigerVNC + websockify + noVNC published on host 127.0.0.1:6080) is removed. It was unreliable: the desktop stack installed asynchronously during provisioning with every step wrapped in `|| true` (silent failures), while the host port mapping existed the moment the container ran — so opening the browser before websockify bound 6080 inside the container reset the connection (ERR_CONNECTION_RESET), with no readiness signal and no loud failure. Containers also can't be rendered as a real desktop by VirtualBox (no framebuffer/display), so the only desktop path that stays is the native VirtualBox VM GUI. Removed: - sbx.rs: GUI_PORT const; PortHolder/port_holder()/kill_port_holder() (the port-consent gate existed only for the noVNC publish); the `gui` param + `-p 127.0.0.1:6080:6080` block in prepare(); HH_SBX_GUI env in dk_bootstrap(); the `gui` param on provision(). - app.rs: PendingGuiLaunch + PortPrompt structs; the port_prompt App field; the `gui` field on PendingSudoLaunch; the port-consent modal + the sudo-submit port check; want_gui/gui parsing + the container-GUI launch message; `gui` threaded through spawn_launch and all call sites. - sandbox-bootstrap.sh: the entire HH_SBX_GUI=1 desktop block. - ui.rs / usage strings: dropped "[gui]"/"noVNC desktop" from the docker/podman help and the /sbx usage line. Kept (unchanged): the VirtualBox VM GUI — gui_launch() (VBoxManage startvm --type gui), launch_vbox_gui, `/sbx vbox gui <vm>` and the `/sbx gui` alias. The `gui` keyword is still stripped from positionals so `/sbx vbox gui <vm>` parses. cargo check passes clean (no warnings). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
18a8936caa |
feat(sandbox): masked sudo modal for VirtualBox install path
The VBox GUI launch path previously had no in-TUI password capture — it relied on cached creds (`sudo -n`) and otherwise aborted with guidance. Generalize the existing masked sudo modal to cover it: `SudoPrompt.pending` becomes a `PendingPrivileged` enum (container `Launch` | `VboxInstall`), so the same local-only, never-logged password buffer now authorizes a VBox install too. On submit the captured password is fed to `ensure_vbox_install` via `sudo -S`, then the VM boots. Empty/Esc still cancel cleanly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c6eeb9b897 |
feat(sandbox): port-in-use consent gate + uniform sudo capture
GUI launch (docker/podman) now pre-flights the noVNC port (6080): if it's already bound, a modal names the holding pid and asks before killing it and proceeding — never a blind `bind: address already in use` collision. Sudo capture is now uniform across every backend installer. Previously only Docker fed the masked-modal password through `--stdin-pass`/`sudo -S`; the captured password was silently dropped for Podman/Multipass and never wired for VirtualBox, and those scripts used bare `sudo` (which hangs/corrupts a raw-mode tty). Now: - shared `run_ensure()` feeds the password to any ensure-*.sh via stdin - podman/multipass/vbox scripts gain the docker sudo ladder (interactive `sudo` / `--yes` `sudo -n` / `--stdin-pass` `sudo -S -p ''`) - podman's apt path wrapped in `sh -c` so one sudo covers update+install - vbox GUI path preflights `sudo_ready()` with actionable guidance, falling back to fail-fast `sudo -n` instead of a tty hang Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
ad13ec14ba |
docs(sandbox): specs for goose harness + GUI distros, command ref, themes
Add the goose-harness and sandbox-distros-GUI design specs, a consolidated command reference and demo-reels plan, the AI-harness planning note, and two new client themes (blue-orange, pink-red-gray). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
20091a9c26 |
feat(sandbox): podman backend + goose AI harness + noVNC GUI sandbox
Add Podman as a rootless/daemonless sandbox backend alongside Docker, Multipass and Local, and wire Goose in as the default agentic harness for the granted `!task` path (bridge execs `<engine> exec <name> goose run` and streams output to chat; auto-degrades to the simple one-shot injector when goose is absent). Add an optional GUI sandbox track (XFCE + TigerVNC + websockify/noVNC on :6080) summoned via `/sbx <engine> gui`, plus container-side provisioning in sandbox-bootstrap.sh and a host-side ensure-podman.sh prereq helper. Refresh the in-app command help to the backend-led `/sbx <engine> [gui]` grammar and minor ui tweaks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
eec1714735 |
feat(layout): input-bar + chat/clergy resize, masked sudo-for-docker, edit-mode unlock
Make the message-input bar part of the F5 resize cycle so its height is adjustable without a sandbox (multi-line/wrapped inputs are now readable), and let chat/clergy borrow height from the compose box when no terminal is present. Add Option C masked sudo prompt that feeds `sudo -S` over stdin to install/start Docker — the password never reaches chat, the PTY, or outbound frames, and Docker Desktop on Linux is detected so no sudo is requested. Fix a freeze where clicking the compose box entered layout-edit mode and silently swallowed every keystroke: clicking the input bar no longer enters edit mode, and typing any printable char now drops out of edit mode and types. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
2ae4adfde4 |
docs(layout): spec for BSP pane tree (resize panes in both dimensions)
Design for replacing the two-scalar layout (pty_pct + roster_width) with a binary space-partition pane tree so every pane is resizable on both axes, fixing the "only roster width adjusts" limitation. Rolls a small in-repo tree (no new dep) and drives the PTY grid from the terminal pane's actual Rect. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
9a038455ca |
docs: correct /send syntax, add /sendroom + /clear, fix Ctrl+Alt+P note
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
README command table claimed `/send <path>` sends to the room; actual code (app.rs) makes `/send <user> <path>` a targeted send and `/sendroom <path>` the room-wide offer. Add the missing `/sendroom` and `/clear` rows (both already in the in-app help) and drop the inaccurate "save to disk" claim on Ctrl+Alt+P — saving is `/theme save`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
556ba5f23a |
docs: drop inaccurate private-beta note — git.churchofmalware clone is public
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Verified anonymous clone from git.churchofmalware.org succeeds; only the GitHub mirror is private. Restore the plain public clone instructions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
7cba32560c |
docs: document private-beta clone (token/SSH) for git.churchofmalware.org
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
The repo is private, so anonymous clone 404s. Add token + SSH clone instructions under the quick-start so invited users can install while we promote the Gitea instance. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
f1467b00ac |
docs: point clone URL at git.churchofmalware.org; refresh /sbx command list
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
- Update the git clone URL in README and CONTRIBUTING from the old GitHub remote to the project's Gitea instance (git.churchofmalware.org/trilltechnician/hack-house.git). Verified the clone succeeds over HTTPS against that path. - CONTRIBUTING: fork step now points at the Gitea instance, not GitHub. - Refresh the /sbx command table to match the client: `/sbx launch` now documents the `install` consent token and `--start` daemon boot, and a new row covers `/sbx launch vbox [new [name]]` (VM picker / cloud-init build). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
9959e09fe2 |
docs(scripts): add --help to remaining scripts; document scripts + layout in README
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
- Add a -h/--help guard (prints the usage header) to the five scripts that lacked one: smoke.sh, smoke-e2e.sh, test-features.sh, demo-save-load.sh, sandbox-bootstrap.sh. All 15 hh/scripts/*.sh now respond to --help. - README: new "Scripts" section detailing every script (setup, hosting, sandbox provisioning, tests) and noting each takes --help; plus an archive note for the film-* recording scripts. - README: new "Window layout" section documenting live pane resizing (F4 fullscreen, click/F5 to select + arrows to resize) and the /layout save/load/list/rm/reset presets, with matching keybinding-table rows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
cab266588d |
chore(scripts): archive film demos, fold join.sh into connect.sh
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Trim newcomer-facing clutter in hh/scripts/ without changing the running product or CI. - Move the one-off demo-recording scripts (film-save-load.sh, film-virtualbox.sh) to scripts/archive/ — they depend on external personal tooling (asciinema, video-toolkit, edge-tts, xdotool) and were never wired into the README or CI. - Delete join.sh: it duplicated connect.sh's join with fewer options (hardcoded port, plaintext-only). Its only unique behaviour — a pre-join git pull (gitea→origin, ff-only) + rebuild — is now an opt-in `--sync` flag on connect.sh, leaving one canonical joiner. No scripts or docs referenced the removed/moved files, so nothing else needed updating. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
37d5916b2e |
feat(layout): fullscreen + interactive pane resizing
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Add a window-management layer so the chat, roster and sandbox-terminal panes can be resized live and fullscreened, with named presets for recall. - New layout.rs: single source of truth for the body split (pty_pct, roster_width) and a Zoom state (Normal/Term/Chat), persisted to layouts/<slug>.toml like themes. Both ui::draw and app::sbx_dims read from it, so resizing just mutates state and clears announced_dims — the per-tick loop re-syncs and broadcasts the new PTY grid. - F4 cycles terminal/chat fullscreen (F-keys aren't forwarded to the shell, so nothing is stolen from in-shell apps). - Interactive editing: click a pane (or F5 to cycle terminal -> chat -> roster) to select it; arrows then resize it live, Esc/Enter finishes. The selected pane gets a bold accent border and an ✎ title marker. ui::pane_at hit-tests against the same body_areas rects draw() paints. - /layout slimmed to presets only (save/load/list/rm/reset); the old numeric pty/chat/roster and full/chatfull/normal verbs are replaced by the interactive flow. - Help menu updated: LAYOUT cluster + KEYS line document F4, click/F5, arrows and Esc. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c9cdd2feca |
feat(sbx): install Docker/Multipass on demand from /sbx launch
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Previously, launching a sandbox on a machine without the backend binary died with a raw "not installed" error and no path forward. Now the missing backend can be installed in-line, gated on explicit consent, so a fresh checkout can go from zero to a running sandbox without leaving the TUI. Folded into the existing `/sbx launch` verb rather than a new command (menu is already dense): `/sbx launch docker|multipass [image] install`. The `install` token opts in; without it the user gets an actionable error naming the exact retry. The token is filtered out of positional image parsing so it never shadows a custom image. The install runs off-thread inside the existing spawn_launch task, before provisioning, so the TUI never blocks and the *launching guard is cleared via BrokerMsg on failure. A fresh Docker install also leaves its daemon up, so launch proceeds straight through. Scripts (detect-first, never silent, --plan dry-run, idempotent if already present): - ensure-multipass.sh (new): Linux→snap (clear failure if snapd absent), macOS→brew cask, Windows→winget. - ensure-docker.sh: new --install mode using Docker's OFFICIAL, GPG-verified apt repo (docker-ce) on Debian/Ubuntu, with dnf (Fedora/RHEL) and pacman (Arch) fallbacks. Deliberately avoids piping get.docker.com into a root shell. Existing daemon-start path intact. sbx.rs: docker_installed()/multipass_installed() detectors and ensure_docker_install()/ensure_multipass_install() wrappers; ENSURE_MULTIPASS const. ui.rs: help text documents the [install] option on both backends. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
764c827d07 |
fix(ux): forward Esc and Ctrl-X to the sandbox shell while driving
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Two interactive shell apps were unusable in the shared sandbox because the TUI swallowed their core keys: - Esc released drive mode and was never mapped in key_to_pty, so vim could never leave insert mode. Esc now forwards 0x1b to the PTY; F2 (already a drive toggle) is the release key. - Ctrl-X (owner kill switch) was intercepted globally, so nano could never quit. It's now gated on !app.driving — while you hold the shell Ctrl-X reaches the PTY (0x18, nano's quit); release with F2 to arm the kill switch. Updated the on-screen hints and /help KEYS cluster to match. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
b8a06077a4 |
fix(sbx): make /sbx save work on a live multipass sandbox
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Multipass can only snapshot a powered-off instance, so `/sbx save` on a running multipass sandbox previously just surfaced multipass's "instance must be stopped" error — making the snapshot that `/sbx load` needs impossible to create through the UI. Now multipass save powers the instance down before snapshotting, and the handler tears the shared session down first (same as `/sbx stop`, but WITHOUT purging so the instance — and thus the snapshot — survives for a later `/sbx load`). Docker is unchanged: `docker commit` captures a live container, so its save stays non-disruptive. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
6160d957f5 |
feat(sbx): create fresh VirtualBox VMs via cloud-init (/sbx launch vbox new)
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
The existing vbox paths only open pre-made images; there was no way to build one from scratch. Add `/sbx launch vbox new [name]` backed by scripts/vbox-new.sh: download an Ubuntu cloud image, convert it to a VDI, build a cloud-init NoCloud seed that creates a sudo login user and installs the sandbox-tools.json toolchain on first boot, then create + boot the VM. VirtualBox guests can't be exec'd into like docker/multipass, so cloud-init is the provisioning channel. Generates a one-time login password (or takes --pass) and authorizes a local SSH key if present. Runs off-thread; rolls back a half-built VM on failure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
d448314e5e |
feat(sbx): VM dev-toolchain bootstrap + save/load parity across backends
Reorganize the help menu into one VIRTUAL MACHINES cluster covering all backends, and bring docker/multipass/vbox to save+load parity: - Launch-time dev toolchain: sandbox-bootstrap.sh + editable sandbox-tools.json (vim/curl guaranteed), installed in docker AND multipass sandboxes at provision time. - Vbox load: vm_restore + `/sbx vmload <vm> [label]` (restore snapshot then boot the GUI). - Multipass load: `/sbx load` is now backend-aware (locate_snapshot + SnapKind), mp_restore re-attaches the shared shell; teardown stops (not purges) an instance that still has snapshots so they survive `/sbx stop`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
cb1f6134f6 |
feat(sbx): apt-get update on docker launch so installs just work
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Base ubuntu images ship without /var/lib/apt/lists, so apt-get install failed with "Unable to locate package" until the user ran update by hand. Refresh the index once during Docker provisioning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
7624cb04f7 |
feat(ux): F1 toggles help, --user flag, connect.sh fresh-build
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
- app.rs: F1 now closes the help overlay (toggle), not just opens it - host-house.sh: add --user/--name flag to override the $USER default seat - connect.sh: incremental cargo build before launch so the UI is never a stale release binary; --no-build escape hatch for toolchain-less joiners Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
d948cdb63e |
fix(scripts): host-house.sh builds from main by default
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Add a BRANCH guard (default: main) so launching the house never demos a stale checkout. ensure_branch() switches to $BRANCH before the cargo build, refuses when the tree is dirty (won't clobber uncommitted work), and no-ops when already on-branch or when BRANCH= is passed to keep the current checkout. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
4a73eb04b9 |
feat(scripts): all-in-one host-house.sh; free stale port; fail-fast join sync
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
- host-house.sh: one tmux session hosting a room (server window) plus your own GUI client window — builds, frees the port, waits for /health, then attaches. - host-room.sh: free_port() kills any stale LISTENer squatting on the port so the bind can't fail with "address already in use". - join.sh: cap each remote pull with `timeout` + GIT_TERMINAL_PROMPT=0 so an unreachable remote fails fast instead of hanging. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
98e202e0fe |
feat(sbx,ft): share VirtualBox VMs — vbox launch grammar, picker & streaming transfer
Unify sandbox summoning under `/sbx launch <docker|multipass|vbox>`, each running on the invoker's own machine. For vbox: - `/sbx launch vbox` opens an arrow-navigable VM picker (↑↓/Enter/Tab/Esc). - `/sbx launch vbox [gui] <vm>` boots frictionlessly for a host that already has VirtualBox + the VM imported; a non-host appends `yes` to install VirtualBox, import the shared appliance, and/or free VT-x first. - `/sbx gui <vm> [yes]` kept as an alias. Add `vm_registered()` + `import_appliance()` (VBoxManage import) in sbx.rs. Make file transfer stream disk-to-disk so multi-GB VM images can be shared (the old 50 MB in-memory cap blocked them): `STREAM_MAX` = 16 GiB for `/send` (`MAX_SIZE` now guards only the in-memory tar_path for sandbox injection). `prepare_send` stat+stream-hashes off the UI thread; `Sink`/`commit` write incoming chunks straight to a temp `.part` file and verify SHA before moving into place. Wire frames (offer/accept/chunk/done, 64 KB, base64) unchanged. A received `.ova`/`.ovf` auto-imports so the recipient can immediately `/sbx launch vbox gui <vm>`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
1fa8c332ed |
chore: consolidate all shell scripts into hh/scripts/
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Move the 14 hack-house scripts (bootstrap, lets-hack, host-room, smoke, demo/film harnesses, connect/join, ensure-docker/vbox) into hh/scripts/ and fix their path resolution for the deeper location. Update cross-refs: sbx.rs script consts, app.rs hints, CI, direnv .envrc, README, and docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
9f7ab92270 |
feat(scripts): add host-room.sh to host a room (server wrapper)
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
Friendly wrapper around `cmd_chat.py serve`: uses the project venv, mints a random room password passed via $CMD_CHAT_PASSWORD (kept out of the process list), prints the join command preferring the Tailscale IP, defaults to --no-tls with an opt-in --tls (cert/key) path, and runs in the foreground. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
9222fa8ad7 |
feat(ai): name agents after their model, not hardcoded "oracle"
Agents now join under their model tag (e.g. "qwen2.5:3b" — model name + parameter size) or profile label, derived at /ai start and tracked on App.agent_name, so the roster shows what's actually answering. Updates the broker re-grant + /ai stop revoke paths, the Python --name default (falls back to provider.model), and the demo/bootstrap/README/docs refs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
3a54578f0a |
perf(net): near-real-time sandbox for non-host viewers
Two causes made a shared shell's PTY stream lag for non-host viewers and then arrive "all at once": 1. Nagle's algorithm — PTY echo/keystrokes/chat are tiny frames; Nagle + delayed-ACK (amplified by Tailscale RTT) coalesced them into bursts. Set TCP_NODELAY on the client websocket (Plain/--no-tls path) and on each server connection's socket (via ws.io_proto.transport). 2. Head-of-line blocking — ConnectionManager.broadcast awaited each send() serially under a global lock, so the slowest viewer gated delivery to everyone AND stalled the server's read of the broker's next frame, backing the stream up. Give each connection its own bounded outbound queue + writer task; broadcast now only enqueues and never waits on a socket. A peer that overflows its backlog (4096) is evicted (close 1013) and resyncs on reconnect rather than buffering without bound or being fed a gapped terminal stream. Init snapshot is enqueued as the guaranteed-first frame (send_state -> state_frame). Adds tests/test_manager.py (FIFO, slow-client isolation, wedged eviction, reconnect replacement). Also fmt-clean sbx.rs/theme.rs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c552ece6b1 |
feat(theme): WCAG-legible backgrounds + oc-demo theme & reaction assets
Derive each rolled palette's surface from its ink via legible_bg so the background is as expressive as it can be while keeping chat text above a contrast floor. Adds the oc-demo theme and reaction images. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
d6d44128c0 |
feat(sbx,ai): snapshot all backends w/ local export; robust AI + rejoin
- /sbx save [--local] and new /sbx vmsave/vmsnaps: snapshot Docker, Multipass, and VirtualBox; --local also writes a portable artifact (docker .tar / VBox .ova) under hh-snapshots/ that survives pruning. - sandbox reappears for anyone who leaves and rejoins (host replays status + screen snapshot + ACL on Joined); SbxStatus ready handler is now idempotent so it never wipes scrollback. - received files auto-bridge into the hosted sandbox (ft::tar_path). - AI agent: translate Ollama's cryptic 404 into model/host/fix guidance. - bootstrap installs the AI layer (Ollama + default model) by default, with consent gates; --no-ai opts out, --yes skips prompts. - help menu lists the new save/vm flags. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
70245cbd9d |
feat(hh): random room password by default; join.sh syncs before connect
- lets-hack.sh: default PW is now `openssl rand -hex 12` (printed in the summary for sharing) instead of the well-known hardcoded `malware-bless` - join.sh: drop the hardcoded password (uses $HH_PASSWORD or a no-echo prompt), and fast-forward pull from gitea+origin then `cargo build` before connecting so joiners run the latest code Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
2c4a4f9a22 |
harden(ft,auth,net): cap transfers/frames, evict stale SRP, distrust XFF
CI / rust client (hh) (macos-latest) (push) Has been cancelled
CI / rust client (hh) (ubuntu-latest) (push) Has been cancelled
CI / rust coverage (push) Has been cancelled
CI / python server (3.10) (push) Has been cancelled
CI / python server (3.11) (push) Has been cancelled
CI / python server (3.12) (push) Has been cancelled
CI / headless e2e smoke (push) Has been cancelled
CI / dependency audit (push) Has been cancelled
CI / secret scanning (push) Has been cancelled
M1: enforce the declared transfer size (clamped to MAX_SIZE) on chunk receipt in both the Rust and Python clients — a malicious sender can no longer grow the receive buffer unboundedly. M2: only honor X-Forwarded-For when TRUST_PROXY is set, so a direct client can't spoof a source IP to dodge the per-IP rate limiter. M3: evict unverified SRP sessions after a 60s TTL on each new handshake, preventing half-finished auths from exhausting memory. M4: drop WS frames larger than 256 KB before they hit the store or broadcast, bounding per-message memory and flood blast radius. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |