P2P screen sharing with wallet-verified identity over the Anvil BSV mesh.
Live demo · Features · Quick Start · Architecture
AnvilCast is a screen-sharing service where every session is identified by a BRC-100 wallet, every signaling envelope is gossiped through the BSV mesh, and every contact is a secp256k1 public key — not an account, not an email, not a phone number.
Two people open it, both have wallets, and they share a screen. There is no sign-up. There is no observation pixel, no analytics, no server-side recording, no account database. The server holds nothing about you that survives the session.
| Feature | Status |
|---|---|
Screen sharing with system audio (getDisplayMedia) |
desktop / Android Chrome |
| Microphone with mute toggle | host |
Camera with PiP overlay on viewer (getUserMedia) |
host |
Recording to local .webm (MediaRecorder) |
both |
| Screen annotation — draw / highlight on the shared video | viewer |
| File drop — drag a file into chat, sent over WebRTC data channel (50 MB cap) | both |
| Chat — server-signed envelopes via Anvil messagebox topic | both |
| Wallet identity — BRC-100 / BRC-103 challenge–response | optional |
| Contacts — wallet-discovered + local nicknames + presence dots | host |
| Ring — call a contact who's currently in their inbox (presence WebSocket) | host |
| Invite — leave an invitation in someone's BRC-33 inbox for whenever they next come online | host |
| TURN relay — coturn fallback for NAT traversal, HMAC ephemeral credentials | server-side |
| Proof Rooms integration — BRC-60 hash chain of session events for auditable evidence capture | both |
- Quick Share (unsecured): a 16-character room code, click a link, watch. No wallet required. Server-managed ephemeral keys. Good for showing somebody a thing.
- Secured (wallet-verified): both sides authenticate their BRC-100 wallet via nonce challenge. Identity keys are exchanged. Contacts persist (locally) across sessions. Required for the proof-rooms evidence flow.
https://anvil.sendbsv.com/cast/
You will need a BRC-100 wallet for the Secured flow. BSV Desktop and the BSV Association mobile browser are tested. The unsecured Quick Share flow requires nothing.
| Platform | View | Host |
|---|---|---|
| Desktop (Chrome / Firefox / Edge / Safari) | yes | yes |
| Android Chrome | yes | yes |
| Android — BSV Association browser (React Native WebView) | yes | no (WebView lacks getDisplayMedia) |
| iOS — any browser | yes | no (Apple has not implemented the Screen Capture API) |
git clone https://github.com/BSVanon/AnvilCast.git
cd AnvilCast
go build -o bin/anvilcast ./cmd/server
./bin/anvilcastOpen http://localhost:8080. Pick a role, share a link.
Route signaling through the Anvil BSV mesh instead of direct WebSocket relay:
./bin/anvilcast \
-anvil-host http://127.0.0.1:9333 \
-anvil-viewer http://127.0.0.1:9334In mesh mode, every signaling message is a secp256k1-signed envelope gossiped to all connected Anvil peers. You need two Anvil nodes — see the Anvil README for setup.
Symmetric NAT and CGNAT (most cellular networks) will block direct P2P. Configure a coturn relay in HMAC ephemeral mode:
TURN_SECRET=$(openssl rand -hex 32) ./bin/anvilcast \
-turn-url turns:your.turn.host:5349 \
-turn-ttl 1hThe same secret goes in coturn's static-auth-secret config. The server generates fresh credentials per /api/config request — credentials never live longer than -turn-ttl.
For production, terminate TLS at a reverse proxy (nginx is what the reference deploy uses) and pass -origins to lock down WebSocket origins:
./bin/anvilcast \
-addr :8090 \
-origins https://your.domainOr use the built-in TLS support:
./bin/anvilcast \
-cert /path/to/cert.pem \
-key /path/to/key.pem| Flag | Default | Description |
|---|---|---|
-addr |
:8080 |
Listen address |
-web |
./web |
Static file directory |
-cert |
TLS certificate path (enables HTTPS) | |
-key |
TLS key path | |
-origins |
Comma-separated allowed WebSocket origins (empty ⇒ allow all, dev only) | |
-anvil-host |
Anvil node URL for host signaling | |
-anvil-viewer |
Anvil node URL for viewer signaling | |
-turn-url |
TURN server URL for ICE config | |
-turn-ttl |
1h |
TURN credential lifetime |
Read from env to avoid ps aux exposure:
| Var | Description |
|---|---|
ANVIL_HOST_AUTH |
Bearer token for the host Anvil node (optional) |
ANVIL_VIEWER_AUTH |
Bearer token for the viewer Anvil node (optional) |
TURN_SECRET |
Shared secret for HMAC ephemeral TURN credentials |
Browser (Host) Browser (Viewer)
| |
|--- /ws (signaling) ----+ +---- /ws (signaling) ---|
|--- /ws/presence -------+ +---- /ws/presence ------|
| | | |
| v v |
| ┌────────────────────┐ |
| │ AnvilCast Go │ |
| │ Server │ |
| │ │ |
| │ Hub Presence │ |
| │ Relay Registry │ |
| │ ProofChain │ |
| └─────────┬──────────┘ |
| | |
| ┌───────────┴───────────┐ |
| v v |
| Anvil Node A Anvil Node B |
| :9333 (host) :9334 (viewer) |
| | | |
| └───── mesh gossip ─────┘ |
| |
+─── WebRTC media (P2P, with TURN fallback via coturn) ────+
/ws— signaling WebSocket. SDP offer/answer, ICE candidates. Bridged to Anvil mesh in mesh mode, otherwise direct relay./ws/presence— authenticated contact presence. Tracks who's online by identity key, broadcasts presence updates to anyone watching that key.- WebRTC media — browser-native, peer-to-peer. STUN by default, TURN relay if configured.
- Anvil mesh — every signaling envelope is a secp256k1-signed message gossiped to all peers. The server cannot forge or modify a signed envelope; verification happens at the Anvil node level.
- Identity — BRC-100 wallet challenge–response. Per BRC-3/BRC-42, the server cannot independently verify wallet signatures (derived child keys require the counterparty's shared secret), so identity is established by the wallet's participation in the nonce challenge — the act of producing a valid
createSignatureresponse is itself the proof. - Proof Rooms — when a session is created from a
proof_room_request(TrueProof contract), every lifecycle event is appended to a BRC-60 hash chain. On session end, the full chain plus its head hash is sent back via thetrueproof_proof_roommessagebox for evidence anchoring.
| Path | Description |
|---|---|
/ws?role=host|viewer&room=<id> |
Signaling — SDP, ICE, room lifecycle |
/ws/presence |
Presence — contact status, presence-initiated rooms |
| Endpoint | Method | Description |
|---|---|---|
/api/config |
GET | ICE config (STUN servers + ephemeral TURN credentials) |
/api/identity |
GET | Server's Anvil node identity pubkeys (mesh mode only) |
/api/identify/create |
POST | Host generates a sign-up token for an invite link |
/api/identify/challenge |
GET | Viewer requests a signing nonce |
/api/identify/respond |
POST | Viewer submits identity proof |
/api/identify/poll |
GET | Host polls for viewer verification result |
/api/messagebox/send |
POST | Send a message to a recipient's BRC-33 inbox |
/api/messagebox/list |
POST | BRC-33 listMessages |
/api/messagebox/acknowledge |
POST | BRC-33 acknowledgeMessage |
/api/messagebox/subscribe |
GET (SSE) | Live stream of inbox messages |
/api/messagebox/chat |
POST | Send a chat message into a room topic |
/api/messagebox/chat/subscribe |
GET (SSE) | Live stream of room chat |
/api/messagebox/live/announce |
POST | Announce a public live session |
/api/messagebox/live |
GET (SSE) | Discover public live sessions |
/api/utxo/check |
GET | UTXO unspent check (used for BRC-52 cert revocation) |
/api/ring |
POST | Legacy invite-by-pubkey endpoint |
There is also a diagnostic endpoint, /api/debug/presence, that lists currently registered presence users. It is locked to direct loopback only — any request with an X-Forwarded-For or X-Real-IP header is rejected, so it is unreachable through a reverse proxy.
| BRC | Used For |
|---|---|
| BRC-100 | Wallet-to-application interface |
| BRC-103 | Mutual authentication (challenge–response) |
| BRC-73 | Grouped wallet permissions (one prompt for many ops) |
| BRC-52 | Identity certificates (three-tier name resolution) |
| BRC-42 / BRC-43 | Per-session derived keys |
| BRC-33 | Messagebox (send / list / acknowledge) |
| BRC-77 | Wallet-to-wallet message signing |
| BRC-60 | Hash chain over session events (proof rooms) |
go test ./... # full suite
go test -race ./... # race detector
go test -cover ./... # coverageThe presence flow has integration tests against a real httptest server (see internal/signaling/presence_test.go) that exercise host-first, friend-first, and case-mismatch scenarios. The BRC-60 hash chain has cross-language test vectors (internal/proofchain/chain_test.go) that verify byte-identical SHA-256 output against the TypeScript reference implementation in trueproof/proof-rooms.
Pre-release audit history lives outside this repo (private), but the reference deploy at anvil.sendbsv.com/cast/ runs the BUG_HUNTS release-gate before each tag. The pre-v1.0.0 audit (2026-04-07) ran:
go vet,staticcheck— cleangovulncheck— clean (aftergo.modtoolchain bump to 1.26.1)gosec— only false-positives accepted (SSRF on a server-controlled URL, SHA-1 in coturn HMAC by spec)gitleaks— cleansemgrep— only false-positives accepted (io.WriteStringon SSE handlers writing JSON-marshaled structs, WebSocket origin check configured in upgrader constructor)go test -race— clean across all packages
If you find a security issue, please report it privately (open a GitHub issue with minimal detail and tag it security) before disclosing publicly.
Go modules (all permissively licensed):
| Module | License | Purpose |
|---|---|---|
github.com/gorilla/websocket |
BSD-2-Clause | WebSocket signaling + presence |
github.com/bsv-blockchain/go-sdk |
Open BSV | BEEF transaction signing |
github.com/decred/dcrd/dcrec/secp256k1/v4 |
ISC | Envelope signing, identity keys |
JS libraries (vendored):
| Library | License | Notice |
|---|---|---|
| QRCode for JavaScript (Kazuhiko Arase, 2009) | MIT | See NOTICE.md and web/js/lib/qrcode.js header |
The Software, and any software derived from it, may only be used on the Bitcoin SV blockchains.
Third-party components are listed in NOTICE.md.
