The single shared mesh is replaced by per-session rooms. Visit /
and the server mints a random 8-hex-char id, redirects to /r/<id>.
That URL IS the session — share the link (or scan the QR code now
shown on the page) on another device to join the same room.
Bus is now sharded per room. Rooms are created implicitly on first
subscribe and GC'd 5 minutes after the last subscriber leaves. No
accounts, no persistence, no server-side state beyond the in-memory
bus map.
Server:
- New endpoints: /, /r/<id>, /api/send?room=, /api/stream?room=
- Room manager with lazy creation + idle GC
- Metrics now labelled by room
- New gauge tether_active_rooms
Client (Go):
- -room flag (accepts bare id OR full /r/<id> URL — paste-friendly)
- All API calls now scope to the room
- The always-on ct210-rtc-peer systemd unit is disabled — sessions
are user-initiated; the user runs tether-client with -room when
they want their laptop in a particular session
Browser (HTML):
- Reads room from /r/<id> path
- Shows QR code + URL + "copy link" button at top
- "+ new session" link in header to start a fresh room
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Server: unchanged shape, just added a "signal" message type to the
existing /api/send + /api/stream bus. Now carries both "clipboard"
(payload) and "signal" (offer/answer/ICE) over the same envelope.
Client: -rtc flag turns the Go listener into a Pion peer. Posts an SDP
offer at startup, accepts the browser's answer through the signaling
bus, exchanges ICE, then receives clipboard text over a DataChannel
named "tether". On message: writes to OS clipboard same as SSE path.
Web UI: acts as the answerer. Listens for "signal" SSE events, replies
to offers, exchanges ICE. When DataChannel opens, the send button uses
RTCDataChannel.send() instead of POST /api/send — data no longer
traverses the server after pairing. Pill in the header flips
sse → negotiating → rtc to make this visible.
Toolchain: bumped go.mod to go 1.26, switched to pion/webrtc v4 and
prometheus/client_golang v1.23.x.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add Prometheus counters/gauges/histograms: messages_total{source},
message_bytes_total, active_subscribers, publish_duration_seconds.
- Add /api/signal/<room> mailbox endpoint (POST adds, GET drains).
Currently scaffolding for WebRTC SDP/ICE exchange — peers do not
use it yet; client-side WebRTC negotiation is roadmap.
client: structured default label
Use "<GOOS>-sse-<role>" (e.g., windows-sse-listener) instead of the
old "linux-client" fallback. -label still overrides.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Single Go module with two binaries (server, client) and an embedded
phone UI. MVP transport is HTTP POST → SSE fanout; the roadmap calls
for upgrading to WebRTC P2P with Sign in with Apple for identity, mDNS
for discovery, and OS clipboard hooks.
- server/: Go HTTP server, embedded index.html, broadcast bus with
short replay history, SSE stream endpoint, single-binary deploy.
- client/: subscribes to SSE feed and prints messages; -send for
one-shot publish from CLI. No OS clipboard touched yet (v0.5).
- web/index.html: dark phone-first UI, paste-clipboard button (uses
navigator.clipboard.readText), live feed of incoming messages via
EventSource.
This commit is intentionally tiny — it proves the end-to-end shape so
the WebRTC/SiwA/mDNS pieces can be added incrementally without
restructuring.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>