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.

Metadata

Release files for beamboard 0.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for beamboard 0.0.1
File Size Uploaded Provenance
beamboard-0.0.1.tar.gz 8.4 kB Without provenance Details

Release files / beamboard-0.0.1.tar.gz

Download URL beamboard-0.0.1.tar.gz
Size 8.4 kB
Tags Source
SHA-256 checksum
How to use checksums
289d54311c8cb091b3e94d8ffea6c75f646ac220c1cb866d41df5632ed7c4d34
BLAKE2b-256 checksum
How to use checksums
9b77ed79d7b98cb9b0f6ee5ed1efdefd35aa467875a714e530a1f2a238a885c0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Security for beamboard 0.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.0.1 This release

1 release file No provenance

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