SIPHR
sign increate key
v0.1 · open beta · audit pending

Code hosting
that we can't read.

Public repos work like any forge. Private repos are end-to-end encrypted with keys that live on your machine. We hold ciphertext, public keys, and wrapped keys we can't unwrap. Not the team. Not a subpoena. Not us.

Generate your keyRead the threat model
↳ keypair generated in your browser↳ passphrase never sent↳ no analytics
 your browser
siphr server sees
# auth.ts
export async function signIn(
  email: string,
  passphrase: string
) {
  const key = await
    deriveKey(passphrase);
  return unwrap(key, blob);
}
# 7c93…e0a1
9a4f c2b8 7e01 d3aa f681
02bc 4a91 7d2e 88c5 1f0a
b73c 9d6e 4271 a05b f8d4
6c19 ae83 50fb 21d7 9c0e
47b2 d815 90a3 6e2c b148
↳ aes-256-gcm, nonce e8…42
blob → ciphertext✓ wrapped to 3 collaborators
your fingerprint
5f9a c218 ab30 d7e6
e0 c0 04 eb 12 d2 e1 6d 05 eb 1e 29 fe 8e 11 19 b4 86 e0 ea 9f 2a f4 75 7f f0 a8 0e a8 ca 5a cc db 55 c4 d3 ac 39 ef 08 27 86 35 47 40 ea cc e9 f1 21 be 76 95 72 de 21 c9 c3 c9 3a 38 ba a5 79 99 81 1a 5b 3c 6d 79 7e 87 ed dc 3e 01 9e da 28 a1 40 58 c7 a7 3b 38 9f 7a fe 32 7a 20 ec 2e 0e bc 91 be ee f9 f8 9f ae c0 d2 a8 45 cb 88 5d 2f 87 91 ab c6 f2 f3 46 9c 88 d3 06 de 67 dc 6e 6a 7e 15 30 9e c6 53 65 f0 66 4f d3 32 f8 ee 27 e5 38 41 08 31 69 74 e6 38 de cf 29 7d b0 5e eb bf f3 29 ef 7d 79 f8 18 41 ab 15 0c b5 00 95 6f cb 7f 17 d4 fb
↳ the whole pitch, in one diagram

Three boxes. One rule. Every key lives with you.

Read left-to-right. The cryptographic boundary is between box 2 and box 3 — once data crosses it, it is unreadable without a key the server has never seen.

box 1 — you

Your machine

  • · passphrase
  • · private key (wrapped)
  • · repo keys (in memory)
  • · plaintext source
✓ can read everything
encrypt
box 2 — the wire

Ciphertext in flight

9a4f c2b8 7e01 d3aa f681
02bc 4a91 7d2e 88c5 1f0a
b73c 9d6e 4271 a05b f8d4
TLS + aes-256-gcm
store
box 3 — siphr.dev

The server

  • · public keys
  • · encrypted object blobs
  • · wrapped repo keys
  • · refs (commit oids)
✗ can't read source · can't unwrap keys · can't decrypt commits
↳ side by side

Same git. Different threat model.

siphr
github
self-hosted gitea
source code at rest
encrypted (per-repo key)
plaintext
plaintext
who can read your private repo
you + collaborators
github staff with access
your sysadmin
subpoena response
ciphertext, useless
plaintext code
plaintext code
commit messages
encrypted
plaintext + indexed
plaintext
search across your repos
client-side, slower
fast server-side
fast server-side
lost passphrase recovery
!you. recovery codes only.
email reset
email reset
price for private repos
free during beta
free / 4 / 21
your hosting

we'll be honest about the tradeoffs. lost-passphrase recovery is the price of “we can't read it either.”

01

Per-repo keys

Every repo gets its own random 256-bit AES key. The repo key is wrapped to each collaborator's public key. We never hold a master.

aes-256-gcm · fresh nonce per object
02

Keys live with you

Generated in your browser at signup. Passphrase-wrapped locally with PBKDF2-SHA256 at 600k iterations. We see your public key, never your private one.

p-256 ecdh · pbkdf2-sha256 · 600k
03

Verify, don't trust

Open source. Reproducible build. Public-key transparency log. Every claim on this page is checkable from the command line in <2 minutes.

↳ /transparency