Skip to main content

beamboard — pasteboard sync

Self-hosted clipboard sync for devices on a Tailnet (or any private network). Three small programs (the two Python ones ship as one package, beamboard), one tiny HTTP+JSON protocol:

 macOS app  ──┐                      ┌── TrueNAS docker-compose
 (menubar,    ├── HTTP over Tailnet ─┤    pbd + SQLite
  pasteboard  │                      └── history capped at N clips
  sync)       │
 pb CLI    ───┘  (copy / paste / history / watch)

The server is the single source of truth; every clip gets a monotonically increasing seq. Clients are stateless and reconcile against seq. Everything is plain text, stdlib-only Python, and dependency-free Swift.

Server (TrueNAS / docker-compose)

cd server
docker compose up -d --build

Point the volume in docker-compose.yml at a dataset (e.g. /mnt/tank/apps/pb:/data, writable by uid 1000) for persistent history.

Or run it anywhere with Python: pipx install beamboard (from a checkout: pipx install .), then PB_DATA=./data pbd (alias: pb serve). No dependencies.

Env Default
PB_PORT 8737 listen port
PB_DATA ~/.local/share/pb (/data in Docker) directory for pb.db
PB_TOKEN (unset) optional shared bearer token; unset = no auth (trust the Tailnet)
PB_HISTORY 100 clips to keep
PB_MAX_BYTES 1048576 max clip size

CLI client (Linux or anywhere)

pipx install beamboard     # from a checkout: pipx install .   (or: make install)
pb config url http://truenas:8737   # saved to ~/.config/pb/config.json
# pb config token ...               # if the server sets PB_TOKEN

echo "text" | pb copy      # copy to the shared pasteboard
pb paste > file.txt        # paste from it (byte-verbatim)
pb history [n] [--json]    # recent clips
pb watch                   # print each new clip as it arrives (pipeable)

The same stdlib-only package (beamboard) provides the pbd server and the pb command, also installed as beamboard (beamboard/server.py and cli.py). pb copy/pb paste behave like macOS pbcopy/pbpaste; a symlink or alias named pbcopy/pbpaste pointing at pb restores the old spelling. pb config shows the effective settings; PB_URL, PB_TOKEN and PB_DEVICE override the config file (device defaults to the hostname). make check builds the sdist and wheel and validates them for PyPI.

macOS menubar app

make -C mac app        # builds BeamBoard.app (SwiftPM, no Xcode project)
make -C mac install    # copies it to /Applications

Open BeamBoard, click the clipboard icon in the menu bar, and set the server URL (and token) in the settings. The app then:

  • watches the system pasteboard (polls changeCount twice a second — the pasteboard has no change notifications) and pushes new local clips,
  • long-polls the server and writes clips from other devices into the system pasteboard,
  • shows the shared history in the menu bar; clicking an entry copies it.

It never overwrites your pasteboard at launch — syncing starts with the first actual change on either side. Requires macOS 13+. The build is ad-hoc signed; add it to Login Items for autostart.

Protocol

Clip: {seq: int, ts: float, mime: "text/plain", text: str, device: str}

Endpoint Behavior
GET /clip latest clip, 204 if empty
POST /clip {text, device} store new head, returns {seq}; identical to current head → deduped (returns existing seq)
GET /history?limit=50 recent clips, newest first
GET /watch?since=SEQ&timeout=30 long-poll: latest clip once head seq > SEQ, else 204 on timeout
GET /healthz 200 ok (never requires auth)

With PB_TOKEN set, all other endpoints require Authorization: Bearer <token>. Long-polling keeps every client a dumb HTTP GET loop — no streams, no state, self-healing across server restarts.

Echo loops are broken by three cheap layers: the server dedups identical consecutive text, the macOS app skips pasteboard changes it caused itself, and the device field lets clients ignore their own clips from /watch.

Security

There is no TLS and (by default) no auth: the design assumes the server is only reachable over a trusted private network such as a Tailnet. Set PB_TOKEN for a cheap second layer. Do not expose the port to the internet.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

beamboard-0.0.1.tar.gz (8.4 kB view details)

Uploaded Source

File details

Details for the file beamboard-0.0.1.tar.gz.

File metadata

  • Download URL: beamboard-0.0.1.tar.gz
  • Upload date:
  • Size: 8.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.15

File hashes

Hashes for beamboard-0.0.1.tar.gz
Algorithm Hash digest
SHA256 289d54311c8cb091b3e94d8ffea6c75f646ac220c1cb866d41df5632ed7c4d34
MD5 415aa8701b4f4b13ea625bc7c11e6c54
BLAKE2b-256 9b77ed79d7b98cb9b0f6ee5ed1efdefd35aa467875a714e530a1f2a238a885c0

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.0.1 This release

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page