Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.reflecto.dev/llms.txt

Use this file to discover all available pages before exploring further.

Reflecto’s product promise is that the server is a dumb relay — it routes opaque encrypted blobs between your paired devices and never sees the plaintext. The public API has a single scoped carve-out: POST /v1/send accepts plaintext over TLS so the server can encrypt-and-forward to every recipient device. This page is the honest accounting of what changes for callers.

What the server sees on /v1/send

When you call POST /v1/send, the plaintext envelope exists in server RAM for the duration of the encrypt-and-forward operation. The server:
  1. Builds the envelope ({ type, payload, source }).
  2. Looks up each recipient device’s stored X25519 public key.
  3. For each recipient: derives a shared secret from the server’s private key and the recipient’s public key, generates a fresh 24-byte nonce, encrypts with nacl.secretbox.
  4. Writes the encrypted blob to the per-device Redis queue.
  5. Discards the plaintext envelope.
The plaintext is never written to Redis, the database, or any log. Error logs may carry the token_id, user_id, and an error code — never the payload.

What the server cannot see

Visible to serverNot visible to server
user_id (resolved from your token)Anything you send between your own devices via mirroring (encrypted on the source device).
token_id, labelShared secrets, device private keys.
Target device list, timestamps, source IPLong-term storage of any /v1/send payload (none is persisted).
The exposure is bounded — plaintext exists in RAM only between the request arriving and the per-recipient encryption finishing — and the same logging hygiene applies regardless of which endpoint handled the request.

Wire format

Recipients decrypt each envelope as:
wire_blob = [0x01] | nonce(24) | ciphertext
sym_key   = HSalsa20(0, X25519(my_private_key, server_public_key))
plaintext = nacl.secretbox.open(ciphertext, nonce, sym_key)
In practice you don’t write the HSalsa20 step yourself — nacl.box.before() in TweetNaCl (and crypto_box_beforenm in libsodium) does the X25519 + HSalsa20 in one call and returns the secretbox key. nacl.secretbox is authenticated encryption — a successful open proves the ciphertext was produced by an entity holding the server’s private key (i.e. the server itself, in normal operation). A failing open means the envelope was tampered with or misrouted; the device drops it. There is no separate signature.

Server keypair

The server holds a single X25519 keypair generated at bootstrap and distributed to devices at pair time (via /v1/pair/confirm for Android, /v1/devices/add-extension for the extension). Devices store the server pubkey alongside other paired-device pubkeys, marked type: "server". There is no runtime backfill endpoint — devices that paired before the public-API rollout must re-pair to receive the key. Server keypair rotation is deferred to V2.

What this means for you

  • Treat /v1/send payloads the way you’d treat any HTTPS POST body. The privacy boundary is “no long-term server visibility,” not “no momentary server visibility.”
  • Don’t put secrets in notification text that you wouldn’t put in any HTTPS request. The server doesn’t log it, but the trust model isn’t zero-knowledge.
  • Mirroring traffic stays end-to-end encrypted. Notifications captured on your phone by NotificationListenerService are encrypted on-device before they hit the wire; the server never sees their plaintext.