Files
unibus/playground/README.md
T
Egutierrez 6b162deeb0 feat(playground): benchmark de rendimiento con flags JetStream/E2E/payload
Añade GET /api/bench (SSE) y una seccion de simulador en index.html: un publisher
inunda una room con miles de mensajes a N subscribers y una grafica en vivo anima
el throughput. Las dos politicas de room se exponen como flags independientes
(persist=JetStream, encrypt=E2E AEAD+Ed25519) mas tamano de payload, midiendo el
coste de cada capa con la libreria cliente real. El benchmark usa peers efimeros
propios, sin tocar los peers nombrados del sandbox manual.

Verificado: las 4 combinaciones enc x persist con fan-out exacto. Bump app v0.2.0.
2026-06-03 22:33:26 +02:00

5.1 KiB
Raw Blame History

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

  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.

Benchmark: throughput simulator

The bottom panel of the UI is a performance simulator. Press ▶ Ejecutar benchmark and one publisher floods a fresh room with thousands of messages that N subscribers receive (fan-out); a live canvas chart animates the sent vs received totals while it runs.

The two policy axes are exposed as independent flags, so the benchmark measures the cost of each layer in isolation:

JetStream Encryption Room policy What it costs
off off {Encrypt:false, Persist:false} plain core NATS fan-out
on off {Encrypt:false, Persist:true} durable JetStream (publish ack per message)
off on {Encrypt:true, Persist:false} AEAD + Ed25519 signature per message, core transport
on on {Encrypt:true, Persist:true} full E2E + durable history

A payload size slider (16 B 8 KiB) sets the message size. Encrypted or persistent runs are capped to 30 000 messages (each message pays per-message crypto and/or a JetStream ack, so they run much slower than plain NATS).

The benchmark uses its own ephemeral peers (fresh identities, never persisted), so it never touches the named peers of the manual sandbox.

It is driven by an SSE endpoint that streams progress samples:

curl -N "http://localhost:7700/api/bench?n_msgs=20000&n_subs=3&payload=128&encrypt=0&persist=0"
# emits: data: {"type":"start",...}  data: {"type":"sample",...}  data: {"type":"done",...}

Query params: n_msgs, n_subs (116), payload (bytes), encrypt (0/1), persist (0/1).

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.