Secure cross-device clipboard sync with end-to-end encryption and a Raspberry Pi relay

Secure cross-device clipboard sync with end-to-end encryption and a Raspberry Pi relay

UUnknown
2026-02-13
12 min read
Advertisement

Build a private, end-to-end encrypted clipboard sync that uses a Raspberry Pi relay — no third-party storage, TLS for transport, explicit pairing.

Hook: If you worry about clipboard data leaking to third-party clouds, juggling fragments across devices, or losing sensitive snippets when you switch contexts, this guide shows how to build a private, seamless clipboard sync with end-to-end encryption — using a Raspberry Pi 5 at home as a dumb relay so your server never sees plaintext.

The idea in one sentence

Encrypt clipboard items on each device with keys you control; push the encrypted blobs to a Raspberry Pi relay exposed to the internet (TLS-protected). The Pi stores and forwards only ciphertext and metadata. Devices fetch, decrypt, and apply snippets locally — the relay cannot read your data.

Why this matters in 2026

  • Privacy-first workflows are mainstream: post-2025 regulation and public concern make keeping sensitive content off third-party infrastructure more important than ever.
  • Raspberry Pi 5 and new accessories (AI HAT+2) make home servers powerful enough to run secure relays and lightweight local AI for PII detection and snippet summarization.
  • End-to-end encryption libraries (libsodium, PyNaCl) and modern curves (X25519/Ed25519) are stable, fast and broadly supported — ideal for secure clipboard sync.

Threat model & design goals

Design your relay with clear goals and limits:

  • Goal: Never let the relay see plaintext clipboard data.
  • Goal: Allow seamless syncing across multiple devices (desktop, laptop, mobile).
  • Threat: Relay compromise — attacker can read stored metadata and ciphertext, replay or delete messages, but not decrypt payloads.
  • Threat: Network-level attackers — mitigate with TLS for metadata and initial pairing.
  • Non-goal: Protect against device compromise (if someone owns your device, they can access decrypted clipboard data on that device).

High-level architecture

  1. Each device holds a long-term identity keypair (Ed25519 for signatures) and an encryption key (X25519 derived shared secrets or a vault symmetric key).
  2. Pairing: add new devices by scanning a QR code from an authorized device (transfers the device public key and encrypted vault key over an offline channel).
  3. When clipboard changes, the client encrypts the snippet with an ephemeral key + recipient symmetric key (or uses a shared vault key) and pushes to the Pi Relay using HTTPS/TLS.
  4. The relay stores ciphertext, metadata (sender id, timestamp) and serves it to authorized devices on fetch requests.
  5. Receiving devices fetch encrypted blobs, verify signature/MAC, decrypt locally and push to the local clipboard.

Why combine E2EE and TLS?

Use both. E2EE ensures the relay cannot read payloads. TLS ensures authenticity of the relay and prevents active network-level attacks during pairing and key exchange. TLS also reduces metadata manipulation risk during transport.

Components you'll build

  • Pi relay: small Flask (or FastAPI) service behind NGINX, Certbot for TLS, SQLite for encrypted blob metadata.
  • Client CLI (Python): clipboard watcher using pyperclip/wl-clipboard/xclip + PyNaCl for encryption + requests for HTTPS transport.
  • Pairing flow: QR code printed from an authorized device that carries the vault key encrypted to the new device key or a manual USB/QR secret transfer.
  • Optional: local AI on Pi 5 to scan and redact high-risk items before storing (metadata-only actions — do not decrypt content on the Pi unless you explicitly accept that risk).

Step 1 — Prepare the Raspberry Pi relay

Hardware & OS (2026 best practice)

Use a Raspberry Pi 4 or Pi 5. If you plan on running local AI features later, Pi 5 + AI HAT+2 (2025/2026 hardware) is recommended. Use a minimal, secure distro — Ubuntu Server or a current Raspberry Pi OS with automatic security updates.

Install prerequisites

On the Pi, install NGINX, Certbot, Python 3.11+, and create a system user for the relay:

sudo apt update
sudo apt install nginx python3-pip python3-venv certbot python3-certbot-nginx sqlite3
sudo adduser --system --group --no-create-home cliprelay

Deploy a simple relay app

Relay app responsibilities:

  • Accept POST /v1/put (auth token + ciphertext blob + metadata)
  • Serve GET /v1/list and GET /v1/get?id=abc for authorized devices
  • Store only ciphertext and non-sensitive metadata (sender id, device id, timestamp)

Example minimal Python (Flask) skeleton — run as a systemd service. This relay does not hold keys and does not attempt to decrypt:

from flask import Flask, request, jsonify, abort
import sqlite3, os, time

app = Flask(__name__)
DB = '/var/lib/cliprelay/clip.db'

def init():
    os.makedirs(os.path.dirname(DB), exist_ok=True)
    c = sqlite3.connect(DB)
    c.execute('''CREATE TABLE IF NOT EXISTS blobs(id TEXT PRIMARY KEY, sender TEXT, device TEXT, ts INTEGER, blob BLOB)''')
    c.commit()
    c.close()

@app.route('/v1/put', methods=['POST'])
def put_blob():
    token = request.headers.get('Authorization')
    # validate token (API key) -- relay-only auth for rate limiting
    if token != 'Bearer YOUR_RELAY_API_KEY':
        abort(401)
    data = request.get_json()
    id = data['id']
    sender = data['sender']
    device = data['device']
    ts = int(time.time())
    blob = data['blob']
    c = sqlite3.connect(DB)
    c.execute('INSERT OR REPLACE INTO blobs(id, sender, device, ts, blob) VALUES (?,?,?,?,?)',(id,sender,device,ts,blob))
    c.commit(); c.close()
    return jsonify({'ok':True})

@app.route('/v1/list', methods=['GET'])
def list_blobs():
    # API key auth
    token = request.headers.get('Authorization')
    if token != 'Bearer YOUR_RELAY_API_KEY':
        abort(401)
    c = sqlite3.connect(DB)
    rows = c.execute('SELECT id, sender, device, ts FROM blobs ORDER BY ts DESC LIMIT 100').fetchall()
    c.close()
    return jsonify([{'id':r[0],'sender':r[1],'device':r[2],'ts':r[3]} for r in rows])

@app.route('/v1/get', methods=['GET'])
def get_blob():
    token = request.headers.get('Authorization')
    if token != 'Bearer YOUR_RELAY_API_KEY':
        abort(401)
    id = request.args.get('id')
    c = sqlite3.connect(DB)
    row = c.execute('SELECT blob FROM blobs WHERE id=?',(id,)).fetchone()
    c.close()
    if not row: abort(404)
    return jsonify({'blob': row[0]})

if __name__ == '__main__':
    init()
    app.run(host='127.0.0.1', port=5000)

Proxy this with NGINX and add standard rate limiting. Use Certbot to get a Let's Encrypt certificate for your dynamic DNS name (DuckDNS, No-IP). TLS protects metadata during transport and verifies the relay's identity.

Step 2 — Device keys, pairing, and key management

Core principle: The relay is dumb. Keep all secret keys on your devices.

  • Each device has an Ed25519 keypair for signing and an X25519 keypair (derived) for encryption.
  • All devices share a single symmetric vault key encrypted on-device; this vault key encrypts clipboard items for distribution to all devices. The vault key is the user’s secret — transfer it only when pairing a new device.
  • Pairing is an explicit user action: export the vault key from an authorized device, encrypt it for the new device and scan/upload via QR/USB or use an offline channel. Do not use the relay for untrusted key transfers unless they are signed by an authorized device.

Pairing flow (simple, secure)

  1. On Device A (authorized), export the vault key and create a QR code that contains: {vault_key_encrypted_to_new_device_pub, device_A_signature}.
  2. On Device B (new), scan QR, verify signature with Device A's public key, if valid, store vault key locally.
  3. Now Device B can decrypt blobs encrypted with the vault key.

Why this avoids relay trust

Relay sees only public keys and ciphertext. If an attacker attempts to add a new device by injecting a public key on the relay, they still cannot receive decrypted content unless you explicitly pair and give them the vault key (signed QR). This preserves user control over device membership.

Step 3 — Client: encrypt, sign and push

Below is a concise Python example using PyNaCl (libsodium bindings) and pyperclip. This example uses a symmetric vault key (32 bytes) with authenticated encryption (SecretBox). Each blob will be sealed with a nonce and an HMAC-like MAC via SecretBox.

pip install pynacl pyperclip requests qrcode[pil]

# client.py (simplified)
import nacl.secret, nacl.utils, base64, uuid, time, pyperclip, requests

VAULT_KEY = b'32_byte_random_vault_key____placeholder__'
RELAY = 'https://yourdomain.example'
API_KEY = 'Bearer YOUR_RELAY_API_KEY'

box = nacl.secret.SecretBox(VAULT_KEY)

def push_clip(text):
    nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE)
    ciphertext = box.encrypt(text.encode('utf-8'), nonce)
    id = str(uuid.uuid4())
    payload = {'id': id, 'sender':'alice@local','device':'alice-mac','blob': base64.b64encode(ciphertext).decode('ascii')}
    r = requests.post(RELAY + '/v1/put', json=payload, headers={'Authorization':API_KEY}, timeout=5)
    return r.ok

if __name__ == '__main__':
    last = ''
    while True:
        txt = pyperclip.paste()
        if txt and txt != last:
            push_clip(txt)
            last = txt
        time.sleep(0.5)

Receiving side will poll /v1/list, fetch new ids via /v1/get, decode base64, decrypt with the same vault key and push to local clipboard.

Step 4 — Secure pairing examples

Example: export vault key from Device A into a QR encoded as an encrypted blob for Device B’s public key. Use Ed25519 to sign the QR payload so the new device can verify authenticity before consuming the vault key.

# PSEUDO: Device A
from nacl.signing import SigningKey
from nacl.public import PublicKey, SealedBox
import qrcode

signing_key = SigningKey.generate()
device_a_pub = signing_key.verify_key.encode()

# new_device_pub is collected offline from Device B
sealed = SealedBox(PublicKey(new_device_pub)).encrypt(vault_key)
sig = signing_key.sign(sealed)
# QR contains base64(sig) and device_a_pub so B can verify and extract vault_key
qrcode.make(base64.b64encode(sig)).save('pair_qr.png')

Step 5 — Operational security and hardening

  • API keys: Relay accepts authenticated requests so random internet hosts can’t spam it. Keep relay API keys for rate-limiting only; they are not used for E2EE.
  • Replay protection: Include per-blob IDs and timestamps. Clients reject blobs older than a configurable window or with duplicate IDs.
  • Rate limits: Use NGINX rate limiting per API key to avoid abuse.
  • Key rotation: support vault key rotation by adding new blobs encrypted with the new key and signed by an authorized device. Old devices should be migrated via pairing.
  • Data retention: Configure the Pi to auto-delete blobs older than X days to reduce metadata exposure. Use SQLite with vacuum and an encrypted filesystem if necessary.
  • Pi hardening: enable auto-updates, firewall (ufw), fail2ban, and run the relay under a non-privileged account; isolate via systemd and restrict file permissions.

Step 6 — Privacy enhancements & local AI (2026)

With Pi 5 + AI HAT+2 you can run small LLMs or classification models locally to autoflag or redact PII before storing metadata or before optionally decrypting on the Pi. Important: do not decrypt user blobs on the Pi unless you accept the risk.

  • Run a local classifier to detect credit card numbers, SSNs and sensitive tokens — trigger automatic blob expiry or local device notifications.
  • Summarize long snippets with a local LLM (running entirely offline) and store only the summary as metadata to help find entries without exposing full content.

Operational considerations & performance

  • Clipboard items are small; storage and bandwidth are negligible for typical use. SQLite is sufficient and simple.
  • Ensure TLS session resumption and keep-alive on the Pi for low-latency transfers when rapidly updating clipboard items.
  • For mobile clients, implement exponential backoff and push notifications (optional) rather than constant polling to conserve battery and consider backup power strategies if you rely on local infrastructure during outages.

Security pitfalls to avoid

  • Do not store vault keys on the Pi.
  • Avoid automatic device pairing via the relay without explicit offline confirmation.
  • Beware of clipboard-sniffing malware on endpoints — E2EE only protects data in transit and at rest on the relay, not on compromised devices.
  • Keep TLS certs current; an expired certificate undermines user trust during pairing.

Recoverability and device revocation

If a device is lost, immediately rotate the vault key from an authorized device and re-pair trusted devices. Because the relay only stores ciphertext, rotating the vault key prevents the lost device from decrypting future blobs. For archival recovery, keep an encrypted backup of the vault key secured by a strong passphrase or an offline hardware key.

Developer deep-dive: Authenticated encryption choices (short)

Use libsodium-style primitives (SecretBox, crypto_box) or age/Crybox patterns. For multi-device sharing, symmetric vault key + SecretBox is simple and fast. If you prefer per-recipient encryption (no shared vault key), encrypt per-recipient with crypto_box (X25519 + ephemeral key), but that increases CPU and storage costs.

Recommendation (pragmatic): start with a symmetric vault key and explicit pairing. It balances simplicity, security and UX for content creators and publishers who need frictionless sync.

Example NGINX snippet (TLS & proxy)

server {
    listen 80;
    server_name clip.example.com;
    location /.well-known/acme-challenge/ { root /var/www/certbot; }
}

server {
    listen 443 ssl;
    server_name clip.example.com;
    ssl_certificate /etc/letsencrypt/live/clip.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/clip.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        limit_req zone=one burst=5 nodelay;
    }
}

Monitoring, logging & privacy

Log only what you need. Avoid logging full ciphertext blobs. Keep access logs minimal, rotate frequently and restrict access. If you require auditability, store only hashes of blob IDs (HMAC with server-side key) so you can prove existence without exposing content.

  • Expect more hardware-accelerated crypto on edge devices (Pi 5 and successors). Use them for faster encryption and local AI tasks.
  • On-device LLMs will enable richer local processing (PII detection, snippet summarization) without sending sensitive content to third parties — see on-device AI playbooks for guidance.
  • Decentralized identity (DID) and hardware-backed keys (secure elements) will simplify secure pairing and revocation — consider integrating when stable standards land in mainstream OSes.

Quick checklist before you go live

  • TLS (Let's Encrypt) configured & auto-renew working
  • Systemd service for relay with restricted permissions
  • API key enforced for relay endpoints
  • All client devices have vault key stored encrypted and backup exists
  • Pairing requires explicit user action (QR / USB)
  • Retention policy and auto-delete configured (see storage guidance: storage costs & retention)

Actionable takeaways

  • Start small: deploy the relay on a Pi, test with two devices and a shared vault key.
  • Pair explicitly: use QR or USB transfer for vault key distribution — do not rely on the relay for trust establishment.
  • Use E2EE + TLS: E2EE protects content; TLS protects transport and pairing authenticity.
  • Harden the Pi: firewall, updates, minimal logs and API rate limiting.
  • Plan for revocation: rotate vault keys on device loss and re-pair trusted hardware.

By running a Raspberry Pi relay you keep full control of your clipboard data while retaining the seamless multi-device sync you rely on. The architecture above is flexible: you can later add per-recipient encryption, hardware-backed keys, or local AI processing on Pi 5-class hardware for smarter privacy-preserving automation.

Call to action: Try the approach with a Pi and two devices this weekend. Start with the minimal symmetric-vault flow, test pairing via QR, and iterate. If you want, clone the starter scripts into your own repo, harden the Pi, and share your setup with your team to get consistent, private clipboard sync across all your devices.

Advertisement

Related Topics

U

Unknown

Contributor

Senior editor and content strategist. Writing about technology, design, and the future of digital media. Follow along for deep dives into the industry's moving parts.

Advertisement
2026-02-15T08:58:15.057Z