Threat model
What Obsidian protects, who from, and — just as important — what it does not protect. This is written to be read by a skeptical security person, not to reassure a marketer. If a claim here is ever untrue of the code, that is a bug; report it.
Status: pre-alpha. Not independently audited. Self-hosting is not yet available — during the alpha the relay is run by the project operator for a closed group of testers. This document describes the target architecture of the metadata-minimizing relay; where the code has not yet caught up, it is marked (in progress).
What Obsidian is
A private, end-to-end-encrypted messenger. Your messages and media are encrypted on your device and decrypted only on your friend's device. The cloud relay is a dumb, ephemeral mail-slot: it holds opaque encrypted blobs for at most 24 hours, then wipes them. Your permanent copy lives only on your own device, encrypted at rest. There is no cross-device sync — that is the design, not a limitation.
The relay's job is to deliver a blob to whoever is meant to receive it. To do that it needs some delivery information. The whole point of this document is to be exact about how little that is, and what leaks anyway.
What we protect — and how
| Asset | Protection | Who is shut out |
|---|---|---|
| Message & media content | AES-256-GCM; the per-message key is wrapped to the recipient's RSA public key. The relay only ever holds ciphertext. | The relay operator, with full root + database access, cannot read content. |
| Who sent a message ("sealed sender") | The sender's identity travels inside the encrypted envelope. The relay stores no sender field. The recipient learns and cryptographically verifies the sender only after decrypting. | The relay operator cannot see who sent any message. |
| Who received a message | The relay routes by an opaque, rotating mailbox token, not by a user ID. Tokens are exchanged friend-to-friend, encrypted; they are never published on your profile. The token → user mapping exists only on devices. | The relay operator sees an opaque token, not an identity. |
| Your private keys | Generated on-device, stored in the OS secure enclave. Never transmitted. | Everyone but you. |
| Your message history | Stored only on your device, in a SQLCipher database encrypted with a key held in the secure enclave. | Everyone but you (and anyone who unlocks your device). |
| Your friends list | Friendships live locally on each device. There is no server-side friends graph at rest. | The relay holds no standing list of who your friends are. |
The combination of sealed sender + opaque mailbox tokens means that an operator who dumps the entire relay database sees rows of: an opaque token, an opaque ciphertext blob, and a timestamp. No names, no user IDs, no "who talks to whom" graph. That is the core claim, and it is the thing to test first (see How to attack this).
What we explicitly do NOT protect
This is the honest part. Do not use Obsidian believing it does any of the following — it does not, and saying so plainly is the point.
- Arrival timing. The relay receives each blob, so it necessarily knows roughly when a message arrived. We do not batch or add cover traffic. An operator watching the server learns the timing of traffic to each mailbox token.
- Network metadata (IP addresses). The relay sees the IP address of every device that connects to it. We do not run Tor or a mixnet. Your ISP and the relay's host see that you are talking to the relay.
- Traffic volume & size. Message and blob sizes are not padded. Volume and size patterns are visible to the operator and can support traffic analysis.
- Mailbox-token unlinkability under active logging. Mailbox tokens are registered over an authenticated connection. We do not store a token → user link — but an operator who actively logs registration traffic at the moment of registration could reconstruct one. True unlinkability needs blind/anonymous registration, which is future work, not present.
- The friendship edge at connection time. Sending a friend request creates a short-lived record that names both users (it is deleted once both sides sync). So the relay can observe that two specific accounts became friends, around when, even though it never sees their messages. Establishing a contact is the one moment the social graph is briefly visible. Unlinkable friend discovery is future work.
- A peer who already holds your mailbox token. Because the relay cannot tie tokens to users, message-fetch is gated by token secrecy, not an ownership rule (in progress: tightening this with per-request token proofs). A friend you gave a token to — or anyone who obtains it — can observe that traffic arrives at that token (never its content). Mitigated by one token per friend-pair and rotation; not eliminated.
- A compromised or unlocked device. All guarantees assume your device is yours and locked. If someone has your unlocked phone, encryption is irrelevant.
- Key loss. If you lose your keys and your backup, your data is gone — permanently, by design. No one, including the operator, can recover it. This is a feature; treat your key backup like the only copy of a physical key.
- The relay operator's uptime / honesty about availability. They run the box. They can take it down, or fail to. (Once self-hosting ships, you may be that operator.)
- Your account's existence. The relay knows accounts exist (email, username#code, public key, push token are stored in the clear so friends can find you). It just cannot tie a specific message to one.
Security assumptions
- The cryptographic primitives and libraries we build on (AES-256-GCM, RSA-2048-OAEP, SQLCipher, the OS secure enclave) are sound and correctly used.
- Your device's OS secure storage is not compromised.
- You keep a backup of your keys and keep your device reasonably secure.
- For the alpha: the relay operator is the project operator, running a single instance for a closed group of trusted testers.
How to attack this (start here)
The fastest way to check the core claim is to be the malicious operator:
- Send real messages between two accounts.
- Open the relay's database directly.
- Inspect the message table. You should find only
mailbox_token(opaque),envelope(opaque ciphertext), andcreated. No sender, no recipient, no user ID, no token → user mapping anywhere in the schema.
If you can reconstruct "who talked to whom" from the database alone, that is a finding — that is the claim broken. The known, documented leaks are the ones in What we explicitly do NOT protect above (timing, IP, volume, the transient friend-request edge); anything beyond those is a bug. Here is how to report it.
Roadmap (honest about what isn't built yet)
- (in progress) Tighten message-fetch from "token secrecy" to a per-request token proof, so a leaked token cannot be polled by a third party.
- (future) Blind / anonymous mailbox registration, to remove the token → user link even under active logging.
- (future) Unlinkable friend discovery, to close the friend-request edge leak.
- (future) Padding / batching / mixnet options for timing and volume — explicitly out of scope for the alpha.
- (future) Independent third-party cryptography audit before any non-trusted rollout.