Files
clipsync/docs/protocol-v1.md
T
2026-08-27 15:38:55 +02:00

3.4 KiB

clipsync Protocol v1 (MVP)

This document defines an implementation-friendly first protocol for clipboard sync.

Transport

  • Discovery: UDP broadcast on port 45821
  • Session: TCP on port 45821
  • Payload encoding: UTF-8 JSON frames
  • Frame format on TCP: 4-byte big-endian length prefix + JSON bytes

Versioning

Each message includes:

  • version: protocol version string, initially "1"
  • type: message type
  • request_id: unique string per request/response pair where relevant
  • device_id: sender stable ID
  • timestamp_ms: unix epoch milliseconds

Discovery message (UDP)

DISCOVERY_ANNOUNCE

{
  "version": "1",
  "type": "DISCOVERY_ANNOUNCE",
  "device_id": "desktop-uuid",
  "device_name": "workstation",
  "platform": "linux|windows",
  "tcp_port": 45821,
  "capabilities": ["clipboard:text"],
  "timestamp_ms": 0
}

Android listens for these packets and shows peers.

Pairing flow (TCP)

1) HELLO

Both peers exchange:

  • protocol version
  • device info
  • ephemeral nonce

2) PAIR_REQUEST

Unpaired side sends pairing request including:

  • public key
  • pairing method ("pin" or "qr")

3) PAIR_CHALLENGE

Responder sends short code metadata (or QR payload hash) and challenge nonce.

4) PAIR_CONFIRM

Requester returns challenge response.

5) PAIR_ACCEPT

Responder confirms trust and stores remote public key.

After PAIR_ACCEPT, both devices persist:

  • peer_device_id
  • peer_public_key
  • paired_at

Session messages

CLIP_UPDATE

{
  "version": "1",
  "type": "CLIP_UPDATE",
  "request_id": "req-123",
  "device_id": "android-uuid",
  "timestamp_ms": 0,
  "origin_device_id": "android-uuid",
  "content_type": "text/plain",
  "encoding": "utf-8",
  "content_hash": "sha256:...",
  "content": "clipboard text",
  "sequence": 42
}

Rules:

  • Only sync text/plain in MVP.
  • Ignore messages with known content_hash from recent history.
  • Ignore message if origin_device_id equals local device id (echo prevention).

ACK

Acknowledges accepted request_id.

ERROR

Contains error code and message (UNPAIRED, UNSUPPORTED_VERSION, INVALID_MESSAGE, RATE_LIMITED).

PING / PONG

Keepalive every 15 seconds. Drop and reconnect after timeout.

Security baseline

  • Reject non-paired peers for clipboard writes.
  • Encrypt session channel (TLS or Noise).
  • Enforce max payload size (e.g., 1 MiB for MVP text).
  • Apply rate limits per peer.

Current daemon behavior (implemented)

  • Server sends HELLO immediately on TCP connect.
  • First client message must be HELLO (else ERROR: INVALID_MESSAGE).
  • After HELLO, server accepts either:
    • PAIR_REQUEST and responds with PAIR_ACCEPT (while persisting peer trust), or
    • CLIP_UPDATE for already-paired peers.
  • CLIP_UPDATE from unknown peers is rejected with ERROR: UNPAIRED.
  • While connected, server polls local clipboard and emits CLIP_UPDATE to the connected paired peer when local text changes.
  • Incoming paired CLIP_UPDATE (content_type = text/plain) is applied to local OS clipboard.
  • Inbound CLIP_UPDATE messages are deduplicated using content_hash (with a fallback hash for missing values).
  • Inbound CLIP_UPDATE messages are rate-limited per peer; over-limit updates return ERROR: RATE_LIMITED.
  • Keepalive is implemented: server sends periodic PING, responds to inbound PING with PONG, and drops idle sessions after timeout.