Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ba04316c1 | |||
| 012b7c15e5 | |||
| 3a90ee985a | |||
| e97a45b78d | |||
| f4572c985e | |||
| 2cd61ee598 | |||
| 4a17f1c6f1 | |||
| 7bd972f39c | |||
| 1c36325d07 | |||
| 35da940cc8 | |||
| 2d6258cab0 | |||
| 2cfc7112fc | |||
| 3fddc17454 | |||
| 3d085eaec4 | |||
| 4bd38d9c30 | |||
| 750692b6aa | |||
| ffb8ef52f0 | |||
| a7b4840e9e | |||
| 88e5dfa0cb | |||
| 7afc38232b | |||
| 2cfa388879 | |||
| 987985b203 | |||
| 9c1ec55e0b | |||
| f993a41cd7 | |||
| c41d85f8ec | |||
| dc8b68315e | |||
| e5fb820e63 | |||
| 354205cc16 | |||
| 0798db7941 | |||
| 2091bcefed | |||
| 09cea40494 | |||
| 300e33980a | |||
| 7fbae3ca84 | |||
| 12ae553ca1 | |||
| b998d28ebf | |||
| 19d5b80a2c | |||
| 338e843e85 | |||
| 60215bbdd4 | |||
| 669266dc55 | |||
| 9c1e2921e9 | |||
| c83abbee8a | |||
| dc6317fc46 | |||
| 7e81a059b9 | |||
| 46e5620f89 | |||
| d1eb4b0bbb | |||
| 14469a2649 | |||
| dba43e47a2 | |||
| d211a75616 | |||
| c61413c648 | |||
| e7746e49cf | |||
| 99afff41c6 | |||
| 92c0a07d56 | |||
| 58d405c518 | |||
| df45303f5c | |||
| 156e9fe176 | |||
| d5f423024e | |||
| a0aa14d7fd | |||
| c09b428718 | |||
| 70d6e26b24 | |||
| 0c04ac74ee | |||
| 06a87e8e76 | |||
| 0b1d09f0b5 | |||
| 946df65b72 | |||
| e49dbca451 | |||
| cf6b0b5b73 | |||
| f93c8c5e4f | |||
| 15aa2027c4 | |||
| 18a8936caa | |||
| c6eeb9b897 | |||
| ad13ec14ba | |||
| 20091a9c26 | |||
| eec1714735 | |||
| 2ae4adfde4 | |||
| 9a038455ca | |||
| 556ba5f23a | |||
| 7cba32560c | |||
| f1467b00ac | |||
| 9959e09fe2 | |||
| cab266588d | |||
| 37d5916b2e | |||
| c9cdd2feca | |||
| 764c827d07 | |||
| b8a06077a4 | |||
| 6160d957f5 | |||
| d448314e5e |
@@ -21,6 +21,13 @@ secured_console_chat.egg-info/
|
|||||||
*.log
|
*.log
|
||||||
err.log
|
err.log
|
||||||
|
|
||||||
|
# Sandbox save/load snapshots (large runtime tarballs, not source)
|
||||||
|
/hh/hh-snapshots/
|
||||||
|
# VM-library /loop artifacts — exported snapshot tars (repo-root, when `sbx`
|
||||||
|
# is run from here) + per-run logs. Generated; the VMs live in the host registry.
|
||||||
|
/hh-snapshots/
|
||||||
|
/.loop-runs/
|
||||||
|
|
||||||
# Out-of-tree experiments (not part of hack-house)
|
# Out-of-tree experiments (not part of hack-house)
|
||||||
/experiments/
|
/experiments/
|
||||||
/headroom/
|
/headroom/
|
||||||
@@ -28,6 +35,13 @@ err.log
|
|||||||
# Heavy / superseded demo-build kit (real demos live in video-toolkit)
|
# Heavy / superseded demo-build kit (real demos live in video-toolkit)
|
||||||
/docs/demo/
|
/docs/demo/
|
||||||
|
|
||||||
|
# Native-harness benchmark harness + results — dev-internal test tooling, kept
|
||||||
|
# on disk but never pushed to origin (coupled to the local tmux+podman test rig)
|
||||||
|
/bench/
|
||||||
|
|
||||||
|
# Local-only planning/design docs (kept on disk, never pushed to origin)
|
||||||
|
/docs/plans/
|
||||||
|
|
||||||
# Project scratch
|
# Project scratch
|
||||||
/i-try/
|
/i-try/
|
||||||
test_rsa.py
|
test_rsa.py
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
# hack-house — Claude operating guide
|
||||||
|
|
||||||
|
Encrypted collaborative terminal sessions with a summoned, shareable sandbox.
|
||||||
|
**Rust TUI client** (`hh/`) + **Python server & operator bridge** (`cmd_chat/`).
|
||||||
|
|
||||||
|
A Claude Code session can join a room as a **first-class member**: read/answer
|
||||||
|
humans and other AIs, and — once granted — drive the shared sandbox with full
|
||||||
|
shell + keystroke control. VMs carry a self-describing `.hh-agent` manifest so
|
||||||
|
work + memory hand off between agents (and humans), and saved VM states are
|
||||||
|
tradeable via a host-global registry.
|
||||||
|
|
||||||
|
## Operating a room — use the `hh-operator` skill
|
||||||
|
|
||||||
|
To enter/run/watch a room, bridge a Claude session into one, or autonomously
|
||||||
|
monitor and respond, **use the `hh-operator` skill** (`~/.claude/skills/hh-operator/`).
|
||||||
|
It documents the full doctrine: the daemon + `hh-bridge` CLI, the read→think→act
|
||||||
|
loop, sandbox drive (exec/write/get), the keystroke relay + stop-vocabulary, the
|
||||||
|
`watch` stop-condition engine, and the `.hh-agent` manifest for handoffs.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run from this repo root; pin the interpreter to the venv.
|
||||||
|
HH=".venv/bin/python -m cmd_chat.operator" # = the hh-bridge CLI
|
||||||
|
$HH up <host> <port> <name> --password <pw> --no-tls # join (spawns the daemon)
|
||||||
|
$HH read --wait --timeout 30 # long-poll for activity
|
||||||
|
$HH say "…" # chat into the room
|
||||||
|
$HH keys "make build" enter ; $HH watch --for 'PASS|FAIL' --in screen # drive
|
||||||
|
$HH manifest push --root /root --name <vm> --purpose … --objective … # handoff
|
||||||
|
$HH spawn "<objective incl. the full join cmd>" --room-host … --go --skip-permissions
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mobile operator on Android / Termux — the phone is REACHABLE, use it
|
||||||
|
|
||||||
|
Spec: `docs/termux-operator.md`. The operator side is pure Python and runs on
|
||||||
|
the **Fairphone-6** in Termux — it can both **join** and **host** rooms (the SRP
|
||||||
|
shim now covers the server side too, so no Rust / no `srp` C-ext). You can drive
|
||||||
|
the device directly over SSH — so **verify reachability before ever telling the
|
||||||
|
user "this can only run on-device, paste it back."** That framing is wrong when
|
||||||
|
the phone answers SSH.
|
||||||
|
|
||||||
|
One canonical, hardened invocation (use this prefix everywhere — it sidesteps
|
||||||
|
the wrong-alias trap and never hangs):
|
||||||
|
```bash
|
||||||
|
PHONE='ssh -i ~/.ssh/phone-deploy -p 8022 -o ConnectTimeout=8 -o BatchMode=yes -o StrictHostKeyChecking=accept-new u0_a203@100.95.202.68'
|
||||||
|
$PHONE 'uname -m; python --version' # aarch64 / Python 3.13.x
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Reachability gate — probe the PORT, not `tailscale status`:**
|
||||||
|
`timeout 8 bash -c 'cat </dev/null >/dev/tcp/100.95.202.68/8022' && echo REACHABLE || echo OFFLINE`.
|
||||||
|
Tailscale reports `fairphone-6` **`active` even when sshd is dead** — Android
|
||||||
|
kills backgrounded Termux, taking `sshd` with it. The tailnet line is NOT
|
||||||
|
proof; the TCP probe is. If OFFLINE, the fix is **on-device** (wake the phone,
|
||||||
|
relaunch Termux, `termux-wake-lock` + start `sshd`) — say so, don't retry a
|
||||||
|
dead port.
|
||||||
|
- **Trap:** the `~/.ssh/config` aliases `phone` / `phone-deploy` point at
|
||||||
|
`100.95.180.14` (`trilluminati`), a *different, usually-offline* phone —
|
||||||
|
`ssh phone` will time out. Use the Fairphone IP `100.95.202.68` (the `$PHONE`
|
||||||
|
prefix), not the alias.
|
||||||
|
- Termux facts: Python 3.13 (≥3.11); **no `/tmp`** — `$TMPDIR` is
|
||||||
|
`$HOME/.claude-temp`; `$PREFIX=/data/data/com.termux/files/usr`; `rustc`/
|
||||||
|
`openssl` not installed by default.
|
||||||
|
- Deps that work: `pkg install python-cryptography` (avoid a rust build),
|
||||||
|
`pip install requests rich websockets`; **skip `srp`** — both client and
|
||||||
|
server fall back to `cmd_chat/client/_srp_pure` (pure-Python SRP-6a shim).
|
||||||
|
- Push-restricted work: copy this tree to the phone with tar-over-ssh rather
|
||||||
|
than a stale gitea clone —
|
||||||
|
`tar czf - cmd_chat scripts requirements-operator.txt | $PHONE 'tar xzf - -C ~/hh-mobile'`.
|
||||||
|
- Phase-0 gate: `python scripts/termux-preflight.py` on the phone → expect
|
||||||
|
`RESULT: PASS`. Operator files staged at `~/hh-mobile` on-device.
|
||||||
|
- **Proven live (2026-07-07):** phone JOINS a dev-box room; the phone HOSTS a
|
||||||
|
room the laptop joins (cross-impl SRP: laptop C-ext ↔ phone pure Verifier);
|
||||||
|
and the stdlib mobile web console (`python -m cmd_chat.operator web`) mirrors a
|
||||||
|
real room (terminal + keystroke drive + chat) in a mobile browser.
|
||||||
|
|
||||||
|
## Key components
|
||||||
|
|
||||||
|
| What | Where |
|
||||||
|
|------|-------|
|
||||||
|
| Operator bridge (daemon + CLI) | `cmd_chat/operator/__main__.py`, `bridge.py` |
|
||||||
|
| Agent manifest (`hh-agent/v1`) | `cmd_chat/operator/manifest.py` — `.hh-agent/` bundle library + CLI |
|
||||||
|
| Recursive spawn (Claude-spawns-Claude) | `cmd_chat/operator/bootstrap.py` (budgeted: `--depth`/`--fanout`) |
|
||||||
|
| Saved-VM registry / discovery | `hh/src/registry.rs` → `~/.hh/registry.json`; reader: `$HH registry list\|show` |
|
||||||
|
| Sandbox grammar (TUI) | `/sbx podman\|launch`, `/grant <name>`, `/sbx save`, `/sbx browse`, `/sbx publish`, `/sbx catalog @user`, `/sbx pull @user <label>` |
|
||||||
|
| Server | `cmd_chat.py serve <host> <port> --password <pw> [--no-tls]` |
|
||||||
|
| Rust client | `hh/target/debug/hack-house connect <host> <port> <name> --password <pw>` |
|
||||||
|
|
||||||
|
## Demo videos — use the `video-toolkit`
|
||||||
|
|
||||||
|
hack-house feature reels are produced with `~/coding/video-toolkit` (headless
|
||||||
|
asciinema-in-tmux capture via `bin/tmux-demo.py`, then a `video-forge` cut).
|
||||||
|
|
||||||
|
- **Flagship reel — three Claudes, one shared VM:** `scenarios/hh-vmhub.json`
|
||||||
|
(capture) + `scenarios/hh-vmhub-cut.json` (forge cut). A planner, builder, and
|
||||||
|
tester (each a real nested `claude -p`) collaborate to build **VMHub** — a
|
||||||
|
GitHub-for-VMs web app — live inside one shared sandbox, then save it to the VM
|
||||||
|
library. Role task-scripts: `scenarios/vmhub-{builder,tester}-task.sh`; staged
|
||||||
|
app under `scenarios/assets/vmhub/`.
|
||||||
|
- A reel must **show the product**: the Rust TUI room (roster, encrypted chat, the
|
||||||
|
shared sandbox F2 view) with Claude visibly operating as a member and
|
||||||
|
Claude-spawning-Claude — not just a console of CLI calls.
|
||||||
|
|
||||||
|
> Provisioning note: the demo summons a pre-baked `localhost/hh-dev:slim` (python
|
||||||
|
> 3.11) podman sandbox so VMHub (stdlib, zero pip deps) runs with no installs.
|
||||||
@@ -20,8 +20,8 @@ We welcome contributions of all types, including:
|
|||||||
|
|
||||||
## 📦 Getting Started
|
## 📦 Getting Started
|
||||||
|
|
||||||
1. **Fork the repository** on GitHub.
|
1. **Fork the repository** on the Gitea instance (`git.churchofmalware.org`).
|
||||||
2. **Clone your fork** locally:
|
2. **Clone your fork** locally:
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/leetcrypt/hack-house.git
|
git clone https://git.churchofmalware.org/trilltechnician/hack-house.git
|
||||||
cd hack-house
|
cd hack-house
|
||||||
|
|||||||
@@ -32,11 +32,12 @@ Encrypted chat that runs in your terminal. You host the server, you control the
|
|||||||
- **SRP authentication** — the password is never sent over the network (zero-knowledge proof)
|
- **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
|
- **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
|
- **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
|
- **Shared sandbox** — summon a disposable `local` / `docker` / `podman` / `multipass` box the whole room can watch and drive. Docker defaults to **Parrot OS Security** (`parrotsec/core`) and Podman to **Kali** (`kalilinux/kali-rolling`) — pentest distros out of the box; Podman is rootless & daemonless, so **no sudo** to launch
|
||||||
- **Snapshot save/load** — freeze a sandbox to a named snapshot and restore it later (`/sbx save` · `/sbx load` · `/sbx snaps`)
|
- **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)
|
- **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
|
- **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](https://ollama.com) (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
|
- **Local-first AI agent** — `/ai start` summons an in-room AI that runs against *your own* [Ollama](https://ollama.com) (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
|
||||||
|
- **AI that acts in the sandbox** — grant an agent *drive* and address it with `/ai <name> !<task>`; it works the shared box through a bounded, host-side **tool-calling loop** (run shell, write/read files, inspecting each result before the next step) and you watch its commands land live in the shared terminal. Ungranted, it stays **advisory-only** (tells you the commands, runs nothing); destructive commands are gated behind an explicit `/ai <name> confirm`
|
||||||
- **Encrypted file transfer** — `/send` → `/accept` with SHA-256 verification
|
- **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
|
- **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
|
- **Themes** — seven switchable "vestments" (`crypt` default · `church` · `neon` · `blush` · `matrix` · `wraith` · `goldcrypt`), plus a live randomizer
|
||||||
@@ -50,14 +51,13 @@ Encrypted chat that runs in your terminal. You host the server, you control the
|
|||||||
| `cmd_chat/agent/` | The model-agnostic AI agent bridge (joins a room as an encrypted 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`) |
|
| `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 |
|
| `docs/providers.md` | Connect any model — profiles, flags, discovery, bring-your-own-provider |
|
||||||
| `hh/scripts/lets-hack.sh` | Spin up a local test "clergy" in tmux (server + N client panes) |
|
| `hh/scripts/` | Helper scripts — setup, hosting, sandbox provisioning, tests (see [Scripts](#scripts); each takes `-h`/`--help`) |
|
||||||
| `hh/scripts/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) |
|
| `hh/direnv-autostart/` | `cd` into a directory to auto-launch a session (direnv) |
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/leetcrypt/hack-house.git
|
git clone https://git.churchofmalware.org/trilltechnician/hack-house.git
|
||||||
cd hack-house
|
cd hack-house
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -150,17 +150,22 @@ Type to chat. Slash commands and keys:
|
|||||||
| `/help` · `F1` | Help overlay |
|
| `/help` · `F1` | Help overlay |
|
||||||
| `/pw` | Show this room's password (local only — never broadcast) |
|
| `/pw` | Show this room's password (local only — never broadcast) |
|
||||||
| `/theme [name]` | Switch vestments, or list them |
|
| `/theme [name]` | Switch vestments, or list them |
|
||||||
| `/send <path>` | Offer a file (or directory) to the room |
|
| `/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 |
|
| `/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) |
|
| `/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 stop` | Dismiss the agent you summoned |
|
||||||
| `/ai <question>` | Ask the agent (`/ai <name> <question>` if several present) |
|
| `/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 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 |
|
| `/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 <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 stop` | Tear down the sandbox you host |
|
||||||
| `/sbx save [label]` · `/sbx load <label>` · `/sbx snaps` | Snapshot the sandbox, restore one, or list snapshots |
|
| `/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 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) |
|
| `/sbx gui <vm> [--install]` | Open a local VirtualBox desktop VM for the room (consent-gated) |
|
||||||
| `/drive` · `F2` | Take the shared shell (`Esc` releases) |
|
| `/drive` · `F2` | Take the shared shell (`Esc` releases) |
|
||||||
| `/grant <user>` · `/revoke <user>` | Owner: delegate/withdraw drive |
|
| `/grant <user>` · `/revoke <user>` | Owner: delegate/withdraw drive |
|
||||||
@@ -169,10 +174,12 @@ Type to chat. Slash commands and keys:
|
|||||||
| `Ctrl+C` (while driving) | Interrupt the running command |
|
| `Ctrl+C` (while driving) | Interrupt the running command |
|
||||||
| `Ctrl+R` | Reconnect after a drop |
|
| `Ctrl+R` | Reconnect after a drop |
|
||||||
| `↑/↓` · `PgUp/PgDn` · mouse wheel | Scroll chat / sandbox scrollback |
|
| `↑/↓` · `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](#window-layout) |
|
||||||
|
| `/layout save \| load \| list \| rm \| reset` | Save / recall named pane arrangements |
|
||||||
|
|
||||||
### The shared sandbox
|
### The shared sandbox
|
||||||
|
|
||||||
Anyone in the room can summon a disposable Linux box with `/sbx launch`. The
|
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
|
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
|
locally and relays its output to everyone else as encrypted frames, so the
|
||||||
server only ever sees ciphertext (same trust model as chat).
|
server only ever sees ciphertext (same trust model as chat).
|
||||||
@@ -180,10 +187,15 @@ server only ever sees ciphertext (same trust model as chat).
|
|||||||
| Backend | Isolation | Notes |
|
| Backend | Isolation | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `local` | none | a `bash` shell on the host — fast, for dev/testing only |
|
| `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 `hh/scripts/ensure-docker.sh`) |
|
| `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 |
|
| `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).
|
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
|
**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
|
[label]`, list what you've stored with `/sbx snaps`, and restore one later with
|
||||||
@@ -233,8 +245,9 @@ they can never advertise a power the room won't honour.
|
|||||||
|
|
||||||
### Sharing files & directories
|
### Sharing files & directories
|
||||||
|
|
||||||
`/send <path>` proposes a transfer; recipients `/accept` or `/reject`. A whole
|
`/send <user> <path>` proposes a transfer to one member; `/sendroom <path>`
|
||||||
directory works too (it's packed before sending). Files are chunked (64 KB),
|
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
|
encrypted with the room key, relayed as opaque ciphertext, and **SHA-256
|
||||||
verified** on arrival before landing in `./downloads/`. Max size is 50 MB.
|
verified** on arrival before landing in `./downloads/`. Max size is 50 MB.
|
||||||
|
|
||||||
@@ -251,6 +264,15 @@ when you quit). Pick a model at summon time with `/ai start <model>`.
|
|||||||
- **Addressed-only.** The agent reads room traffic like any client but forwards
|
- **Addressed-only.** The agent reads room traffic like any client but forwards
|
||||||
to the model *only* the messages that trigger it (`/ai …`) — no passive
|
to the model *only* the messages that trigger it (`/ai …`) — no passive
|
||||||
surveillance, no cost or noise when idle.
|
surveillance, no cost or noise when idle.
|
||||||
|
- **Can drive the sandbox.** Grant an agent *drive* (`/grant <name>`, or summon it
|
||||||
|
pre-granted with `/ai start <name> allow`) and ask it to act with
|
||||||
|
`/ai <name> !<task>`. It works the shared box through a bounded **host-side
|
||||||
|
tool-calling loop** — run shell commands, write and read files — inspecting each
|
||||||
|
result before the next step, and you watch its commands appear live in the
|
||||||
|
shared terminal. Every command runs *inside the sandbox* (the container/VM is the
|
||||||
|
blast radius), capped in count and time. Without drive it stays **advisory-only**
|
||||||
|
(it spells out the commands, runs nothing). Destructive commands are blocked
|
||||||
|
pending an explicit `/ai <name> confirm`.
|
||||||
- **Model-agnostic.** Swap the backend without touching the client: bundled
|
- **Model-agnostic.** Swap the backend without touching the client: bundled
|
||||||
adapters for `ollama` (default), `anthropic`, and any OpenAI-compatible
|
adapters for `ollama` (default), `anthropic`, and any OpenAI-compatible
|
||||||
endpoint (OpenAI, Groq, Together, local vLLM…), plus a `module:Class` hook for
|
endpoint (OpenAI, Groq, Together, local vLLM…), plus a `module:Class` hook for
|
||||||
@@ -285,9 +307,43 @@ directly:
|
|||||||
Seven bundled themes — `crypt` (default, neutral monochrome, `✝` sigil),
|
Seven bundled themes — `crypt` (default, neutral monochrome, `✝` sigil),
|
||||||
`church`, `neon`, `blush`, `matrix`, `wraith`, and `goldcrypt`. Switch live with
|
`church`, `neon`, `blush`, `matrix`, `wraith`, and `goldcrypt`. Switch live with
|
||||||
`/theme <name>`, list them with bare `/theme`, roll a fresh randomized vestment
|
`/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
|
with `Ctrl+Alt+P` (keep one you like with `/theme save [name]`), or load your own
|
||||||
`--theme <path>` (see `hh/themes/`). Each theme defines its own sigil, colours,
|
TOML at launch with `--theme <path>` (see `hh/themes/`). Each theme defines its
|
||||||
and roster width.
|
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.
|
||||||
|
|
||||||
|
- **Fullscreen** — `F4` cycles the sandbox terminal fullscreen → chat
|
||||||
|
fullscreen → back to the split. The input bar always stays visible, so
|
||||||
|
`F4` always brings you back.
|
||||||
|
- **Resize a pane** — click a pane (or press `F5` to cycle the selection:
|
||||||
|
chat → terminal → roster → input). The selected pane gets a bold accent border
|
||||||
|
and an `✎` marker in its title. The chat/terminal/roster panes form a binary
|
||||||
|
split tree, so **every pane resizes on both axes** wherever a divider bounds it:
|
||||||
|
- `↑` / `↓` grow / shrink the selected pane's **height**. With a sandbox up,
|
||||||
|
chat trades against the terminal directly below it. With no sandbox, chat and
|
||||||
|
the clergy instead **borrow from the message bar** — `↑` grows the chat box
|
||||||
|
(shrinks the input), `↓` does the reverse — so vertical resize always moves
|
||||||
|
something. The **input bar** itself is also selectable and grows on `↑/↓`.
|
||||||
|
- `←` / `→` grow / shrink the selected pane's **width** (the chat/terminal
|
||||||
|
column trades with the roster, down to hidden). Resizing the terminal's
|
||||||
|
width now re-syncs the shared PTY too, not just its height.
|
||||||
|
- `Esc` or `Enter` finishes editing.
|
||||||
|
- **Presets** — save the current arrangement and recall it later:
|
||||||
|
|
||||||
|
| 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
|
### Staying connected
|
||||||
|
|
||||||
@@ -296,6 +352,51 @@ 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
|
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.
|
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
|
## Configuration
|
||||||
|
|
||||||
| Variable | Where | Effect |
|
| Variable | Where | Effect |
|
||||||
|
|||||||
|
After Width: | Height: | Size: 682 KiB |
|
After Width: | Height: | Size: 752 KiB |
|
After Width: | Height: | Size: 273 KiB |
|
After Width: | Height: | Size: 271 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 946 KiB |
|
After Width: | Height: | Size: 711 KiB |
|
After Width: | Height: | Size: 344 KiB |
|
After Width: | Height: | Size: 1011 KiB |
|
After Width: | Height: | Size: 641 KiB |
|
After Width: | Height: | Size: 601 KiB |
|
After Width: | Height: | Size: 423 KiB |
|
After Width: | Height: | Size: 582 KiB |
|
After Width: | Height: | Size: 560 KiB |
|
After Width: | Height: | Size: 852 KiB |
|
After Width: | Height: | Size: 760 KiB |
|
After Width: | Height: | Size: 316 KiB |
|
After Width: | Height: | Size: 1001 KiB |
|
After Width: | Height: | Size: 699 KiB |
|
After Width: | Height: | Size: 826 KiB |
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 363 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 680 KiB |
|
After Width: | Height: | Size: 740 KiB |
|
After Width: | Height: | Size: 857 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 197 KiB |
|
After Width: | Height: | Size: 763 KiB |
|
After Width: | Height: | Size: 602 KiB |
|
After Width: | Height: | Size: 269 KiB |
|
After Width: | Height: | Size: 259 KiB |
|
After Width: | Height: | Size: 307 KiB |
|
After Width: | Height: | Size: 304 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 197 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 192 KiB |
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 413 KiB |
|
After Width: | Height: | Size: 386 KiB |
|
After Width: | Height: | Size: 382 KiB |
|
After Width: | Height: | Size: 419 KiB |
|
After Width: | Height: | Size: 783 KiB |
|
After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 296 KiB |
|
After Width: | Height: | Size: 694 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 610 KiB |
|
After Width: | Height: | Size: 368 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 194 KiB |
|
After Width: | Height: | Size: 332 KiB |
|
After Width: | Height: | Size: 701 KiB |
|
After Width: | Height: | Size: 559 KiB |
|
After Width: | Height: | Size: 579 KiB |
|
After Width: | Height: | Size: 671 KiB |
|
After Width: | Height: | Size: 624 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 820 KiB |
|
After Width: | Height: | Size: 194 KiB |
|
After Width: | Height: | Size: 291 KiB |
|
After Width: | Height: | Size: 875 KiB |
|
After Width: | Height: | Size: 607 KiB |
|
After Width: | Height: | Size: 731 KiB |
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 240 KiB |
|
After Width: | Height: | Size: 312 KiB |
|
After Width: | Height: | Size: 314 KiB |
|
After Width: | Height: | Size: 339 KiB |
|
After Width: | Height: | Size: 851 KiB |
|
After Width: | Height: | Size: 765 KiB |
|
After Width: | Height: | Size: 766 KiB |
|
After Width: | Height: | Size: 817 KiB |
|
After Width: | Height: | Size: 681 KiB |
|
After Width: | Height: | Size: 916 KiB |
|
After Width: | Height: | Size: 802 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 242 KiB |
|
After Width: | Height: | Size: 629 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 271 KiB |
|
After Width: | Height: | Size: 334 KiB |
|
After Width: | Height: | Size: 724 KiB |
|
After Width: | Height: | Size: 586 KiB |
|
After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 805 KiB |
|
After Width: | Height: | Size: 776 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 414 KiB |
|
After Width: | Height: | Size: 554 KiB |