The help overlay now scrolls (↑/↓, PgUp/PgDn, Home/End) with a position
indicator and only Esc dismisses it, so stray keystrokes can't close a menu
that overflows the screen. Adds three bundled vestments (blush, matrix,
wraith); they're auto-discovered via Theme::available(), so they appear in
the menu and /theme list with no hardcoded entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Make connecting any model a config step, not a code change:
- models.toml named profiles (api_key_env names an env var, never the key)
- providers gain available_models(); add preflight + --list-models/--check
- /ai list and /ai models in-room; client probes local Ollama for
/ai models when no agent is running, and /ai list hints to summon one
- docs/providers.md provider guide + examples/echo_provider.py
- README: command table, AI section, layout updated
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Owner of the spawning client can summon/dismiss a local AI agent from inside
the room (default ollama/qwen2.5:3b); the agent emits encrypted typing frames
that drive a "thinking" spinner in the client.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add cmd_chat/agent: a headless client that joins a room via SRP, decrypts
broadcasts, and answers /ai <question> through a pluggable model provider
(ollama default + anthropic + openai-compatible + module:Class). Server and
zero-knowledge guarantees unchanged; the agent is just another encrypted client.
Also pin the lets-hack demo to a detached worktree of main (default) so running
it from dev still demos stable main without touching the working checkout.
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>
- Graceful shutdown: Ctrl+C quits in chat (interrupts PTY while driving),
RAII TermGuard + panic hook + SIGTERM/SIGHUP always restore the terminal
- Default theme is now "crypt" (neutral monochrome); theme sigil mirrored in
chat/roster/help so the pentagram only renders under the "church" theme
- Neutralize inverted-pentagram branding across CLI, scripts, docs, and Cargo
metadata (kept only in themes/church.toml + the render-time placeholder)
- Rewrite root README around hack-house; add bootstrap.sh, SECURITY.md,
CODE_OF_CONDUCT.md, CHANGELOG.md, and issue/PR templates
- .gitignore cleanup; stop tracking .venv
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>
Centered modal listing every command, keybinding, and roster glyph. Opens with
F1 (desktop) or the /help command (phone-friendly, since F-keys aren't on the
Termux keyboard); any key closes it. Rendered with a Clear overlay so it floats
above the panes. Works from chat or drive mode; Ctrl-Q still quits.
9 tests pass; clean build; verified live.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Chat history: PgUp/PgDn (page), arrows (line when no sandbox), Home=oldest,
End=live. Viewport holds steady when new lines arrive while scrolled up;
sending a message jumps back to live. Backlog capped at 4000 lines.
- Sandbox terminal: vt100 parser now keeps 2000 rows of scrollback; ↑/↓ scroll
it when not driving (arrows still go to the shell while driving). Offset
applied each frame; reset on dismiss / End.
- Title indicators: 'chat ↑N (End=live)' and 'sandbox · ↑N scrollback'.
Termux's extra-keys row has arrows + PgUp/PgDn/Home/End, so it's phone-usable.
9 tests pass; clean build.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The owner's keystrokes and terminal output used to round-trip through the
server, so output lagged (appeared only on the next keypress) and Ctrl-C got
queued behind a flood of outgoing output frames (e.g. 'tree') — so it never
interrupted and the command seemed to hang.
- Owner writes drive keystrokes straight to the local PTY (instant; Ctrl-C is
never starved). Remote drivers still relay via the server.
- Owner renders its sandbox locally from the PTY and ignores its own echoed
data/status frames (broker.is_none gate); others still render from echoes.
- Coalesce PTY output bursts into one frame (no flood).
- select! is biased on keyboard input; tick 120ms -> 50ms for snappier redraws.
Verified live: echo renders with no extra key; sleep+Ctrl-C interrupts cleanly.
9 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
F2 is unreachable on phone/Termux keyboards, so add a /drive chat command to
enter sandbox drive mode (type into the shared shell; Esc releases). F2 still
works on desktop. Esc no longer quits from chat mode (footgun on mobile) — quit
is Ctrl-Q only. Updated on-screen hints + sandbox pane title.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Default vestments (was monochrome 'crypt') now match the Church of Malware
aesthetic: neon on black — cyan window-chrome borders, acid-green text/prompts
and your own messages, soft-cyan for others, purple system/occult lines,
hot-magenta self/owner. themes/church.toml ships the same palette; crypt.toml
(monochrome) and neon.toml remain as alternates via --theme.
Confirmed ratatui's serde accepts #rrggbb (hex --theme files load). 9 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Linux-style user permissions inside the sandbox (the original superuser ask):
- Backends are now persistent (docker run -d + exec; multipass instance) so the
broker can provision accounts and run the shell as a chosen user.
- sbx::provision(): create a real unix account per coven member at launch; the
OWNER becomes a passwordless superuser (sudo group + /etc/sudoers.d NOPASSWD
drop-in on multipass). The shared shell runs as the owner's account.
- /sudo <user> and /unsudo <user> (owner-only): real usermod + sudoers.d in the
VM — delegate/withdraw superuser. ACL frame carries sudoers; roster shows
⛧ owner · ⚡ sudoer · ◆ driver · • member.
Verified live on a real Multipass VM: shell runs as owner@vm with
'sudo -n whoami' == root; '/sudo member' gives member 'NOPASSWD: ALL';
teardown purges the instance. Docker provisions accounts + persistent
container (shell as root; sudo pkg absent so drive-grant is the delegation).
Tests: 7 cargo tests pass; clean build.
Co-Authored-By: Claude Opus 4.8 <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>
App-level RBAC over the single shared PTY, enforced by the broker:
- The sandbox launcher becomes owner (superuser) and first driver; broadcasts
an encrypted {"_perm":"acl",owner,drivers} frame all clients track.
- /grant <user> and /revoke <user> (owner-only) delegate/withdraw drive rights
= delegating control of the shared (root) shell — the superuser-delegation ask.
- The broker honors {"_sbx":"input"} only from permitted drivers, keyed on the
SERVER-AUTHENTICATED sender (the message username the Sanic session stamps),
not a spoofable self-asserted field — closes the spec's identity-binding gap.
- F2 is gated: non-drivers get 'ask the owner to /grant you'; revoke drops drive
live. Roster shows roles: ⛧ owner · ◆ driver · • member.
Verified live (two TUIs): member blocked pre-grant, owner /grant member, member
then drives a command in the sandbox; roster + permission messages all correct.
cargo test: 4 pass.
Note: per the single-shared-PTY decision, drive-grant *is* the permission model;
per-user unix accounts/sudo would need per-user shells (future mode).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- sbx.rs: prepare()/teardown() — multipass launch (idempotent, reuses an
existing instance) on /sbx launch multipass, delete --purge on stop;
Backend::default_image() per backend (24.04 / ubuntu:24.04).
- app.rs: async non-freezing launch — prepare runs in spawn_blocking and the
sandbox handle is handed to the run loop via a channel, so a ~30s multipass
VM boot never freezes the UI (status: "summoning…"). Sandbox is sized to the
actual pane (not fixed 24x80); broker resizes the PTY and broadcasts
{"_sbx":"resize"} on terminal-size changes; clients set their vt100 size to
match. Teardown on /sbx stop and on exit.
- net.rs: parse status rows/cols + resize frames.
Verified: cargo test (4 pass), clean build, live local sandbox via the async
path with dynamic full-width sizing. multipass 1.16.3 present; VM-boot path
implemented (live VM verify is slow, runs the same async flow).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Collaborative sandbox over the same zero-knowledge encrypted channel:
- sbx.rs: SandboxBackend (Local / Docker / Multipass) spawning a shell in a PTY
(portable-pty); reader thread pumps output to the broker.
- Broker (owner's client): /sbx launch [backend] [image] boots the sandbox and
relays PTY output as encrypted {"_sbx":"data"} frames; /sbx stop tears down.
PTY input arrives as {"_sbx":"input"} frames and is written back.
- All clients render the shared terminal from data frames via a vt100 parser;
F2 toggles drive mode (keystrokes -> input frames, incl. Ctrl-C); esc releases.
- ui.rs: sandbox pane (split below chat) with drive indicator.
- Server stays zero-knowledge: PTY bytes are Fernet-encrypted like chat/files;
the VM runs on the initiator's client, never the server.
Tests (cargo test, 4 pass): PTY I/O round-trip + headless end-to-end relay
(PTY -> _sbx frame encode -> decode -> vt100 screen shows command output).
Note: Multipass assumes the instance is launched separately (lifecycle = P3b);
per-user unix accounts + sudo delegation = P4.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pin the cross-language fernet regression test to a fixed key+token (server-
independent) instead of a session token. Confirms rust decrypts python-encrypted
fernet; the live-chat path was verified end-to-end in the TUI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Connect subcommand: SRP auth then a ratatui UI over tokio + crossterm.
- Async ws (tokio-tungstenite); reader task decrypts/parses frames into events.
- Panes: top bar (e2e + house N/cap), chat scrollback, roster (self marked ⛧),
input box. Undecryptable frames surface as a system line, not a silent drop.
- Themes (vestments) via TOML --theme; default occult-monochrome + neon.
- Verified live in tmux: render, chat round-trip, roster, join/leave.
- Adds fernet python->rust interop regression test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rebrand the Rust client crate (coven/ → hh/, package+binary "hack-house"),
README, CLI strings, and branch (coven → hack-house). Gitea repo renamed
cmd-chat → hack-house to match. Crypto/server logic unchanged; selftest +
golden-vector test still green, binary is now `hack-house`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>