encrypted terminal chat with file xfer, shared terminal acess, sandbox vm supporitn docker, multipass and virtualbox. also local ai model integration.
Go to file
leetcrypt 2c4a4f9a22
Some checks are pending
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
harden(ft,auth,net): cap transfers/frames, evict stale SRP, distrust XFF
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>
2026-06-05 06:59:16 -07:00
.github test(client),ci: fuzz frame parsers, VT-x classifier tests, smoke + CI hardening 2026-06-04 22:56:00 -07:00
cmd_chat harden(ft,auth,net): cap transfers/frames, evict stale SRP, distrust XFF 2026-06-05 06:59:16 -07:00
docs docs(sbx): VirtualBox backend spec, crypto pay-gate, save/load PoC 2026-06-03 10:10:44 -07:00
examples feat(ai): model profiles, capability discovery, and agentless /ai list|models 2026-06-01 15:25:07 -07:00
hh harden(ft,auth,net): cap transfers/frames, evict stale SRP, distrust XFF 2026-06-05 06:59:16 -07:00
lab feat: add host-chat.sh — one-command server setup with friend instructions 2026-05-25 22:40:57 -07:00
tests feat(hh): /pw command, RAM-only direnv autostart, robust lets-hack; coven→clergy 2026-05-31 22:29:17 -07:00
.gitignore feat(ui): stacking role badges in roster + chat; drop default pentagram 2026-06-04 22:56:10 -07:00
bootstrap-ai.sh feat(setup): optional bootstrap-ai.sh to install Ollama + pull a model 2026-06-01 11:38:21 -07:00
bootstrap.sh feat(setup): optional bootstrap-ai.sh to install Ollama + pull a model 2026-06-01 11:38:21 -07:00
CHANGELOG.md feat(hh): graceful shutdown, crypt default theme, neutralize branding, share-prep 2026-05-31 23:23:19 -07:00
cmd_chat.py feat: complete client-server architecture refactoring 2026-01-02 14:42:33 +03:00
CODE_OF_CONDUCT.md feat(hh): graceful shutdown, crypt default theme, neutralize branding, share-prep 2026-05-31 23:23:19 -07:00
CONTRIBUTING.md docs: point clone URLs at leetcrypt/hack-house 2026-05-31 23:50:25 -07:00
example.gif New README + Demonstartional video 2026-01-02 20:37:11 +03:00
LICENSE Code refactoring 2023-03-08 18:59:38 +03:00
models.toml feat(ai): model profiles, capability discovery, and agentless /ai list|models 2026-06-01 15:25:07 -07:00
README.MD feat(ui): stacking role badges in roster + chat; drop default pentagram 2026-06-04 22:56:10 -07:00
requirements.txt feat(coven): SRP/Fernet crypto parity + multi-user coven foundation ⛧ 2026-05-30 11:47:25 -07:00
SECURITY.md feat(hh): graceful shutdown, crypt default theme, neutralize branding, share-prep 2026-05-31 23:23:19 -07:00
tests.bat feat: add SRP authentication, improve security 2026-01-02 23:09:00 +03:00

hack-house

encrypted collaborative terminal sessions with a summoned sandbox

License: MIT Rust Python 3.10+

hack-house demo

Two clients sharing a multipass sandbox — summon, drive the shell, real per-user sudo.


Fork from https://github.com/diorwave/cmd-chat

This tool a privacy and security oriented chatroom that we added file sharing as well as shared terminal sessions.

For sharing. For learnng. For hacking. For building. For demos. For teaching. For mentorhsip for the people who dont want to trust corporations to manage their data and communications.

Encrypted chat that runs in your terminal. You host the server, you control the room. Close the window — everything's gone. Messages and files are encrypted client-side before the server ever sees them.

Features

  • End-to-end encrypted — Fernet (AES-128-CBC + HMAC), encrypted client-side before anything leaves your machine
  • SRP authentication — the password is never sent over the network (zero-knowledge proof)
  • Zero-knowledge server — relays only ciphertext; cannot read messages, files, or terminal output
  • RAM only — nothing persisted on the server; close it and history is gone
  • Shared sandbox — summon a disposable local / docker / multipass box the whole room can watch and drive
  • Snapshot save/load — freeze a sandbox to a named snapshot and restore it later (/sbx save · /sbx load · /sbx snaps)
  • Local VirtualBox VMs/sbx vms detects VirtualBox and lists your VMs; /sbx gui <vm> opens a desktop VM locally for the room to gather around — per-user consent gate, with automatic resolution of VT-x conflicts (Docker Desktop / multipass)
  • Real permissions — the host grants/revokes drive (keyboard) and sudo (VM superuser) per user; stacking roster badges show exactly who holds what, both in the clergy panel and inline on every chat message
  • Local-first AI agent/ai start summons an in-room AI that runs against your own Ollama (no API key, nothing leaves your machine); replies stream token-by-token with in-RAM semantic recall of the conversation for context; model-agnostic, addressed-only, end-to-end encrypted like every other client
  • Encrypted file transfer/send/accept with SHA-256 verification
  • TLS — self-signed by default, or bring your own cert; --no-tls for local/Tailscale use
  • Themes — seven switchable "vestments" (crypt default · church · neon · blush · matrix · wraith · goldcrypt), plus a live randomizer

Layout

Path What
hh/ The Rust ratatui client (the flagship)
cmd_chat/, cmd_chat.py The Python (Sanic) server + legacy Python client
cmd_chat/agent/ The model-agnostic AI agent bridge (joins a room as an encrypted client)
models.toml Named provider profiles for /ai start <profile> (see docs/providers.md)
docs/providers.md Connect any model — profiles, flags, discovery, bring-your-own-provider
hh/lets-hack.sh Spin up a local test "clergy" in tmux (server + N client panes)
bootstrap-ai.sh Optional: install Ollama + pull a model for the local /ai agent
hh/direnv-autostart/ cd into a directory to auto-launch a session (direnv)

Quick start

git clone https://github.com/leetcrypt/hack-house.git
cd hack-house

1. One-shot setup (bootstrap.sh)

Checks prerequisites, creates the Python venv, installs the server's dependencies, and builds the Rust client:

./bootstrap.sh              # venv + deps + debug build
./bootstrap.sh --release    # release build
./bootstrap.sh --check      # report tooling only, change nothing

bootstrap.sh does not touch direnv — the autostart in step 4 is a separate, opt-in convenience.

Optional AI layer (bootstrap-ai.sh). Want the local /ai agent? This runs the baseline setup first, then installs Ollama (if missing) and pulls a default model — nothing here changes the AI-free baseline above:

./bootstrap-ai.sh                      # baseline + Ollama + qwen2.5:3b
./bootstrap-ai.sh --check              # report only, change nothing
HH_AI_MODEL=llama3 ./bootstrap-ai.sh   # pull a different model

2. Try it in tmux (lets-hack.sh)

The fastest way to see it working: builds the client, boots a fresh --no-tls server on 127.0.0.1:4173, and opens a pane per user.

cd hh
./lets-hack.sh                  # alice + bob, tiled in tmux
./lets-hack.sh neo trinity      # custom users
./lets-hack.sh --theme neon     # pick vestments
./lets-hack.sh --reuse          # keep a live server (reconnect tests)
./lets-hack.sh --kill           # tear it all down

3. Manual setup

Server (Python):

pip install -r requirements.txt
python3 cmd_chat.py serve 0.0.0.0 3000 --password <room-password>

Client (Rust):

cd hh
cargo build --release
./target/release/hack-house connect <server_ip> 3000 <yourname> \
    --password <room-password> --insecure
Flag Purpose
--password Room password (required)
--no-tls Connect without TLS (local / trusted tunnel)
--insecure Skip TLS cert verification (self-signed certs)
--theme <path> Load a vestments TOML (see hh/themes/)

4. Autostart with direnv (optional, separate)

A convenience for daily use, independent of bootstrap.sh. Run the one-time setup once:

cd hh/direnv-autostart
./setup.sh           # installs direnv, hooks bash/zsh, `direnv allow`s this dir

After that, simply cd-ing into the directory launches a single session for the logged-in user with a freshly minted in-memory room password (reveal it in-app with /pw, share it out-of-band to invite others). The password is generated at launch and never written to disk — matching the project's RAM-only model. If a session is already live, it just points you at it.

Using it

Type to chat. Slash commands and keys:

Command / key Action
<text> Send an encrypted chat message
/help · F1 Help overlay
/pw Show this room's password (local only — never broadcast)
/theme [name] Switch vestments, or list them
/send <path> Offer a file (or directory) to the room
/accept · /reject Respond to a pending file offer
/ai start [model|profile] Summon a local AI agent (default ollama/qwen2.5:3b; a bare name is a models.toml profile)
/ai stop Dismiss the agent you summoned
/ai <question> Ask the agent (/ai <name> <question> if several present)
/ai list List the agents present (or hint to /ai start if none)
/ai models Models the active agent can serve — or, with no agent, your local Ollama tags
/sbx launch [local|docker|multipass] [image] Summon the shared sandbox
/sbx stop Tear down the sandbox you host
/sbx save [label] · /sbx load <label> · /sbx snaps Snapshot the sandbox, restore one, or list snapshots
/sbx vms Detect VirtualBox and list local VMs
/sbx gui <vm> [--install] Open a local VirtualBox desktop VM for the room (consent-gated)
/drive · F2 Take the shared shell (Esc releases)
/grant <user> · /revoke <user> Owner: delegate/withdraw drive
/sudo <user> · /unsudo <user> Owner: delegate/withdraw VM superuser
Ctrl+C · Ctrl+Q Quit gracefully
Ctrl+C (while driving) Interrupt the running command
Ctrl+R Reconnect after a drop
↑/↓ · PgUp/PgDn · mouse wheel Scroll chat / sandbox scrollback

The shared sandbox

Anyone in the room can summon a disposable Linux box with /sbx launch. The person who summons it is the owner/host: their client runs the real PTY locally and relays its output to everyone else as encrypted frames, so the server only ever sees ciphertext (same trust model as chat).

Backend Isolation Notes
local none a bash shell on the host — fast, for dev/testing only
docker container ubuntu:24.04 by default; /sbx launch docker --start boots the daemon (or run ./ensure-docker.sh)
multipass full VM 24.04 by default; strongest isolation, ~30 s to boot, the choice for real use

Tear it down with /sbx stop (purges the VM/container).

Snapshots. Freeze the current sandbox to a named checkpoint with /sbx save [label], list what you've stored with /sbx snaps, and restore one later with /sbx load <label> — handy for resuming a half-built environment or replaying a demo from a known-good state.

Local VirtualBox VMs. Separate from the relayed sandbox, /sbx vms detects a VirtualBox install and lists your VMs, and /sbx gui <vm> boots one as a full desktop VM on your own machine (--install offers to install VirtualBox first if it's missing). It's not owner-gated — the per-user confirmation gate is the permission, so everyone opens their own copy. If a hardware hypervisor (Docker Desktop, multipass) is holding VT-x, hack-house detects the conflict and offers to stop it so the VM can boot.

Driving the shell

The shared terminal is watch-by-default: everyone sees the live output, but only granted drivers can type into it.

  • /drive (or F2) takes the keyboard; Esc releases it. /drive exists so the whole flow works on mobile/SSH clients with no function keys.
  • While driving, your keystrokes go to the PTY; Ctrl+C interrupts the running command (it does not quit the app).
  • PgUp/PgDn and the mouse wheel scroll the terminal's scrollback even while driving; End jumps back to live.

Unix permission control

Permissions are enforced at two layers:

  1. App-level drive ACL — who is allowed to type into the shared shell. The owner runs /grant <user> / /revoke <user>.
  2. Real VM identities — on multipass/docker, each member is provisioned an actual unix account, with the owner as superuser. On multipass, /sudo <user> / /unsudo <user> toggle real sudo rights inside the VM, so "may type" and "may run privileged commands" are independent and enforced by the OS itself.

The roster shows each member's status with stacking badges: host (the theme's sigil, e.g. ), sudoer (), driver (◆), and member (•). They're additive — a host who summoned a sandbox and can drive reads ✝⚡◆ — and the host badge appears the moment someone is first in the room, before any sandbox exists. The same badge is rendered inline next to the author on every chat message, so a message's authority is legible right in the transcript, not only in the side panel. Because the badges read the exact ACL the sandbox enforces, they can never advertise a power the room won't honour.

Sharing files & directories

/send <path> proposes a transfer; recipients /accept or /reject. A whole directory works too (it's packed before sending). Files are chunked (64 KB), encrypted with the room key, relayed as opaque ciphertext, and SHA-256 verified on arrival before landing in ./downloads/. Max size is 50 MB.

The AI agent (local-first)

Summon an AI participant with /ai start — it joins the room as a normal encrypted client (same SRP + room key as everyone else) and answers when you address it with /ai <question>. /ai stop dismisses it (it's also cleaned up when you quit). Pick a model at summon time with /ai start <model>.

  • Runs on your machine. The default provider is Ollama — a local model (default qwen2.5:3b), no API key, nothing leaves your host. Run ./bootstrap-ai.sh once to install it and pull the model.
  • Addressed-only. The agent reads room traffic like any client but forwards to the model only the messages that trigger it (/ai …) — no passive surveillance, no cost or noise when idle.
  • Model-agnostic. Swap the backend without touching the client: bundled adapters for ollama (default), anthropic, and any OpenAI-compatible endpoint (OpenAI, Groq, Together, local vLLM…), plus a module:Class hook for your own. Cloud providers are opt-in and read their API key from the agent's environment — never the room.
  • Named profiles. Register a backend once in models.toml and summon it by name: /ai start groq-llama. Profiles store api_key_env (the name of an env var, never the key), so the file is safe to commit. See the full provider guide — profiles, explicit flags, discovery, and bring-your-own-provider.
  • Discoverable. /ai list shows who's present and /ai models shows what the active agent can serve (active model bracketed). With no agent running, /ai models still probes your local Ollama so you can see what's pullable before summoning. By hand, --list-models enumerates a backend and --check preflights it (exit 0/1) without joining a room.
  • End-to-end like everything else. Replies are encrypted client-side; the server still only ever relays ciphertext.

Each agent uses one room seat — raise CMD_CHAT_MAX_USERS if the room is full. To run an agent by hand (a cloud provider, or on another host), drive the bridge directly:

.venv/bin/python -m cmd_chat.agent <server_ip> <port> \
    --name oracle --password <room-pw> --provider ollama --model qwen2.5:3b
# cloud (opt-in): --provider anthropic --model claude-opus-4-6   (needs ANTHROPIC_API_KEY)

Themes (vestments)

Seven bundled themes — crypt (default, neutral monochrome, sigil), church, neon, blush, matrix, wraith, and goldcrypt. Switch live with /theme <name>, list them with bare /theme, roll a fresh randomized vestment with Ctrl+Alt+P (and save it to disk), or load your own TOML at launch with --theme <path> (see hh/themes/). Each theme defines its own sigil, colours, and roster width.

Staying connected

If the connection drops (network blip, laptop sleep), press Ctrl+R to re-run the SRP handshake and re-attach — no restart needed. If you were hosting the sandbox, it's re-announced so the room re-syncs the shared shell. Chat keeps up to ~4000 lines of scrollback; the sandbox terminal keeps 2000.

Configuration

Variable Where Effect
CMD_CHAT_MAX_USERS server Room capacity (default 4)
PORT · PW · HOST lets-hack.sh Override the test server's port / password / bind host
THEME lets-hack.sh Vestments for every pane (church · neon · crypt)
HH_SESSION · HH_USER direnv autostart tmux session name / your in-session name

Securing your connection

  • Tailscale (recommended) — both parties join a tailnet; traffic rides an encrypted WireGuard tunnel, no port forwarding. Connect with --no-tls over the trusted tunnel, or keep TLS on.
  • LAN — use your local IP; both devices on the same network.
  • Public internet — forward the port and use a real cert (--cert/--key on the server).

Share the room password out-of-band (in person, a disappearing Signal message, or a one-time-secret link) — never over an unencrypted channel.

How it works

CLIENT                              SERVER                         CLIENT
  │── POST /srp/init {A} ──────────►│                               │
  │◄── {B, salt, room_salt} ────────│                               │
  │  derive room_key = HKDF(password, room_salt)                    │
  │── POST /srp/verify {M} ────────►│                               │
  │◄── {H_AMK, ws_token} ───────────│                               │
  │══ WSS /ws/chat?ws_token ═══════►│◄══════════════════════════════│
  │  encrypt(msg, room_key) ───────►│──── ciphertext ──────────────►│
  │                                  │        decrypt(ct, room_key)  │
  │  server stores ONLY ciphertext — it cannot read messages        │
  • SRP — both sides prove they know the password without transmitting it.
  • Room key — each client derives HKDF(password, room_salt) independently; the server never holds it.
  • Sandbox — the host runs a PTY locally and relays its output as encrypted _sbx frames; drivers' keystrokes flow back the same way. Permissions are enforced both at the app layer (drive ACL) and in the VM (real unix users / sudo).

Crypto parity

cd hh
cargo run -- selftest                              # offline: Rust SRP ≡ Python golden vectors
cargo run -- handshake <ip> <port> <name> --password <pw> --no-tls

Contributing

See CONTRIBUTING.md. Security reports: see SECURITY.md.

License

MIT · hack the planet