UniClipboard

Mobile sync

How to sync clipboard between desktop and an iPhone or any SyncClipboard-compatible client — on the LAN, or across networks via a server node or Tailscale.

Edit on GitHub

Mobile sync is an HTTP companion mode (introduced in 0.7): the desktop daemon exposes a small SyncClipboard-compatible HTTP service, and a paired phone reads / pushes clips to it with HTTP Basic Auth.

It is intentionally narrower than desktop ↔ desktop sync:

  • Not P2P. The phone is a plain HTTP client — no NAT punching, no relay. It works on your LAN out of the box; for cross-network reach, run a headless server node (a public HTTPS endpoint) or use a Tailscale / VPN overlay (see below).
  • Plain HTTP + Basic Auth at the listener — TLS is added by a reverse proxy or the overlay tunnel, not the listener itself. On a bare LAN, only enable on networks you trust.
  • Phone is not a peer of the space — it doesn't get a node ID and doesn't see the encrypted history database.

Two phones don't sync with each other; they each talk to the desktop.

v1 wire format is unencrypted HTTP on the LAN. Don't enable the listener on coffee-shop Wi-Fi, conference Wi-Fi, or any network where you don't trust everyone on the same SSID.

Enable mobile sync (one-shot)

The fastest way is the wizard — flip the feature on, configure the LAN listener, register the phone, and print a QR code in one go.

Open the Devices page

The page has a "This device" Hero on top and a row of two tabs: Paired devices and Mobile sync. Switch to the Mobile sync tab; two action buttons appear on the right of the tab bar: Configure and Add device. If the listener fails to bind, Add device stays clickable and clicking it surfaces the reason as a toast — the full detail is inside the Configure modal.

Click Configure

A centered "Mobile sync settings" dialog opens. Under Sync switches, turn on Enable mobile sync and LAN listener. Then under Network parameters:

  • Bind IP — leave on Auto to have the desktop offer every RFC1918 candidate at copy time, or pick a specific interface to fix the advertised URL to one address. Tailscale CGNAT addresses (100.64.0.0/10) are included in the dropdown so a Tailscale- reachable phone can be paired the same way as a LAN phone; this is independent of the Settings → Network → Allow overlay network addrs toggle, which only affects desktop ↔ desktop pairing. Either way the actual socket always binds to 0.0.0.0; this picker only controls which address gets advertised to the mobile device.
  • Port — defaults to 42720. Change only if there's a conflict.

The dialog shows the current listening address as you go. If Bind IP is Auto, that row expands inline into every candidate LAN IPv4 — copy the one the mobile device can dial. If a specific interface is picked, a single URL is shown. The LAN listener is hot-swapped: changes apply immediately, no daemon restart needed.

If the daemon raises lan_listener_error (port in use, permission denied, etc.), a red Alert at the top of the dialog spells out the reason; clicking Add device surfaces the same reason as a toast while the listener can't bind.

Click Add device

Pick a label (e.g. "My iPhone 15"). The desktop mints a one-time username and password and shows them inside a single scan-first credentials modal:

  • Big QR (top, always on) — encodes every reachable candidate address (the public entry if configured, plus all qualifying interface IPs including Tailscale CGNAT) together with the credentials as a single uniclipboard://connect URI, so one code works whether the phone is on the LAN or coming in from outside; clients that understand the candidate list probe them in order. Scan it from the UniClipboard iOS App (TestFlight public beta — recommended; see Pair an iPhone below), the UniClipboard Android client, or any other SyncClipboard-protocol client. All fields auto-fill; no typing. A baseUrl dropdown sits next to the QR so you can switch which address is the preferred (first) candidate — the remaining candidates stay in the code — without re-opening the Configure modal.
  • "Haven't installed a client?" disclosure (collapsed by default) — expand it when the phone has no client yet. Inside are two tabs: iOS shows a QR for the TestFlight invite (with a Shortcut install QR as a sub-fallback for users who can't or won't use TestFlight), and Android shows a QR for the GitHub Releases APK download page.
  • "Credentials" disclosure (amber, collapsed by default) — expand it to reveal the raw username / password for clients that need manual entry. A Backup button copies a Server / Username / Password block to the clipboard in one go.

Closing the modal (the Done button, the X icon, Esc, or clicking outside the dialog) all keep the device — there's no in-modal discard path. To revoke a device, open it from the Mobile sync device grid instead (see Manage paired devices).

The plaintext password is shown once. The desktop only retains an Argon2id hash after the modal closes.

# One-shot: enable feature + LAN listener, register one device,
# print install QR + a one-time password.
uniclip stop                                 # required: writes refuse a running daemon
uniclip mobile setup                    # interactive (recommended)

# Or non-interactive (e.g. CI / scripts)
uniclip mobile setup \
    --label "My iPhone 15" \
    --accept-network-risk \
    --non-interactive
# → prints baseUrl, username, password (one-time), installUrl, and a
#   multi-address QR (ASCII) that the phone probes address-by-address

uniclip start                                # bring the daemon back up

For non-interactive runs, --label and --accept-network-risk are required. --ip / --port are optional advanced pins — omit them and the QR carries every detected interface, so there is normally nothing to pick. --username / --password-stdin are optional and default to auto-mint.

The plaintext password is shown once. If the user loses it, click the device card on the Mobile sync grid, press Edit device inside the dialog and set or regenerate the password (or uniclip mobile revoke <id> + add) — there is no way to retrieve the original.

Pair an iPhone

There are two iOS options. The UniClipboard iOS App is the recommended path; the Shortcut stays as a fallback.

iOS (UniClipboard App — TestFlight public beta)

The UniClipboard iOS App is currently distributed through TestFlight as a public beta. Invite link:

https://testflight.apple.com/join/nyNQ8dQe

Install TestFlight from the App Store

On your iPhone, open the App Store, search for TestFlight, and install it. TestFlight is Apple's official beta-distribution app and is required to install the UniClipboard beta build.

On the iPhone, open the TestFlight invite link:

https://testflight.apple.com/join/nyNQ8dQe

iOS will hand the link off to the TestFlight app. Tap Accept, then Install, and UniClipboard will be added to the Home Screen.

Generate credentials on the desktop Add device

Back on the desktop, follow the flow above to click Add device under Devices → Mobile sync. The credentials modal opens directly on the connect-URI QR — base URL, username, and one-time password are encoded into a single scan. (If you haven't installed the App yet, expand the "Haven't installed a client?" disclosure for a TestFlight QR; otherwise just scan the big QR from the App right away.)

Enter the connection info in the App

Open the UniClipboard iOS App, tap Add server, and scan the big QR from the desktop credentials modal. The base URL, username, and password are filled in automatically; just confirm and save. Once connected, the iPhone syncs clipboard with the desktop in both directions.

If the install or first launch hangs, turn off your proxy / VPN client first. Some users on Loon, Surge, Shadowrocket, Clash, or similar tools see one of these symptoms:

  • TestFlight shows a certificate or HTTPS error when opening the invite link, or
  • TestFlight can't reach App Store Connect and the Install button spins forever.

Temporarily disable the proxy (global rules, TUN mode, HTTPS decryption / MitM included) so TestFlight goes direct. Re-enable it once the App is installed — the App itself only talks to your desktop over the local network and doesn't depend on the proxy.

iOS (Shortcut — fallback, no UniClipboard App)

If TestFlight isn't an option right now, the Shortcut path still works. In the credentials modal, expand the "Haven't installed a client?" disclosure, stay on the iOS tab, and look for the "Don't want the App? Scan to install the Shortcut as fallback" sub-QR.

  1. Scan the Shortcut install QR with the iPhone Camera (or open the install link in Safari). iOS prompts to install UniClipboard.shortcut.
  2. Open the installed Shortcut and manually paste the base URL, username, and password (from the Credentials amber disclosure at the bottom of the modal — use its Backup button to copy all three in one go) into the Shortcut's matching fields. The Shortcut QR only carries the install link — it does not prefill credentials.
  3. Run the Shortcut (or trigger it from the iOS share sheet) to

Android / other clients

The UniClipboard Android client lives at github.com/UniClipboard/UniClip — one client for both iOS and Android, with Android APKs published in releases (side-load only, no Google Play). Because the desktop listener speaks the SyncClipboard protocol, any other SyncClipboard-compatible client also works. Recommended picks today:

  • UniClipboard/UniClip — our official app (the same client as on iOS): install the APK, enter the base URL, username, and password, and clipboard sync goes both ways. Recommended for most Android users.
  • Upstream SyncClipboard — the protocol's reference implementation. Primarily a desktop client, but Android builds are published in its releases. A reasonable pick if you already live in the SyncClipboard ecosystem or want to track upstream directly.

Either way, the connection settings are the same:

  • Base URL / server addresshttp://<advertised-IP>:<port> (e.g. http://192.168.1.5:42720). Note http://, not https:// — the listener itself has no TLS in v1.
  • Username — printed by the credentials modal / setup.
  • Password — printed by the credentials modal / setup (one-time).

Public access via an HTTPS reverse proxy. For deployments that expose mobile sync over the public internet (e.g. a headless server node), put a TLS reverse proxy such as Caddy or nginx in front of the plain-HTTP listener and advertise its address instead of an internal IP:

uniclip mobile network set \
    --url https://clip.example.com \
    --accept-network-risk

The install URL / QR then encodes https://clip.example.com (no internal port), while the listener stays plain HTTP on the internal network. The proxy terminates TLS and forwards to the internal mobile_lan port, which must not be published directly to the public internet. The --ip <IP> form instead produces the LAN http://<IP>:<port> URL; the two are mutually exclusive.

The UniClipboard mobile app speaks the same SyncClipboard-compatible protocol as the desktop listener. Bugs in the app itself are usually best filed at UniClipboard/UniClip, while protocol-side bugs on the desktop listener belong here in the main repo.

Android limitations today. No TLS at the listener (plain HTTP + Basic Auth — TLS comes from a reverse proxy or overlay), same-LAN by default (cross-network via a server node or Tailscale), not a peer of your space's trust mesh, and Android 10+ background-clipboard restrictions can delay or gate writes when the app is not in the foreground.

Cross-network sync via Tailscale

Put the desktop and phone on the same Tailscale network (tailnet) and each gets a 100.x.x.x address — the phone can reach the desktop from cellular or a remote Wi-Fi exactly as if they shared a LAN, with no VPS and no domain. Since 0.12 the Bind IP dropdown also lists 100.64.0.0/10 (Tailscale's CGNAT range).

For the full walkthrough, caveats, and troubleshooting see Cross-network sync with Tailscale. The other cross-network path is a self-hosted server node, which exposes the gateway as a public HTTPS endpoint; that page also compares the two.

This only changes reachability, not the v1 HTTP wire format. The listener still speaks plain HTTP + Basic Auth; confidentiality is provided by the Tailscale / WireGuard tunnel you trust. It also does not close the listener on your LAN — the socket always binds 0.0.0.0, and "Bind IP" only picks which address is advertised to the phone.

Manage paired devices

ActionGUICLI
List paired devicesDevices page → Mobile sync paneluniclip mobile status
Add another deviceMobile sync panel → Add deviceuniclip mobile add --label "…"
Edit a deviceClick device card → dialog → Edit device (label / username / password)GUI only today; mint a new one and re-pair the phone
Revoke a deviceClick device card → dialog → Revokeuniclip mobile revoke <device-id>
Disable featureConfigure dialog → switch offuniclip mobile disable
Inspect listener URLMobile sync panel headeruniclip mobile status

Editing lets you change the label, username, and/or password from the Edit device view. Setting or regenerating the password mints a fresh one-time password and atomically invalidates the previous Argon2id hash; changing the username reissues the password too. The old credentials stop working immediately, so update the phone (UniClipboard iOS App, Shortcut, or other client) with the new credentials.

Revoking removes the device record. Any subsequent request with the old credentials gets 401 Unauthorized.

Disabling the feature turns the master switch and listener off together but leaves paired-device records in place — re-enable later and the same usernames continue to work. To wipe paired records, use revoke (or the GUI revoke button) explicitly.

Security model — what's covered, what isn't

SurfaceCovered
On-disk password storageYes — only an Argon2id hash is persisted server-side.
Credential rotationYesRotate password mints a new one and invalidates the old hash.
LAN-side wire confidentialityNo (v1) — plain HTTP. TLS comes in v2.
Cross-network reachabilityLAN by default — extend it with a server node (public HTTPS) or a Tailscale / VPN overlay (below).
Mobile-as-peer trustNo — phone never participates in the iroh / space trust mesh.

Practical implications:

  • Treat the LAN listener like an SSH server on a private network: be selective about where you turn it on.
  • Public Wi-Fi: keep the listener disabled. Re-enable on a trusted network when you actually want the integration.
  • Server node: for always-on, cross-network access from anywhere (without putting every device on a VPN), run a headless server node that fronts the gateway with a TLS reverse proxy — full steps in Self-hosting a headless server node.
  • VPN / Tailscale: a VPN or Tailscale tailnet that puts both the desktop and the phone on the same virtual subnet (RFC1918 or Tailscale's 100.64.0.0/10) is a reasonable "LAN-style sync from outside the home" workaround — full steps in Cross-network sync via Tailscale. It's still plain HTTP on the wire; the VPN / WireGuard tunnel provides the confidentiality, so the overlay network itself must be trusted.

Troubleshooting

For the full checklist (phone can't open the URL, 401, broken images, what to do after changing the LAN IP), see Troubleshooting — Mobile sync (iOS Shortcut).

Where to look next

On this page