← back to codex gallery

TRILLTECHNICIAN

HackHouse Project · HackHouse — Secure File & Screen Sharing for Hackers
TRILLTECHNICIAN art
# hack-house ### encrypted collaborative terminal sessions with a summoned sandbox [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Rust](https://img.shields.io/badge/client-rust-orange.svg)](https://www.rust-lang.org/) [![Python 3.10+](https://img.shields.io/badge/server-python%203.10+-blue.svg)](https://www.python.org/downloads/) ![hack-house demo](docs/hack-house-demo.gif) *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

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/scripts/ Helper scripts — setup, hosting, sandbox provisioning, tests (see Scripts; each takes -h/--help)
hh/direnv-autostart/ cd into a directory to auto-launch a session (direnv)

Quick start

git clone https://git.churchofmalware.org/trilltechnician/hack-house.git
cd hack-house

1. One-shot setup (hh/scripts/bootstrap.sh)

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

hh/scripts/bootstrap.sh              # venv + deps + debug build
hh/scripts/bootstrap.sh --release    # release build
hh/scripts/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 (hh/scripts/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:

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

2. Try it in tmux (scripts/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
./scripts/lets-hack.sh                  # alice + bob, tiled in tmux
./scripts/lets-hack.sh neo trinity      # custom users
./scripts/lets-hack.sh --theme neon     # pick vestments
./scripts/lets-hack.sh --reuse          # keep a live server (reconnect tests)
./scripts/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 <user> <path> Offer a file (or directory) directly to one member
/sendroom <path> Offer a file (or directory) to the whole room
/accept · /reject Respond to a pending file offer
/clear Wipe your chat scrollback (local only)
/ai start [model\|profile] [allow] Summon a local AI agent (default ollama/qwen2.5:3b; a bare name is a models.toml profile). allow auto-grants it sandbox drive at spawn
/ai stop Dismiss the agent you summoned
/ai <question> Ask the agent (/ai <name> <question> if several present)
/ai <name> !<task> Have a granted agent act in the shared sandbox (advisory-only if it has no drive)
/ai <name> confirm Approve a gated (destructive) command the agent proposed
/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 <local\|docker\|podman\|multipass> [image] [install] Summon the shared sandbox — the backend leads (/sbx launch … still works). Docker → Parrot OS (parrotsec/core), Podman → Kali (kalilinux/kali-rolling, rootless, no sudo). install fetches a missing backend; docker --start boots a stopped daemon
/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 vbox [new [name]] Open the local VirtualBox VM picker, or build a fresh VM via cloud-init
/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
F4 · F5 · click Layout: fullscreen terminal · select a pane to resize (then arrows · Esc) — see Window layout
/layout save \| load \| list \| rm \| reset Save / recall named pane arrangements

The shared sandbox

Anyone in the room can summon a disposable Linux box with /sbx <backend>. 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 Parrot OS Security (parrotsec/core) by default — swap parrotsec/security per-launch for the full pentest set; /sbx docker --start boots the daemon (or run hh/scripts/ensure-docker.sh)
podman container Kali rolling (kalilinux/kali-rolling) by default — rootless & daemonless, no sudo to launch (add kali-linux-headless for the toolset); hh/scripts/ensure-podman.sh installs it
multipass full VM 24.04 by default; strongest isolation, ~30 s to boot, the choice for real use

The backend leads the command — /sbx podman, /sbx docker, /sbx multipass,
/sbx local (the older /sbx launch <backend> still works). Override the image
positionally, e.g. /sbx docker parrotsec/security or /sbx podman ubuntu:24.04.
Both container engines are Debian/apt-based, so the dev-toolchain bootstrap runs
unchanged. 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.

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 <user> <path> proposes a transfer to one member; /sendroom <path>
offers it to everyone. Recipients /accept or /reject. A whole directory
works too (it's packed into a .tar 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>.

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> \
    --password <room-pw> --provider ollama --model qwen2.5:3b
# joins as its model tag ("qwen2.5:3b") unless you override with --name
# 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 (keep one you like with /theme save [name]), or load your own
TOML at launch with --theme <path> (see hh/themes/). Each theme defines its
own sigil, colours, and roster width.

Window layout

The chat, roster (clergy), sandbox-terminal, and message-input panes are
resizable and can be fullscreened — live, with no restart. Resizing the terminal
also re-syncs the shared PTY grid so everyone in the room sees the same
dimensions.

Command Effect
/layout Show the current arrangement + a reminder of the keys
/layout save <name> Save the current split/roster to hh/layouts/<name>.toml
/layout load <name> (or /layout <name>) Re-apply a saved layout
/layout list List saved presets
/layout rm <name> Delete a saved preset
/layout reset Restore the default split

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.

Scripts

Everything lives in hh/scripts/; run from the hh/ directory. Every script
supports -h / --help
for full usage.

Setup

Script What it does
bootstrap.sh One-shot first-run setup: checks prereqs, creates the Python venv, installs server deps, builds the Rust client (plus the AI layer unless --no-ai).
bootstrap-ai.sh Optional AI layer: runs the baseline, then installs Ollama and pulls a default local model for the /ai agent.

Run a session

Script What it does
lets-hack.sh Local demo/test: boots a --no-tls server and tiles one TUI client pane per user in tmux.
host-house.sh Host a real room and take a seat — builds the client, starts the server, and opens your own client, all in tmux.
host-room.sh Host the server only (no seat): mints a password, frees the port, prints the LAN/Tailscale join banner, runs in the foreground.
connect.sh Join a room with the password kept RAM-only (no-echo prompt). Flags: --sync (pull latest code before building), --tls, --insecure, --no-build, -P PORT.

Sandbox provisioning — driven by the client at runtime; you rarely run these by hand

Script What it does
ensure-docker.sh Install Docker and/or start its daemon (idempotent; --check/--plan/--yes). Invoked by /sbx docker.
ensure-podman.sh Install Podman (rootless; sets up subuid/subgid). Daemonless — nothing to start. Invoked by /sbx podman.
ensure-multipass.sh Install Multipass. Invoked by /sbx multipass.
ensure-vbox.sh Install VirtualBox (warns on Secure Boot). Invoked by /sbx vbox and /sbx gui.
vbox-new.sh Create + boot a fresh Ubuntu VirtualBox VM via cloud-init. Invoked by /sbx vbox new.
sandbox-bootstrap.sh Baseline dev-tool install piped into a Docker sandbox at provision time (package list from sandbox-tools.json).
sandbox-tools.json Editable package list consumed by sandbox-bootstrap.sh.

Tests & demos — for contributors

Script What it does
smoke-e2e.sh Headless CI smoke test: server + two real TUI clients in tmux; asserts SRP join, chat round-trip, /sbx dispatch.
smoke.sh Crypto smoke test: Rust unit tests → SRP self-test → live server → Rust handshake → Python decrypts the Rust-sent message.
test-features.sh Broad TUI regression: drives owner + member panes and scrapes the screen to assert ~13 UI features.
demo-save-load.sh PoC harness proving the persistent-sandbox flow (/sbx save → quit → /sbx load, code intact).

hh/scripts/archive/ holds personal demo-recording scripts (film-*.sh) that
depend on external video tooling — not needed to use or develop hack-house.

Configuration

Variable Where Effect
CMD_CHAT_MAX_USERS server Room capacity (default 4)
PORT · PW · HOST scripts/lets-hack.sh Override the test server's port / password / bind host
THEME scripts/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

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        │

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