documentation

This commit is contained in:
2026-08-27 18:43:51 +02:00
parent 0426b6eeec
commit 5372fe26e8
7 changed files with 135 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
# clipsync
LAN clipboard sync between Android and Linux/Windows devices. A device is a
participant in a sync mesh; paired devices exchange clipboard changes over an
authenticated session.
## Identity & Trust
**Device ID**
A stable, install-time unique identifier for a device. Immutable for the life
of the install and used as the echo/loop key for clipboard updates.
_Avoid_: device name, peer id (transient)
**Identity key**
A device's long-term key pair. The public half is exchanged at pairing and
pinned in the trust record; it authenticates every session.
_Avoid_: public key (ambiguous — could mean ephemeral)
**Pairing**
The one-time process that establishes mutual trust between two devices: an
authenticated key-exchange handshake plus verification of a short
authentication string derived from the handshake. Produces a pinned trust
record on both sides.
_Avoid_: linking, connecting
**SAS (Short Authentication String)**
A human-comparable code both devices derive from the handshake transcript. The
user confirms both sides show the same string to defeat a man-in-the-middle.
_Avoid_: PIN, pairing code, OTP
**Trust record**
The persisted per-peer entry proving a completed pairing: peer device ID,
pinned identity public key, paired-at time, SAS-verified flag, display name,
last-seen time.
_Avoid_: credential, token
**Peer**
A device for which a trust record exists (i.e., already paired).
_Avoid_: client, node, endpoint
**Session**
A single authenticated channel between two paired peers over one TCP
connection. Re-established on every (re)connect using the pinned identity keys.
## Sync Propagation
**Origin device**
The device that first produced a clipboard change. Immutable as the update is
fanned out; used for echo prevention.
_Avoid_: sender (ambiguous — forwarders also send)
**Sequence**
A persistent, monotonic per-device counter tagging each clipboard update.
Together with origin device ID it forms the loop-prevention key.
_Avoid_: nonce, index
**Fan-out**
The propagation rule: a device sends each local clipboard change to every
currently-connected trusted peer.
**Loop prevention**
Dropping a clipboard update when its origin device ID equals the local device
ID, or when its (origin device ID, sequence) pair is not newer than the
last-seen pair for that peer.
**Ordering**
The deterministic total order `(timestamp_ms, origin device ID)` used to decide
which clipboard update wins when changes conflict; bounded by a clock-skew
guard.
## Protocol & Discovery
**CLIP_UPDATE**
A message carrying one clipboard change: origin device ID, content, content
type, content hash, sequence, timestamp. Text only in MVP; content type is
extensible.
**Discovery**
How unpaired devices find each other: one side announces over UDP and the
other listens, with manual IP:port entry as a fallback when announcement is
blocked.
**Clipboard backend**
The component that reads and writes the operating-system clipboard on the
desktop side; reaches the clipboard through the platform's sanctioned API
rather than raw display-server access.
+8
View File
@@ -0,0 +1,8 @@
# Transport security uses Noise, not TLS
We secure the session with the Noise protocol (an `XX` handshake) using each
device's long-term identity key, rather than mutual TLS with per-device certs.
Noise avoids a CA/cert machinery on a peer-to-peer LAN, maps cleanly onto the
SAS pairing flow, and is light enough for the Odin daemon and Android
(Conscrypt). The pinned trust record and the session key are the *same* key,
simplifying the model.
+7
View File
@@ -0,0 +1,7 @@
# Sync topology is fan-out to all trusted peers
A clipboard change is sent to every currently-connected paired peer, not just
one. The trust store already models a list of peers, and restricting to 1:1
later would be a painful product reversal. Fan-out makes the system
many-to-many by construction; correctness then rests entirely on loop
prevention (see ADR-0003).
+9
View File
@@ -0,0 +1,9 @@
# Loop prevention keys on (origin device ID, sequence), not content hash
The original protocol draft used `content_hash` recent-history dedup as the
loop breaker. That is wrong for many-to-many: two different devices pasting
the same text are falsely dropped as echoes, and the hash is optional so the
gate is bypassable. The loop breaker is now the immutable origin device ID plus
a per-peer monotonic sequence: ignore updates whose `(origin, sequence)` is not
newer than the last-seen pair, and drop any update whose origin is the local
device. `content_hash` is retained only for integrity/diagnostics.
+8
View File
@@ -0,0 +1,8 @@
# Pinned-key mismatch forces a full re-pair
If a peer reconnects with a static key different from the pinned one, the
session is rejected and the peer is treated as unpaired; a fresh handshake plus
SAS verification is required before the pinned key is updated. Silently
accepting a changed key would void the entire purpose of SAS verification (a
LAN MITM who knows the peer's device ID could swap the key). Reinstalling an
app therefore means a new identity and a new pairing.
@@ -0,0 +1,8 @@
# Android reads the background clipboard via AccessibilityService
Android 10+ blocks background apps from reading the primary clip, which breaks
the "send local copy to desktop" half of bidirectional sync. We use an
AccessibilityService to read the clipboard in the background; if the user denies
it, the app degrades gracefully to receive-only plus manual send (share/quick
tile). Applying a received clip via `setPrimaryClip` needs no extra permission
and always works.
@@ -0,0 +1,9 @@
# Desktop clipboard uses the xdg-desktop-portal Clipboard API
The desktop daemon reaches the clipboard through `xdg-desktop-portal` rather
than native X11 calls. Wayland — the default session on modern Fedora/Ubuntu —
exposes no direct clipboard access, so an X11-only daemon is broken for most
Linux users. Consequence: on Wayland the daemon cannot passively poll; an active
portal session started by a user gesture is required, so "enable clipboard
sync" is an explicit action (trivial no-op grant on X11). Windows uses the
Win32 clipboard API.