Skip to main content
Most security pages list what a product defends against. This one has both halves, and the second is longer. Read the protocol page for the mechanisms referenced here.

What Reflecto is

A bridge between devices you already own. Your phone’s notifications appear on your computer; clips and files move both ways; an AI assistant can reach you on your phone. There are no accounts — a “user” is a set of devices that have paired with each other.

What we’re protecting

  • Notification content — messages, codes, call alerts, everything your phone shows you
  • Clipboard content — often passwords, tokens, addresses
  • Files in transit between your devices
  • Device secret keys — compromise means everything above, and see no forward secrecy
  • Metadata — who you talk to, when, and how often, which is frequently as sensitive as content

What this protects against

What this does not protect against

Your device being compromised

The most relevant threat to almost everyone, and the one we can do least about. Reflecto decrypts on your devices — that is the entire design. Malware with sufficient privileges, a rooted phone, a compromised browser profile, or someone with your unlocked device reads everything Reflecto shows you. Related and often overlooked: your history on the computer is plaintext at rest. Notification history, clip history — including the passwords and tokens named above — and agent messages persist in browser storage inside your profile with no additional encryption. Any process that can read the profile can read them.

You cannot inspect the code

Reflecto is closed source. You are trusting a binary we built. There are no reproducible builds, so you cannot confirm the Play Store app was built from any particular source, and a build targeted at one user would not be detectable by inspection. What you can verify is below.

Messages an AI agent sends you

When you or an assistant calls the public POST /v1/send API, or uses the hosted MCP connector, the server holds that message in plaintext for as long as it takes to encrypt it to each of your devices. An agent holds no keys, so somebody composes the message; that somebody is our server. The plaintext is never written to disk, never persisted, and never logged. But “no long-term visibility” is weaker than “no visibility,” and this path gets the weaker one. Mirrored notifications, clipboard and files never touch it. Full detail on the encryption page.

Guessing a live pairing code

A six-digit code is a 10⁶ space with a 300-second window. An attacker who guesses a live code pairs as a phone to a stranger’s computer: they receive that computer’s outbound dismiss, reply and clipboard traffic, and can inject arbitrary notifications into it. What currently stands in the way is a per-IP rate limit, and it is weaker than it looks: the limiter is in-process, so its effective ceiling multiplies by the number of server instances. We consider this the weakest structural point in a design with no accounts, and we would rather say so than wait to be told.

Metadata

The server necessarily sees who is talking to whom and when: device IDs and labels, message sizes, timestamps, delivery targets, FCM tokens, Web Push endpoints, and per-device status (battery, charging, ringer mode, network type). Encryption hides content, not the fact or shape of communication. Google additionally sees delivery timing, dismissed message IDs, disconnect device IDs, and registration nonces — all outside the encrypted envelope. Peer-to-peer file transfer reveals each device’s public IP and port to Google’s public STUN servers during connection setup, along with the fact and timing of the attempt. Local network addresses are not sent there — they travel only between the two peers inside the encrypted envelope.

What we do with IP addresses

More precisely than we put it before. Our application logs do not contain client IPs. But:
  • Coarse geography (country, region, city) is derived from your IP and sent to Google Analytics with routing events.
  • The public API’s rate limiter stores counters keyed by literal client IP in Redis (rl:api:ip:<ip>) for up to an hour.
  • Our hosting provider and its edge keep their own access logs, outside our code.
“We do not log IPs in our application” is true. “Your IP never persists anywhere” would not be.

Analytics, precisely

The analytics opt-out in settings is client-side only. Turning it off stops the client SDK. It does not stop server-side analytics, which has no opt-out path at all. The server emits routing events — pairing initiated and completed, device unpaired, notification relayed and dropped — to Google Analytics 4 and PostHog, keyed to a pseudonymous per-pair identifier and tagged with the coarse geography above. No notification content, clip content, or on-device AI output is ever collected on any path.

An unverified pairing

Pairing is trust-on-first-use. A malicious server could hand each device a key it controls. Safety numbers make that detectable — but only when someone compares them, and most people never will.

Verifying two computers against each other

You can verify your phone against a computer. You cannot verify two computers against each other: they learn each other’s public keys from a server-provided directory. Concretely, PC-to-PC clipboard sync and PC-to-PC file transfer are trust-the-server today, with no detection path. This is a known v1 gap and the weakest point in the verification story.

No forward secrecy, no key rotation

Device keypairs never rotate and there is no ratchet. One compromised device secret key retroactively decrypts every ciphertext for that pair that an attacker captured, not merely what is still queued. The server keypair does not rotate either.

Checking the Android app’s traffic yourself

The Android app pins no certificates, but it also does not trust user-installed certificate authorities — the platform default since Android 7. You therefore cannot put a proxy in front of it on an unmodified device. The other three surfaces do not have this limitation.

The server controls delivery

Encryption stops the server reading your messages. It does not stop the server dropping, delaying, reordering or replaying them. Each message is individually authenticated; there is no integrity check across the stream, so a missing message is not detectable by the client. Related: the queue holds 200 entries per device and evicts the oldest on overflow. Someone able to generate traffic to your devices can push undelivered notifications out of the queue before you read them.

Rate limiting is uneven

Public API tokens (rfk_live_) are limited per token, per IP, and per receiving user. Hosted MCP connectors are not: connector sends currently carry no per-connector quota and do not increment the per-receiver daily counter. They are bounded by per-IP limits only, which are in-process and therefore multiply by instance count.

Traffic analysis

Message sizes and timing are visible to anyone watching the connection. There is no padding and no cover traffic.

A targeted attacker with real resources

If your adversary is a state actor, a well-resourced intelligence service, or anyone who can compel a specific build to be served to a specific person, Reflecto is not the right tool. It is built to keep an ordinary hosted service — including us — out of your notifications. That is a real guarantee. It is not the same as resisting a targeted adversary.

Things we have not done

  • No third-party security audit. libsodium and TweetNaCl-js have public audits; Lazysodium is an unaudited binding; our use of all of them is unaudited. We have not commissioned one. If we do, our intent is to publish the report rather than summarise it — an audit you cannot read is weak evidence.
  • No server or device key rotation.
  • No warrant canary.

Who else touches your data

What you can verify yourself

Without a repository, and without our cooperation: Compare safety numbers. Settings → Privacy & security → Verify encryption on the computer; device detail on Android. Matching 60-digit numbers mean the key exchange was not tampered with. The algorithm is published in full, so you can recompute it yourself from the two public keys. Watch what actually leaves. The Chrome extension and the web app are JavaScript running in your own browser — open devtools, watch the network tab, and compare against the documented wire format. You should see [0x01 | nonce | ciphertext] going out and nothing legible. The CLI is an ordinary Node process you can put behind a proxy. Check that decryption is local. Every readable string in the panel came from a blob that arrived opaque. Plaintext reaching the server would have to leave your browser as plaintext, and you would see it. Read the protocol page against reality. It documents nonce length, version byte, minimum blob size, FCM cleartext fields and payload shapes precisely enough that a mismatch would be obvious. If what you observe disagrees with what we published, that gap is the story — please tell us.

Data retention

Device deletion is asymmetric and worth knowing. Disconnecting a computer deletes that computer’s record and its pair rows. The phone’s record is deliberately spared — including its FCM token, label and status columns — so it is not stranded from its other computers. The phone’s record goes when the phone itself disconnects, or when the reaper collects a device that never paired. You can delete your data at any time.

Reporting a vulnerability

Email security@reflecto.dev with what you found and how to reproduce it. We will not pursue or threaten anyone for good-faith security research: testing against your own devices and account, without degrading service for others and without accessing other people’s data. Tell us before you publish and give us a reasonable window to fix it; if you would like to encrypt the report, ask and we will arrange a channel.