Skip to main content

Pairing & Encryption

Contop uses QR code pairing for out-of-band key exchange and WebRTC for end-to-end encrypted communication.

QR Code Pairing

The QR code serves as a secure out-of-band channel for exchanging connection details and cryptographic material.

QR Payload

The QR code contains a compact JSON payload with short keys for ~40% smaller QR codes:

KeyFull NameRequiredDescription
ttokenYesPairing token string
ddtls_fingerprintYesSHA-256 fingerprint of server's DTLS certificate
hhostYesServer LAN IP
pportYesServer port
eexpires_atYesToken expiration timestamp (ISO 8601)
cconnection_typeYes"permanent" or "temp"
ggemini_api_keyNoGemini API key for mobile use (omitted if empty or in subscription mode)
oopenai_api_keyNoOpenAI API key (if configured)
aanthropic_api_keyNoAnthropic API key (if configured)
ropenrouter_api_keyNoOpenRouter API key (if configured)
tstailscale_ipNoTailscale IP (if available)
ssignaling_urlNoSignaling URL (temp connections only)
paprovider_authNoCompact subscription flags (pa.g, pa.a, pa.o = "sub")

Pairing requires at least one API key or at least one provider configured in subscription mode. When a provider is in subscription mode, its API key is omitted from the QR payload (the mobile app doesn't need it — requests route through the desktop's CLI proxy instead). This means subscription-only users can pair without configuring any API keys.

DTLS Certificate

  • Ephemeral self-signed X.509 certificate (RSA 2048)
  • SHA-256 fingerprint in AB:CD:EF:... format
  • Generated via asyncio.to_thread() to avoid blocking
  • Fingerprint is embedded in the QR code for verification during WebRTC handshake

Token Management

Token Types

TypeTTLPersistenceUse Case
Permanent30 days~/.contop/tokens.json on diskYour personal devices
Temporary4 hoursIn-memory onlyGuest access, demos

Token Lifecycle

  1. GenerationPOST /api/pair creates a token with metadata
  2. Validation — Token verified on every WebRTC signaling connection
  3. Renewal — Permanent tokens reused if still valid (QR regenerated with fresh network info)
  4. RevocationDELETE /api/pair or per-device DELETE /api/pair?device_id=...
  5. Single active per device — Auto-revokes old token when same device_id re-pairs
  6. Per-device revokeDELETE /api/pair?device_id=... revokes a specific device's token and force-disconnects the session

Token Storage

  • Server: ~/.contop/tokens.json with 0o600 permissions (owner-only read/write)
  • Mobile: OS secure enclave via expo-secure-store (Keychain on iOS, Keystore on Android) — never in AsyncStorage

WebRTC Encryption

All data between phone and desktop is encrypted end-to-end:

ChannelEncryptionKey Exchange
Data channelsDTLS 1.2+Certificate fingerprint verified via QR
Video streamSRTPKeys derived from DTLS handshake

Certificate Fingerprint Verification

During the WebRTC handshake, the mobile app verifies that the server's DTLS certificate fingerprint matches the one embedded in the QR code. This prevents man-in-the-middle attacks even if the signaling channel is compromised.

Network Detection

The server detects available network paths for the QR code:

  1. LAN IP — Socket-based detection of local network address
  2. Tailscale IPtailscale ip -4 CLI or 100.x.y.z interface scan
  3. Tunnel URL — Cloudflare Tunnel URL (started on demand)

The mobile app uses connectSignalingWithFallback() to try LAN → Tailscale → Tunnel in order.

Mobile Device Metadata

The mobile app sends device metadata during pairing:

  • Device name — From expo-device (e.g., "iPhone 15 Pro")
  • Location — GPS from expo-location (if permission granted)

This metadata is stored alongside the token and displayed in the desktop's device management panel.


Related: Connection Methods · Device Management · REST API — Pairing