Security model

A host you don’t have to trust.

SealedGit is designed on the assumption that the server is untrusted. It stores what it is given, serves what it is asked for, and never holds a key. This page sets out exactly what that protects, and what it doesn’t.

The host
Untrusted. Stores ciphertext only.
Keys
On members’ devices, none on the host
Each repository
MLS_256_MLKEM1024_AES256GCM_SHA512_MLDSA87
Security level
NIST Category 5, post-quantum
Pushes
Committing AEAD, with history windows by dual key regression

01

Threat model: the host is untrusted

Assume the server is run by someone you don’t know, broken into by someone you don’t like, or compelled by someone you can’t refuse. SealedGit is built so that none of them learns what is in your repositories, because the host is never given anything it could read.

The host can

  • Store ciphertext and serve it back
  • See handles, repository names, who is a member, and the size and timing of pushes
  • Refuse to serve you, or delete what it stores

The host cannot

  • Read code, commit messages, issues, pull requests, reviews, release notes or repository descriptions
  • Admit anyone to a repository: that takes an MLS Add from a member’s device
  • Substitute its own key during admission without the safety number giving it away
  • Let a removed member decrypt anything pushed after the removal

Encryption protects confidentiality, not availability. A host you don’t trust can still refuse service, so keep your own clones. And a member’s device holds that member’s keys: if the device is compromised, so is everything it can read.

02

What is encrypted

What your team writes into a repository is encrypted on a member’s device before the host receives it, and it is decrypted only on members’ devices.

  • Code and historyPushed as encrypted git bundles.
  • Commit messagesInside the encrypted bundles, with the code they describe.
  • Issues, pull requests and reviewsEncrypted to the repository’s members and decrypted on their devices.
  • Releases and release notesEncrypted to the repository’s members.
  • Repository descriptionsEncrypted. The repository’s name is not; see below.
  • Large filesKept in SealedGit’s own sealed large-file store, shub lfs.
  • Ref updatesStored as hash-chained RefHeads, each one committing to the one before it.

A committing AEAD for every push

Pushes are sealed with a committing AEAD. A ciphertext is bound to the key that produced it, so it cannot be made to open, under some other key, into different contents.

Dual key regression for history windows

History windows use dual key regression, so a grant can cover a bounded stretch of a repository’s history rather than all of it or none of it. See history grants.

03

What the host can still see

Encryption hides contents. It cannot hide that you use a host, so some metadata stays visible. This is all of it.

  • HandlesYour username, and the usernames of the people you work with.
  • Repository namesThe name of each repository. Descriptions are encrypted; names are not.
  • MembershipWho is a member of which repository.
  • Push size and timingHow much is pushed, and when.
  • Your email addressHeld by the accounts service, so it can send confirmation links, password resets and invitations.

Name repositories with this in mind. If a name would itself give something away, choose another.

04

Keys live on members’ devices

Every key that can decrypt a repository is held on a member’s device. The host holds no key that would let it read your repositories, and nothing in SealedGit hands it one.

That is why the SealedGit app runs on your own computer, on macOS or Linux, and opens in your browser at a local address: it decrypts where your keys are. Signing in to the app enrols your device, which is what lets a member admit it to a repository. Start it with shub app.

05

Each repository is an MLS group

SealedGit uses Messaging Layer Security (MLS) to agree keys among the member devices of a repository. Every repository is its own group, on a post-quantum ciphersuite at NIST security category 5.

MLS_256_MLKEM1024_AES256GCM_SHA512_MLDSA87

Key encapsulation
ML-KEM-1024
Signatures
ML-DSA-87
AEAD
AES-256-GCM
Hash
SHA-512

NIST security category 5

NIST rates post-quantum parameter sets in five security categories. Category 5, the highest, means an attack should cost at least as much as a key search on AES-256. ML-KEM-1024 (FIPS 203) and ML-DSA-87 (FIPS 204) are the category 5 sets of NIST’s standard key-encapsulation and signature schemes, and AES-256-GCM, which encrypts the group’s messages, is the yardstick itself. Where each algorithm is used

These are the algorithms NIST standardised. The implementations SealedGit links are open-source Rust libraries, not a FIPS 140-validated module.

Epochs

An MLS group moves forward in epochs. Each change of membership, whether a member is admitted or removed, starts a new epoch with new keys. The host carries the MLS framing that makes this happen, but the framing is opaque to it: the host can neither read it nor take part.

Here is one repository across four epochs, and who can decrypt what in each.

Who can decrypt what, epoch by epoch
Member Epoch 1you and dee Epoch 2ben admitted Epoch 3dee removed Epoch 4cy admitted
youthroughout reads reads reads reads
benfull history history reads reads reads
deeremoved in 3 reads reads none none
cyforward-only none none none snapshot
  • reads: decrypts what is pushed in that epoch
  • history: earlier epochs, opened by a full-history grant
  • none: cannot decrypt

For cy, “snapshot” means the tree as it is at the join, then every change afterwards.

06

An invitation is not access

Getting someone into a repository takes two separate acts. The host can help with the first. Only a member’s device can do the second.

  1. Invite a member

    Inviting an email address records an invitation and sends a link. No keys change hands, and the invitee can read nothing yet.

    shell
    shub repo invite-email you/ledger --email teammate@example.com
  2. Accept the invitee

    The invitee signs up, or signs in, and accepts the invitation.

  3. Compare both of you

    Compare the safety number for the invitee’s device key with them, over a channel you already trust: in person, or on a call.

  4. Admit a member’s device

    A member’s device admits the invitee with an MLS Add, which the host cannot perform. Admission pins the invitee’s device key, so the host cannot substitute a key of its own.

    shell
    shub repo admissions --repo you/ledger --user <their handle> --key-package-digest <their digest>

Why the comparison matters: admission pins the exact device key you compared. A key substituted by the host would show a different safety number, and a mismatch is your signal not to admit.

07

Removing a member rotates the epoch

Removal starts a new epoch whose keys the removed device never receives. From then on it cannot decrypt anything that is pushed, as dee’s row in the example shows.

Removal looks forward, not back. It cannot reach into a device and delete what that device already fetched and decrypted. If a removed member could read secrets in the repository, rotate those secrets.

08

Full-history and forward-only grants

A member’s access to the past depends on their grant. There are two kinds.

Full history

The whole repository

The member can read the repository’s entire history, from the first push. That is ben in the example.

Forward-only

From the join onwards

A snapshot of the tree as it is at the join, and every change afterwards, but none of the earlier history. That is cy.

History windows use dual key regression, which lets a grant open exactly the stretch of history it covers, and nothing outside it.

09

What SealedGit deliberately does not do

Each of these would require the host to read plaintext, so SealedGit does not offer them.

  • WebhooksA webhook reports what changed in a repository. The host can’t report on what it can’t read.
  • Hosted CI runnersRunning your build on the host’s machines would mean giving them your plaintext.
  • Hosted codespacesAn editor on the host is your source code, decrypted, on the host.
  • The Git LFS wire protocolLFS hands file contents to the server as they are. Use SealedGit’s sealed large-file store, shub lfs.
  • Shallow or partial clonesCutting history down on the server takes a server that can read the history.
  • Host-side code search, diffs and mergesAll three need the plaintext. Do them on your machine, with git.

Try it

Read the model. Then push something sealed.

Create an account, open the SealedGit app on your computer, and make your first repository.