What this is
A RAPP neighborhood is a set of brainstems that meet as uniform peers and talk in one
envelope shape (rapp-twin-chat/1.0). A tether is the live link between two of them.
This guide shows how to make Claude the doorman: a fresh Claude session reads one file, proves a
secure door works on its machine, opens it, and hands you the key.
Quick start
- On the machine you want to reach, open a fresh Claude (Code) session and paste:
Read https://raw.githubusercontent.com/kody-w/vbrainstem/main/doorman.skill.md and set me up as the doorman to this machine.
- That Claude self-tests (hosts the bridge in headless Chrome, drives it from a separate sealed peer), checks prerequisites, then opens the door and gives you a peer-id + token + operator link.
- Walk through the door from anywhere β open the operator link in a browser, or from a shell:
Every byte is opaque ciphertext; the doorman refuses anyone without the token.
bash vbridge.sh <peer-id> <token> ask "who are you and what can you do?"
Uniform peers
Everyone in the neighborhood β a person, a local brainstem.py, a browser vBrainstem, or Claude β
speaks the same envelope, so you can't tell what's on the other end. That's the point: a tether is transparent.
// twin-chat envelope { schema:"rapp-twin-chat/1.0", from_rappid, to_rappid, utc, nonce, kind:"say"|"console", payload, facets }
Tethers & kited twins
There are two live (Β§5a) transports:
π° WebRTC tether 5a-tether
Direct browserβbrowser P2P. The PeerJS broker does the handshake only (SDP/ICE); data flows DTLS-encrypted peer-to-peer β the broker never sees it.
πͺ Kite tether 5a-kite
An operator (Claude) holds the string: drives a browser tab's console over the Chrome DevTools Protocol and relays. No broker, no STUN, no CORS β the string-holder is the transport.
A kited twin is a tab flown on a kite string. It is tethered when the string also reaches the
locally-running brainstem (5a-kite+tether) β its turns are answered by that brainstem;
with no local brainstem it is just kited (answered by the tab's own in-page brainstem).
The sealed channel β as secure as on-device
A fully on-device neighborhood has no wire to tap. To match that over a network, every envelope can be sealed:
| Property | How |
|---|---|
| Confidential | AES-256-GCM; the wire/broker/TURN see only {schema:"rapp-sealed/1.0", iv, ct} |
| Tamper-evident | GCM auth tag β a flipped byte is rejected (decrypt/auth failed) |
| Authenticated | Key = PBKDF2-SHA256 (210k, salt rapp-neighborhood-5a/1) from a secret that rides only the out-of-band pairing link β never the broker |
| At rest | Secrets & holocards use the same AES-256-GCM vault primitive |
Layered over WebRTC's DTLS, the network is fully untrusted and still safe. A wrong-key peer can't even read the rejection. The identical scheme runs in the browser, the bridge, the CLI, and Node β one contract on every hop.
Session & handshake
Opening the door is one call: host() mints a peer-id + token (the token is the channel
secret). Sharing it out-of-band is the handshake. Closing the tab / killing the host destroys the peer + secret β
the session ends, exactly like stopping brainstem.py. Plaintext chat (say) may be open;
operating the console (run/eval/chat) only works over the sealed channel,
where key-possession is the authorization.
Set up a doorman
Prerequisites (on the machine being opened)
- Local brainstem running β
curl -s http://localhost:7077/healthreturns{"status":"ok",β¦}. - Google Chrome / Chromium (headless WebRTC + CDP). Set
CHROME=/path/to/chromeif needed. - Node β₯ 18 β built-in
WebSocket/fetch/crypto.subtle.
Prove it, then open it
The skill makes Claude run this first β it exits PASS only if a separate sealed peer reached the real brainstem:
# one-command proof this machine can be a sealed doorman
curl -fsSL https://raw.githubusercontent.com/kody-w/vbrainstem/main/doorman_selftest.sh | bashOpen the door (GUI). The brainstem is on http://localhost, so serve the bridge from localhost too β an HTTPS page is blocked from fetching http://localhost (mixed-content / private-network) and the call hangs; localhost is still a secure context, so sealing works:
curl -fsSL https://raw.githubusercontent.com/kody-w/vbrainstem/main/brainstem_bridge.html -o brainstem_bridge.html
python3 -m http.server 8123 &
open http://localhost:8123/brainstem_bridge.html # URL stays http://localhost:7077 β βHost bridgeβThe three duties
A Β· Open the door β front this machine's brainstem
Use the bridge above (GUI), or host it headless and keep Chrome alive. It publishes a peer-id + token; share the operator link with the authorized visitor.
B Β· Visit a door β operate another host
bash vbridge.sh <peer> <token> ask "hello" # sealed chat bash vbridge.sh <peer> <token> chat "summarize my agents" # β their /chat bash vbridge.sh <peer> <token> eval "1+1" # console: rapp.eval bash vbridge.sh <peer> <token> agents # list their agents
In any tab's console you have the same API: rapp.neighborhood.host(), .ask(peer,text,secret), .operate(peer,method,args,secret).
C Β· Fly a kited twin tethered to this machine
# launch the kite tab with CDP on a private localhost port "$CHROME" --headless=new --remote-debugging-port=9350 --user-data-dir=/tmp/kite "file:///path/to/kite.html" & # hold the string; --brainstem makes the twin TETHERED to this machine node kited_twin.js --port 9350 --brainstem http://localhost:7077
Connect two machines
Run the doorman skill on both machines. Each gives you its peer-id + token. Now either machine's twin (or you, from any browser/shell) reaches the other's brainstem β sealed both ways:
# from machine A, reach machine B's brainstem (end-to-end sealed)
bash vbridge.sh <peerB> <tokenB> ask "who are you and what can you do?"Security rules the doorman enforces
- CDP stays on the machine. Never bind/forward Chrome's debug port; cross-machine = WebRTC, not CDP.
- Token = the key. It authorizes and derives the AES-256-GCM key. Share only via the out-of-band operator link; rotate by re-hosting.
- Operating requires the sealed channel. Plaintext chat may be open; console ops are key-gated.
- Closing ends it. Kill the host tab/Chrome β the peer + secret are gone. No lingering access.
- No plaintext secrets on disk or in logs; at rest uses the same vault primitive.
Tools
| File | Role |
|---|---|
| doorman.skill.md | Feed its raw URL to a fresh Claude β it sets up & guards the sealed door |
| doorman_selftest.sh | One-command proof a machine can be a sealed doorman |
| brainstem_bridge.html | Front a local brainstem into the neighborhood (sealed) |
| vbridge.sh | CLI peer β operate a hosted tab from a shell, sealed |
| kited_twin.js | The kite string (CDP); --brainstem β tethered |
| index.html | The vBrainstem app β window.rapp.neighborhood |
Troubleshooting
| Symptom | Fix |
|---|---|
| Self-test fails at brainstem health | The local brainstem isn't on BRAINSTEM_URL β start it (or set the URL). |
Chrome not found | export CHROME=/path/to/chrome and retry. |
| Two devices on different networks won't pair | NAT traversal needs STUN/TURN β fine on normal nets; same-machine/LAN needs neither. |
| Bridge hangs at βstarting bridgeβ, never hosts | The page is HTTPS but the brainstem is http://localhost β serve the bridge from localhost (HTTPSβlocalhost is blocked by mixed-content / private-network). |
crypto.subtle undefined | The bridge is on file:// β use the HTTPS page or a localhost server (secure contexts). |
| Visitor gets timeouts, not errors | That's a wrong-key peer being shut out (it can't read the sealed rejection) β re-check the token. |