REVELation Documentation
Peering and Discovery
Peering is a powerful mechanism that allows one "master" instance to push a presentation simultaneously to multiple "follower" instances. The other instances could mirror the primary presenter screen, show notes or lower-thirds versions, allow different aspect ratios for streaming, or show the presentation in different languages.
Peering must first be enabled under "Settings" on both master and follower instances, and "network" mode must be enabled at least on the master instance.
The peering screen shows available instances on the local network. For peering to work, all instances must be connected to the same local network. It likely will not work on public WiFi or other setups that "isolate clients."
Pairing is always initiated from the "follower" to the "master." You must know the master Pairing Pin (available from the info dialog on the main screen).
Below is a more technical reference on how the protocol works.
Table of Contents
Overview
Peering lets one REVELation wrapper instance (the "master") remotely open/close a presentation on another instance (the "follower").
At runtime, the system uses:
- mDNS (
bonjour-service) for LAN discovery. - Plain HTTP (not HTTPS) on the wrapper's Vite port for pairing and command bootstrap endpoints.
- Socket.IO for ongoing peer commands.
- RSA-2048 signatures (SHA-256) for challenge-response identity checks and short-lived socket auth payload signing.
- A shared pairing PIN for authorization.
Protocol Direction Overview:
- Master nodes advertise via mDNS and expose peering points via an HTTP protocol.
- Pairing is initiated from the "follower" to the "master" The "follower" acts as the client and calls the candidate peer's HTTP (vite server) endpoints (running on
viteServerPortport, typically 8000). - Command direction: followers keep outbound Socket.IO connections to each paired master and receive peer-command events via the Vite server Socket.IO endpoint (/peer-commands, also
viteServerPort).
Implementation-specific note: In this Electron wrapper, advertising and endpoint availability are gated by local config (
mdnsPublish) and startup mode (network).
Ports:
- Discovery advertisement includes
pairingPort, currently the same asviteServerPort. - Pairing endpoints (
/peer/*) are served over HTTP.
Implementation-specific note: In this Electron wrapper,
/peer/*is hosted on the Vite server (viteServerPort, typically 8000), hard-disabled unlessmdnsPublish === true, and separate from Reveal Remote (revealRemoteServerPort, often 1947).
Key and secret storage:
- "Masters" maintain an RSA key pair used to prove their identity to repeat connections from "followers."
-
rsaPublicKey,rsaPrivateKey,mdnsPairingPin, andpairedMastersare persisted in Electron config and automatically managed. - Private keys are long-lived and reused across runs unless config is replaced. Paired followers are treated as unverified and command connection fails if the master is unable to authenticate to followers with its private key.
Roles and Transport
Terminology used by implementation:
-
master: a paired node a follower listens to for peer commands. -
follower: local node executing commands from paired masters. -
instanceId: stable per-install random hex identifier (16 hex chars; 8 random bytes).
Transport summary:
- mDNS service type:
revelation - Pairing/auth endpoints: HTTP JSON
- Realtime command channel: Socket.IO on path
/peer-commands
Discovery (mDNS)
Protocol behavior:
- Peers are discovered via mDNS service type
revelation. - Instances may advertise metadata using mDNS TXT fields listed below.
Implementation-specific note: In this Electron wrapper, browse/publish are controlled by
mdnsBrowse/mdnsPublish, browser refreshes every 15 seconds, and self-advertisements are ignored byinstanceId.
Service publication details:
- Service type:
revelation - Service name:
mdnsInstanceName(default${username}@${hostname}) - Host:
${os.hostname()}.local(unless already.local) - Port:
viteServerPort -
disableIPv6: true
Published TXT payload:
-
instanceId -
mode -
version -
hostname -
pairingPort -
pubKeyFingerprint(sha256(publicKeyPem)hex)
Implementation-specific note: Host selection prefers first discovered IPv4 address and falls back to
service.host. Previously paired instance IDs are re-verified on mDNSupvia/peer/challengebefore being accepted as online.
Pairing Protocol
Pairing is HTTP JSON over http://<peerHost>:<pairingPort>.
Implementation-specific note: In this Electron wrapper, pairing endpoints are available only when target peer
mdnsPublish === true.
1) Fetch identity
GET /peer/public-key
Response:
{
"instanceId": "<string>",
"instanceName": "<string>",
"hostname": "<string>",
"publicKey": "-----BEGIN PUBLIC KEY-----...",
"publicKeyFingerprint": "<sha256 hex>"
}
Validation rules:
- Verify that response fields needed for trust selection are present and consistent.
Implementation-specific note: This wrapper enforces discovered TXT hostname match (when present), then chooses master ID by priority: response
instanceId, discoveredpeer.instanceId, then discoveredpeer.txt.instanceId.
2) Challenge-response with PIN
Client generates challenge as base64 of 32 random bytes.
POST /peer/challenge
Request:
{
"challenge": "<base64 random>",
"pin": "<pairing pin>"
}
Response:
{
"signature": "<base64 RSA-SHA256 signature of challenge>"
}
Server-side pin rule:
- If
mdnsPairingPinis set/non-empty, providedpinmust match exactly or server returns403. - If no PIN is configured, PIN is not enforced.
Verification rule on client:
- Verify signature over
challengewith RSA-SHA256. - Public key used for verification is:
- existing stored
pairedMasters[n].publicKeyfor sameinstanceId, else -
/peer/public-keyresponsepublicKey.
3) Persist paired master
Implementation-specific note: This wrapper persists paired masters in local config (
pairedMasters) with fields such asinstanceId,publicKey,name,pairedAt,hostHint,pairingPortHint, andpairingPin. It also maintains a runtime cache (pairedPeerCache) and removes both entries on unpair.
Peer Command Channel
Followers connect outbound to paired masters and receive peer-command events.
Implementation-specific note: This wrapper refreshes follower connections every 10 seconds and requires target
mdnsPublish === truefor bootstrap/fan-out endpoints.
Bootstrap: signed socket info
Follower requests:
GET /peer/socket-info?instanceId=<followerInstanceId>&pin=<pairingPin>
Implementation-specific note: This wrapper validates PIN exactly as
/peer/challengeand currently does not authorize based oninstanceIdquery parameter.
Response:
{
"socketUrl": "http://<host>:<port>",
"socketPath": "/peer-commands",
"token": "<hex 16 random bytes>",
"expiresAt": 1700000000000,
"signature": "<base64 RSA-SHA256 signature>"
}
Signed payload format:
-
"${token}:${expiresAt}:${socketPath}"
Follower verifies signature with stored master public key before connecting.
Socket.IO connect
Follower connects to socketUrl with path /peer-commands and auth payload:
{
"token": "...",
"expiresAt": 1700000000000,
"signature": "...",
"instanceId": "<followerInstanceId>"
}
Server handshake validation:
- Requires
token,expiresAt,signature. - Requires
expiresAt >= Date.now(). - Verifies signature over
"${token}:${expiresAt}:/peer-commands"using local configured public key.
Command fan-out
Implementation-specific note (not core protocol): this loopback POST /peer/command endpoint is an Electron-wrapper convenience for local UI-to-socket fan-out. A compatible implementation may use a different local dispatch mechanism.
Local master-side UI posts commands to loopback only:
POST /peer/command to http://127.0.0.1:<vitePort>/peer/command
Request:
{
"command": {
"type": "open-presentation",
"payload": {
"url": "<share URL>"
}
}
}
or
{
"command": {
"type": "close-presentation",
"payload": {}
}
}
Server rules:
- Rejects non-loopback callers with
403. - Emits
peer-commandevent to all connected peer sockets.
Implementation-specific note: This wrapper handles
open-presentationby opening the URL in presentation windows (including additional screens), handlesclose-presentationby closing them, and logs/ignores unknown command types.
Persistence and Data Model
Implementation-specific section: The following keys and schemas describe this Electron wrapper's local storage model, not protocol-required on-wire fields.
Config keys relevant to peering:
-
mode:networkenables LAN server binding;localhostdoes not publish. -
mdnsBrowse: controls whether this node browses for peers and whether follower-side pairing/peer command behavior is allowed. -
mdnsPublish: controls whether this node advertises itself innetworkmode and whether local/peer/*endpoints are enabled. -
mdnsInstanceName: advertised name. -
mdnsInstanceId: stable unique node id. -
mdnsPairingPin: shared secret checked by/peer/challengeand/peer/socket-info. -
rsaPublicKey/rsaPrivateKey: RSA-2048 PEM keypair. -
pairedMasters: persisted trust records.
pairedMasters[] persisted schema:
-
instanceId: string(required) -
name: string -
publicKey: string(PEM) -
pairedAt: string(ISO datetime) -
hostHint: string -
pairingPortHint: number -
pairingPin: string
Runtime-only cache (pairedPeerCache) entries:
-
host,port,addresses[],hostname,lastSeen
Compatibility Checklist
A parallel implementation is wire-compatible if it does all of the following:
- Publishes and browses mDNS service type
revelationwith matching TXT fields. - Uses the same HTTP endpoints and JSON payloads:
-
GET /peer/public-key -
POST /peer/challenge -
GET /peer/socket-info
-
- Uses RSA-SHA256 signatures with base64 signatures and PEM keys.
- Uses challenge format as base64 random bytes.
- Uses socket signed payload format exactly:
token:expiresAt:socketPath. - Uses Socket.IO path
/peer-commandsandpeer-commandevent name. - Persists trusted peers by
instanceId+ pinnedpublicKey. - Re-verifies known peers when rediscovered by mDNS before accepting them as online.
Implementation-specific note: This wrapper also exposes loopback
POST /peer/commandfor local UI fan-out, but that endpoint is not required by the core peer wire protocol.
Security Model and Assumptions
Current trust model:
- Identity is cryptographic (RSA keypair).
- Authorization is mostly PIN-based for pairing and socket bootstrap.
- Transport is plaintext HTTP on LAN.
Assumptions required for safe operation:
- LAN is semi-trusted and not actively MITM'd.
- Pairing PIN is kept private and reasonably strong.
- Initial key fetch during first pair is not tampered with.
- Device compromise implies peer trust compromise (private key + stored PIN).
Known limitations:
- No TLS; metadata, tokens, and commands are observable on LAN.
- No mutual identity binding in socket handshake beyond signed bootstrap tuple.
-
/peer/socket-infodoes not currently enforce callerinstanceIdauthorization.
Implementation-specific note: In this wrapper,
mdnsAuthTokenexists in config but is unused by the current protocol, and PIN throttling is in-memory per source IP (3 failures -> 60s block), so counters reset on app restart and are not shared across multiple instances.
Hardening Recommendations
High-impact improvements:
- Add HTTPS for all
/peer/*endpoints and Socket.IO transport (TLS), with certificate pinning or TOFU pinning. - Replace static/shared PIN with per-device enrollment secrets or short-lived pairing codes.
- Bind
/peer/socket-infoissuance to an authorizedinstanceIdand require nonce-based replay protection. - Sign and verify richer claims (issuer, subject instanceId, issued-at, expiry, audience) instead of raw tuple strings.
- Encrypt at-rest sensitive values (
rsaPrivateKey, stored pairing secrets) with OS keychain/secure enclave integration.
Medium-impact improvements:
- Add key rotation and explicit trust reset workflows.
- Extend PIN abuse protections with persistent/distributed lockout and telemetry.
- Restrict Socket.IO command recipients by explicit pair mapping instead of broadcast-to-all connected peers.
- Validate mDNS fingerprint (
pubKeyFingerprint) against persisted key before any active challenge requests. - Add audit logging for pair/unpair, socket-info issuance, and command sender identity.
Troubleshooting
For operator-focused troubleshooting steps (including manual pairing when mDNS discovery is blocked), see:
Documentation Hub
Created with REVELation Snapshot Presenter