3.6 KiB
unibus playground
An all-in-one, web-based sandbox for the unibus message bus. One command brings up the entire stack embedded — no NATS to install, no services to wire — and a browser UI lets you exercise the bus visually: create peers, create and join rooms (cleartext or end-to-end encrypted), invite, publish, watch messages arrive live, and kick members (forward secrecy).
This is a playground (see .claude/rules/playgrounds.md): it lives inside
the unibus app, reuses the parent Go module (no separate go.mod), is not
indexed, and keeps all runtime state under playground/local_files/ (ephemeral,
safe to delete).
Run
From the unibus app directory:
cd /home/enmanuel/fn_registry/projects/message_bus/apps/unibus
go run ./playground
Then open http://localhost:7700 in your browser.
Stop with Ctrl-C — the server tears down the web UI, every bus client, the
control plane, and the embedded NATS cleanly (no orphaned processes).
Architecture
The browser never speaks NATS. The Go server is the actual bus peer:
browser ──fetch/SSE──▶ playground server (:7700)
│ holds one unibus client per named peer
├──HTTP──▶ membership control plane (127.0.0.1:8480)
└──NATS──▶ embedded NATS + JetStream (:4260)
- :7700 — web UI (the only browser-facing port).
- 127.0.0.1:8480 — membership control plane (rooms, members, sealed keys, rekey, blobs). Internal only.
- :4260 — embedded NATS + JetStream (the data plane). Internal only.
Each named peer gets its own long-term identity, persisted to
playground/local_files/<name>.id, so a peer keeps the same endpoint across
restarts. When a peer creates or joins a room, the server subscribes on its
behalf and streams every received frame to that peer's open browser tabs over
Server-Sent Events.
The playground only orchestrates the public unibus client API
(CreateRoom, Join, Subscribe, Publish, Invite, Kick); it never
reimplements bus or crypto logic.
Try it: 2 peers + encryption + kick
- Open two browser tabs on http://localhost:7700.
- Tab A: type
alice, click Connect. - Tab B: type
bob, click Connect. - Tab A (alice): type a subject like
room.general, tick 🔒 encrypted (E2E), click Create room. Copy the resultingroom_id. - Tab A (alice): in the Action panel, pick
bobas the target peer (use the ↻ button to refresh the peer list if needed) and click Invite to this room. - Tab B (bob): paste the
room_idinto the join field and click Join. - Type messages in both tabs and hit Send — each message appears live in
both tabs, tagged with subject, sender, time, and 🔒 (encrypted) or
clear. - Tab A (alice): click Kick from this room with
bobselected. The room key rotates to a new epoch. New messages alice sends are no longer visible to bob — forward secrecy: bob no longer holds the current key.
Cleartext rooms (leave the checkbox unticked) behave like plain NATS fan-out: fast, ephemeral, unsigned. Encrypted rooms are the Matrix-like mode: E2E encrypted, persisted, and per-message signed.
State / cleanup
All writable state lives under playground/local_files/:
<name>.id— per-peer identity (private keys; treat like an SSH key).play.db— membership store (rooms, members, sealed keys).blobs/— media blob store.js/— embedded JetStream store.
Delete the whole playground/local_files/ directory to reset to a clean slate.
It is gitignored and never distributed.