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>
- /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>
Replace the stale Django CI template with a CI workflow that builds and
tests both codebases: cargo fmt/clippy/build/test for the hh client and
pytest across Python 3.10-3.12 for the server. Apply cargo fmt and fix
all clippy lints so the gates pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- add /pw (alias /password): reveal this room's password locally (never
broadcast); surfaced in the F1 help overlay and the join hint
- direnv-autostart/: cd-to-launch a single real-user session via direnv;
password is minted in memory at launch (never written to disk, matching the
RAM-only model) and scoped to the child process. setup.sh installs direnv,
hooks bash/zsh, and `direnv allow`s the dir
- lets-hack.sh: boot a FRESH server by default (replacing any live one) with a
--reuse opt-out; add -h/--help/-help; guard against killing the tmux session
you're attached to; switch-client into the coven when run inside tmux
- rename coven→clergy across rust/python/scripts; tests/test_coven.py→test_clergy.py
- snapshots in-progress hack-house client work (sandbox, themes, net, ui)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire-compatible with the Python client's _ft protocol (offer/accept/reject/
chunk/done, 64KB chunks, SHA-256 verified), over the encrypted channel:
- ft.rs: read_payload (file or tar'd directory), save/extract with a zip-slip
guard (relative-only, no .. / absolute; unpack_in double-checks), SHA-256.
- /send <file> and /sendd <dir>; receiver sees an offer banner, /accept or
/reject; chunks stream in the background and the result is verified + saved
to ./downloads (directories extracted in place).
- Refactor: all outgoing ws frames now funnel through an mpsc channel drained
(batched) by the run loop, so the background chunk-sender and the PTY relay
transmit without owning the socket.
- ui.rs: pending-offer banner on the input bar.
Tests: 7 cargo tests (file + dir tar round-trip, traversal guard, + crypto/sbx).
Verified live: two TUIs, file and directory transfer, SHA-256 verified, saved.
Note: dropping accepted files into the active sandbox VM dir is a future add-on.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>