initial commit
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
# 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`
|
||||
|
||||
```json
|
||||
{
|
||||
"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`
|
||||
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Proposed Repository Layout
|
||||
|
||||
## Top-level
|
||||
|
||||
- `android-app/`
|
||||
- `desktop-daemon/`
|
||||
- `docs/`
|
||||
|
||||
## Android app layout
|
||||
|
||||
- `android-app/app/src/main/java/.../`
|
||||
- `discovery/` UDP discovery listener and peer cache
|
||||
- `pairing/` pairing workflow and trust store
|
||||
- `sync/` persistent session and clipboard sync engine
|
||||
- `service/` foreground/background lifecycle service
|
||||
- `ui/` peer list, pairing, status
|
||||
|
||||
## Desktop daemon layout (Odin)
|
||||
|
||||
- `desktop-daemon/src/main.odin`
|
||||
- `desktop-daemon/src/net/`
|
||||
- discovery broadcaster/listener
|
||||
- tcp session + framing
|
||||
- `desktop-daemon/src/protocol/`
|
||||
- message schemas and handlers
|
||||
- `desktop-daemon/src/pairing/`
|
||||
- key management + pairing flow
|
||||
- `desktop-daemon/src/clipboard/`
|
||||
- Linux backend
|
||||
- Windows backend
|
||||
- `desktop-daemon/src/storage/`
|
||||
- paired peers file/db persistence
|
||||
- `desktop-daemon/src/runtime/`
|
||||
- daemon loop, reconnect, retry policies
|
||||
|
||||
## Config files planned
|
||||
|
||||
- `desktop-daemon/clipsync.toml` (optional override)
|
||||
- Android app settings screen for default port and discovery toggles
|
||||
Reference in New Issue
Block a user