
What the SSP relay can and cannot see
SSP is a two-device wallet. Your browser extension holds one key, your phone holds the other, and neither can move funds alone. But those two devices have to talk to each other, and they do it through a server we run called the relay.
That server is the obvious place to ask an uncomfortable question: if everything routes through infrastructure the wallet company operates, what exactly does the wallet company see?
It's a fair question and it deserves a specific answer rather than a reassuring one. So here is what's actually in the code.
The relay cannot sign anything
Start with the part that matters most.
The relay never receives a private key. Not encrypted, not split, not in any form. Your wallet key stays in your browser extension and your key-app key stays on your phone, and the only things that cross the network are public keys, unsigned data, and signatures that have already been made on your devices.
This isn't a policy commitment, it's a structural one. Two-of-two multisig requires both signatures to move funds, and the relay possesses neither key. A completely malicious relay — ours, compromised, or replaced by an attacker — still cannot produce a valid transaction, because producing one requires secrets that were never sent to it.
That's the guarantee. Everything below is about what the relay does handle, which is a narrower but genuinely non-empty set.
What passes through, and for how long
The relay holds four kinds of record. Two of them delete themselves.
Sync data, when you pair your two devices. This carries the chain, your wallet identity, the key app's extended public key, the resulting WK identity, public nonces, the generated address, and the recovery xpub with its signature.
Action data, when you're signing something. This carries the chain, the derivation path, your WK identity, the action type, the payload itself, and the relevant UTXOs.
Both of those collections have a TTL index in MongoDB set to expireAfterSeconds: 900. Fifteen minutes. The database deletes the record whether or not anything else happens — it isn't a cleanup job someone has to remember to run, and it isn't a promise in a privacy policy. It's an index, enforced by the database itself.
Push notification tokens, so your phone can be woken when there's something to approve. These persist, because a notification token that expires every fifteen minutes would be useless.
The recovery xpub, one per identity, with no expiry at all. This is deliberate and the code says so: it exists so the wallet can fetch it whenever it needs to, rather than only during the brief windows when both apps happen to be awake.
The part we should be blunt about
Look again at that sync payload. It contains an extended public key.
An xpub is not a spending key and cannot authorise a transaction. But it is not nothing. From an account xpub you can derive every address that account will ever use, which means anyone holding it can watch the entire balance and transaction history for that chain. It's read access to your financial life on that account — precisely the linkage that on-chain privacy depends on avoiding.
The action payload is similarly real. While you're signing, the relay handles the unsigned transaction: where the money is going, how much, and from which outputs.
So the honest summary is not "the relay sees nothing." It's:
The relay cannot spend your money, and for fifteen minutes at a time it can see what you're doing with it.
The fifteen-minute window is the mitigation, and it's a meaningful one — it bounds how much history can accumulate in one place. But during that window the data is there, and we'd rather say so plainly than let "non-custodial" do rhetorical work it hasn't earned.
The design principle worth borrowing
There's a comment in the recovery service that captures the architecture better than any diagram:
the wallet verifies that signature against the identity pubkey it derives itself, so this store is not trusted.
The recovery xpub is stored alongside a detached signature that SSP Key made over it. When the wallet retrieves it, the wallet independently derives the identity public key and checks the signature itself. If the relay returned a different xpub — through compromise, a bug, or deliberate substitution — the signature wouldn't verify and the wallet would reject it.
The relay is treated as an untrusted pipe by the software that depends on it. That's the right way to build on infrastructure you operate, because it means your own server being wrong doesn't become your users' problem. It's also the pattern to look for when assessing any wallet: not "do they promise to behave?" but "what happens if their server misbehaves?"
What a hostile relay could actually do
Worth being concrete about the real threat model.
It could observe. Within the TTL window, your xpub and your pending transaction. That's a privacy exposure, not a theft risk.
It could censor. Refuse to pass messages between your devices, which would stop you signing new transactions through the normal flow. Annoying and disruptive — and not the same as losing anything. Your keys are still yours, your funds are still on-chain, and recovery paths exist precisely because the relay might not be there.
It could lie, and mostly fail. Substituting a recovery xpub is defeated by the signature check described above. This is why the verification matters.
It cannot sign. No key, no signature, no transaction.
The realistic worst case is surveillance and disruption, not loss. That's a meaningfully better position than a custodial service, where the equivalent worst case is that the money is gone. It is not the same as "nobody can see anything," and conflating the two is how people end up with a false picture of their own privacy.
What you can do about the visible parts
Understand what pairing exposes. Syncing a chain means an xpub for that chain transits the relay. That's the cost of the two-device design working at all.
Remember the window is short but not zero. Fifteen minutes per action is a bound, not an absence.
Treat the recovery xpub as permanent. It's stored without expiry by design, and it's public key material with a verifiable signature attached — but it is durable, and you should know that rather than discover it.
Judge the architecture, not the assurance. The useful question about any wallet's server isn't whether the company promises discretion. It's whether the software would notice if the server lied. Ours checks signatures rather than trusting responses, and you can read that in the code rather than taking our word for it.
The thing we'd most want understood is the shape of the trade. A two-device wallet needs a coordination channel, and a coordination channel is a place where metadata gathers. We've bounded that with database-enforced expiry and designed the clients to distrust the server — but the honest version is that the relay sees real things, briefly, and no amount of careful architecture makes that number zero.


