Commit Graph

12 Commits

Author SHA1 Message Date
Claude Opus 4.7
618d330682 v0.5: stateless room-based sessions with QR pairing
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>
2026-05-21 01:13:33 -05:00
Claude Opus 4.7
7995908c87 v0.4: symmetric presence chirps + per-peer mesh
Both Go peer and browser now broadcast {type:"presence", from, role}
every 10s on the bus. When either side sees a presence from someone
they don't yet have a RTCPeerConnection to, they initiate a new one
targeted at that specific peerID via the new "to" field on signal
messages. Each side keeps a map<peerID, RTCPeerConnection> instead of
the v0.3 single-connection model.

This means:
- N browsers can pair with M peers (true mesh)
- New tabs auto-discover existing peers via their next 10s chirp
- Restarts and network blips recover within 10s instead of needing
  a manual browser refresh
- 45s lastSeen timeout sweeps disconnected peers and tears down their
  PeerConnection

The browser UI now shows a row of peer chips that flip green when their
DataChannel opens. The pill shows "rtc" if *any* peer is open, else
"negotiating" if any are in progress, else "sse".

Go side regenerates a random peerID per process start (was static).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 01:09:24 -05:00
Claude Opus 4.7
6ea7ed579e README: document v0.4 symmetric presence chirp design + bump status
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 01:05:50 -05:00
Claude Opus 4.7
7e63ffd357 client: chirp offer every 5s while unpaired
Solves the late-subscriber problem — browsers that load the page after
the peer's startup offer would never see one. Now the peer re-broadcasts
the offer every 5 seconds until the DataChannel opens, then stops.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 01:03:06 -05:00
Claude Opus 4.7
ce020d641a web: stop double-JSON-encoding the signal payload
Browser was JSON.stringify-ing the inner payload before putting it
in the outer message envelope. Go side then saw a JSON string where
it expected a JSON object, and SignalPayload Unmarshal silently
failed — which is why answers never made it back through the
peer connection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:56:38 -05:00
Claude Opus 4.7
09355e9914 web: filter incoming by per-browser peerID, not source label
Old filter hid all source=web messages — meant multiple browsers
couldn't see each other's sends. Now each browser only filters out
its OWN peerID, so iphone↔mbp↔desktop all see each other's clipboard.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:48:21 -05:00
Claude Opus 4.7
137a81c6a8 label: source 'phone' → 'web' (page works from any browser)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:42:42 -05:00
Claude Opus 4.7
98dc2ca2a6 v0.3: WebRTC P2P via Pion (Go) + RTCPeerConnection (browser)
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>
2026-05-21 00:37:31 -05:00
Claude Opus 4.7
80539ae60c server: /metrics endpoint + signaling stub for v0.3 WebRTC
- 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>
2026-05-21 00:30:37 -05:00
Claude Opus 4.7
24854e44d6 client: default to https://tether.pecord.io for double-click UX
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:22:31 -05:00
Claude Opus 4.7
fa52bf2693 client: actually write incoming messages to the OS clipboard
The MVP only printed to stdout. Now the listener calls
clipboard.WriteAll on every received message, except when the message
originated from itself (to avoid clobbering local edits with our own
prior send).

Adds:
- github.com/atotto/clipboard (cross-platform: Win/macOS/Linux)
- -no-clipboard flag for stdout-only mode
- "→ clipboard updated" trace line so the user can confirm the write

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 00:19:35 -05:00
Claude Opus 4.7
b8f168df54 v0.1: HTTP+SSE broadcast bus + phone web UI + Linux client
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>
2026-05-20 23:53:31 -05:00