// privacy model

We store almost
nothing about you.

mdpush runs on a zero-knowledge architecture. Your content, titles, categories, and project names are encrypted on your device. By the time anything reaches our servers, it's already gibberish to us.

// the difference

What you see. What we see.

Same document. Two very different views.

you (in your browser)
Auth implementation — round 2
by gabriel · 2m ago · expires in 7d

Here's the revised auth flow after yesterday's review. The big change: we're moving session-token hashing into the database trigger so we never have raw tokens in transit…

const session = await db.sessions.create({...})
us (in our database)
eyJ2IjoxLCJhbGciOiJBRVMtMjU2LUdDTSIsIml2IjoiVjJqM3F4OUZ QbFp4Yk5NTSIsImN0IjoiOXBHa2g3VjBNL3JFM1R5NU5zZklzRGdwT3F 1WFNESlNqK1JuUklYa3I5T2ZkSXdKZk94aFpPVlhlNEZSL2pNbmpYR2N INnRaY3FXMzlvV2lYM3JOVnEzVTZjOEQ4TUZkM3JhQzVQZ3JjUUVzWEY 3M3kvTjI1Z0RyL3NhTUZqUFZ0WlBYNUVRZHRWaXc2NWFkSEJOaVBJQ09 GczVHRkRRSm9HYVQwclFWMnZGS1U=
×no title, no content, no category, no project
// every field, accounted for

Exactly what we store.

No vague language. Field by field, what hits our database and whether we can read it.

DataWhat we storeCan we read it?
EmailHashed for lookup + encrypted for sending codes× No
PassphraseNever stored, never transmitted× No
Document contentAES-256-GCM ciphertext× No
Titles & metadataEncrypted alongside content× No
Encryption keysWrapped with your passphrase-derived key× No
View countPlain number (operational)Yes
Expiration datePlain timestamp (operational)Yes
Lock typelight or strong (operational)Yes

// operational data is the bare minimum needed to enforce expiration, view limits, and lock gates

// we never store

Things you won't find in our logs.

×IP addresses×Browser fingerprints×Plaintext content×Your passphrase×Decryption keys×Referrer headers
// proof

This is the entire row.

If we were ever subpoenaed, this is literally all we'd have to hand over for one document.

documents row
{
  "id": "k7f2x9",
  "user_id": "u_3a91...",
  "encrypted_payload": "<ciphertext above>",
  "wrapped_doc_key": "<wrapped with passphrase-derived key>",
  "lock_type": "strong",
  "lock_credential_hashes": ["<sha256>"],
  "created_at": "2026-04-06T14:22:01Z",
  "expires_at": "2026-04-13T14:22:01Z",
  "max_views": 10,
  "view_count": 3,
  "status": "active"
}

No title. No content. No project name. No category. No reader IPs. The wrapped key is mathematically useless without your passphrase, which we've never seen.

// common worries

The questions we get the most.

What if you change your mind later?+
We can't. The CLI is open source — anyone can audit that the encryption happens before upload. If we shipped a server that suddenly required plaintext, you'd see it in the diff. The architecture commits us, not just our promise.
Can you see who shared what to whom?+
We see who uploaded a doc and that some browser opened the link. We don't see the recipient's identity, the document's subject, or anything about the relationship between the two. The lock credential is hashed, so even “who sent you this?” answers never reach us in plaintext.
What about metadata correlation?+
We don't log IPs, user agents, or referrers. We don't track which docs a single user opens together. We don't join your account to your reading patterns. The minimum data is the only data.
What happens if I forget my passphrase?+
Your encrypted docs become unreadable. We literally cannot recover them — that's the same property that makes the system safe from us. Save your recovery code somewhere safe (a password manager works great).
Is the encryption actually real?+
Yes — and you don't have to trust us on that. The Go CLI is open source. Read the encryption code, run it locally, verify the ciphertext that gets uploaded is what you'd expect from AES-256-GCM. That's the whole point of publishing it.

Now you know everything.

Drop a markdown file and try it for yourself.

← back to home