feat: add web playground for interactive bus testing (SSE + vanilla UI, all-in-one server)
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
1. Open **two browser tabs** on http://localhost:7700.
|
||||
2. Tab A: type `alice`, click **Connect**.
|
||||
3. Tab B: type `bob`, click **Connect**.
|
||||
4. Tab A (alice): type a subject like `room.general`, tick **🔒 encrypted
|
||||
(E2E)**, click **Create room**. Copy the resulting `room_id`.
|
||||
5. Tab A (alice): in the Action panel, pick `bob` as the target peer (use the
|
||||
↻ button to refresh the peer list if needed) and click **Invite to this
|
||||
room**.
|
||||
6. Tab B (bob): paste the `room_id` into the join field and click **Join**.
|
||||
7. Type messages in **both** tabs and hit Send — each message appears live in
|
||||
both tabs, tagged with subject, sender, time, and 🔒 (encrypted) or `clear`.
|
||||
8. Tab A (alice): click **Kick from this room** with `bob` selected. 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.
|
||||
Reference in New Issue
Block a user