git.lucas.co / cce-remote
remote trackpad and keyboard server
git clone https://git.lucas.co/cce-remote.git

README.md (3.4K)

 1 # cce-remote
 2 
 3 Use a phone as a trackpad + keyboard for the cce desktop.
 4 
 5 A single small server: it serves an embedded web page (touch trackpad +
 6 keyboard UI) over HTTP on your tailnet and bridges the page's WebSocket input
 7 events into the compositor's control socket — the same channel `ccectl`
 8 uses, so injection rides the real compositor input path.
 9 
10 ## Run
11 
12 ```sh
13 make install        # installs cce-remote to ~/.local/bin
14 cce-remote          # serves loopback + the tailnet on :17017 (or: cce-remote <port>)
15 cce-remote --lan    # every interface, plain HTTP (or CCE_REMOTE_LAN=1)
16 ```
17 
18 Open `http://<this-machine's-Tailscale-address>:17017` on the phone (its
19 100.x address or MagicDNS name). Add it to the Home Screen for a fullscreen
20 app feel. A connection from anywhere else gets a 403 saying so.
21 
22 ## Controls
23 
24 - one-finger drag — move the pointer
25 - tap — left click; two-finger tap — right click
26 - two-finger drag — scroll (natural direction)
27 - press-and-hold, then drag — held drag (release on lift)
28 - `left` / `right` buttons — explicit clicks
29 - top bar — esc/tab/arrows; ctrl/alt/sup are sticky toggles (tap to hold,
30   tap again to release — chords work: ctrl on, tap `c`, ctrl off)
31 - keyboard button — summon the phone keyboard (typing goes through a US-layout
32   char→evdev map; iOS `beforeinput` is used, so autocorrect noise is
33   filtered)
34 - windows button (three bars) — window switcher: tap a window to focus it
35 - menu button (three dots) → live view — window view mode: a live stream of the focused window, delivered
36   ack-clocked over a WebSocket — at most one frame in flight, so a slow
37   link drops frame rate instead of falling behind — with resolution and
38   quality adapting to the measured link (up to 1400px edge when it's fast).
39   Frames come from the compositor's damage-driven window stream, falling
40   back to wlr-screencopy, then grim; `/stream` remains as a curl-friendly
41   MJPEG debug endpoint.
42   Input is identical to the trackpad — tap = click, two-finger tap = right
43   click, press-and-hold = held drag, one-finger drag = pointer motion (a
44   cyan ring marks the cursor — compositor frames carry none); taps never
45   warp the pointer. Two fingers pinch-zoom / pan the view itself. Toggle
46   again for the trackpad. Both `/stream` and the one-shot
47   `/shot` endpoint are PIN-gated; nothing accumulates on disk.
48 
49 ## Security
50 
51 Pairing PIN: a persistent 6-digit PIN is generated on first run (printed at
52 startup, stored 0600 in `~/.config/cce/cce-remote.pin`). The page asks for
53 it once per device and remembers it (localStorage); the server closes any
54 WebSocket whose first frame isn't `auth <pin>`, so no input can be injected
55 without pairing. Delete the PIN file to rotate it.
56 
57 Wrong PINs are rate-limited per source address — five in a row, then one more
58 every 30 seconds (HTTP replies `429 Too Many Requests`) — and globally, twenty
59 in a row across every address, then one per 30 seconds, so the 6-digit space
60 can't be walked from one address or from many. Correct PINs cost nothing and a
61 success clears the peer's record, so a phone reconnecting its stream is never
62 throttled — unless someone has drained the global budget, which locks everyone
63 out until it refills.
64 
65 Traffic is plain HTTP, so the PIN crosses the wire in clear. That is why only
66 loopback and the tailnet (100.64.0.0/10, fd7a:115c:a1e0::/48) are served by
67 default: WireGuard encrypts the tailnet end to end. `--lan` serves every
68 interface; use it only on a network you trust.