Postern Labs
MAINNET BETA. Unaudited · relaxed k=4 regime (trivially forgeable) · k=8 hardening reverted, re-activating with a difficulty fix · very few nodes, 51%-attackable · not a security · for evaluation — not to protect real secrets yet. Full disclaimer →

Depot — the app-distribution model

Multiple repos. Pinned keys. Nothing trusted on first contact.

Depot is Postern's app-distribution model — a signed-catalog, multi-repository client built the way F-Droid proved this should work, and reimplemented from zero so nothing here depends on fdroidserver's AGPL code. Instead of trusting one Postern-run list of apps, you keep a list of repositories: ours, plus any community's, company's, or independent developer's you choose to add. Each one is added by URL and a pinned Ed25519 public key delivered out of band — never by showing up and being trusted on first contact. Every repo is verified on its own terms, every card tells you which repo it came from, and the strongest badge Depot hands out — ATTESTED — has to be earned by an actual rebuild from source, not claimed.

multi-repo client: design / spec Postern Store catalog verifier: reference prototype unaudited

How it fits together

Repo → index → client — the F-Droid shape, reimplemented.

A Depot repo is a signed index file: app metadata kept separate from its versions and builds, a self-describing repo{} block, and a detached Ed25519 signature over the exact bytes served — the same separation F-Droid's index-v2 format proved out. The client fetches that index, verifies the signature in-browser via WebCrypto, and merges apps from every repo you've enabled into one list, always labelled by source. We reused the concept, not the code: no line of fdroidserver (AGPL) is in this stack, and everything is reimplemented under a permissive license so Postern's own stack stays permissive end to end.

Layer 1 · Repo

Any operator — Postern Store or a third party. Just a static file host and an offline Ed25519 keypair; no coordination with Postern required.

Layer 2 · Signed index

catalog.json + detached .sig, a self-describing repo{} block, format postern.catalog/v1 extended — index-v2-like, no fdroidserver code reused.

Layer 3 · Client (Depot)

Verifies the signature in-browser via WebCrypto, applies the normalizeEntry honesty rules, merges apps from every enabled repo, and always shows the origin repo on the card.

Plainly stated: no fdroidserver (AGPL) code is used anywhere in this stack — only the index-format concept, reimplemented from zero under a permissive license.

Same rules, any repo

The official catalog and a repo you add yourself are held to the identical standard.

Publishing a Depot repo takes a static file host and an offline Ed25519 keypair — nothing else. A developer, a community, or a company can put their own catalog up and have their users add it exactly the way they'd add ours: URL plus pinned key. What doesn't move is the honesty layer: normalizeEntry, the code that decides what a listing is allowed to claim, runs identically over every repo, first-party or not.

Official repo

Postern Store — the official repo

  • Pinned Ed25519 key required — no trust-on-first-use, even for the first-party repo.
  • VERIFIED / TAMPERED / UNSIGNED / UNVERIFIED shown per repo, never assumed.
  • normalizeEntry honesty rules apply: a proprietary app can never show "hosted by us"; ATTESTED requires the attestable-nix tier, a PASS record, and a pinned builder.
  • Origin repo always visible on every app card.
Third-party repo

Any third-party repo you add

  • Pinned Ed25519 key required — added by URL + fingerprint delivered out of band, no trust-on-first-use.
  • VERIFIED / TAMPERED / UNSIGNED / UNVERIFIED shown per repo, never assumed.
  • The same normalizeEntry honesty rules apply: no repo can grant itself a stronger badge by writing different JSON.
  • Origin repo always visible on every app card.

What Depot actually does

Seven pieces, each independently honest.

Add & trust a repo

Add a repository by URL plus a pinned Ed25519 public key delivered out of band — never trust-on-first-use. Every repo you enable is verified independently and its apps stay labelled by source.

design / spec unaudited

CRL / revocation

A signed, version-monotonic revocations.json covers compromised repo keys, compromised builder identities, and malicious artifact hashes, checked against a freshness window (max_stale_secs, 14 days by default).

design / spec unaudited

Builder-identity pinning

The ATTESTED badge only counts a PASS record if it's signed by a builder identity the client already pinned as trusted — never one the record names for itself. Closes the forge-a-PASS-record attack.

design / spec unaudited

Reproducible builds → ATTESTED badge

Two independent build hosts rebuild an app from its pinned source commit in a hermetic Nix sandbox and compare the result to the published artifact. Only an explicit PASS on both comparisons unlocks the badge.

design / spec unaudited

F-Droid-style index format

Metadata separated from versions/builds, a self-describing repo{} block, a detached signature over the exact bytes served — the index-v2 shape, reimplemented from zero with no AGPL code reused.

design / spec unaudited

Self-hosted repos

Any static file host plus an offline Ed25519 keypair is enough to publish a repo — no coordination with Postern required, and the same honesty rules apply automatically.

design / spec unaudited

Curated catalog / honesty spine

normalizeEntry enforces the non-negotiable rules in code, on every repo: no proprietary app shown as "hosted by us," no checksum shown unless signing.state is actually published, no self-granted badges.

design / spec unaudited

The strongest badge

What ATTESTED actually proves — and what it doesn't.

The ATTESTED badge means: this app was rebuilt from its exact, pinned source commit, in a hermetic Nix sandbox, on two independent build hosts; both rebuilds matched each other and matched the binary actually published; and the record proving all that was signed by a builder identity the client had already pinned as trusted — not one the record names for itself. That last part matters: without it, anyone publishing a build could forge a PASS record and vouch for a key they generated five minutes earlier. Depot's builder-trust check closes exactly that hole. What it does not prove: that the source code is safe, or that Postern (or anyone) has audited it. Reproducibility answers "is the binary the source?" — nothing more.

Trust can be withdrawn

Depot says so out loud.

revocations.json is a signed, monotonically-versioned revocation list covering three things: repo keys found to be compromised, builder identities compromised or decommissioned, and individual artifact hashes discovered — after publication — to be malicious. The client checks this list against a freshness window (max_stale_secs, 14 days by default) and, when a fresh check fails, shows that in the UI rather than defaulting to a quiet "still trusted." Be clear about the limit: a well-positioned network attacker can suppress the revocation list until that window lapses. That's a real gap, not a hidden one — Depot's revocation is honest about being delayed, not instant.

Inside a CRL

What revocations.json actually revokes.

FieldRevokesClient behavior on match
Repo keyA compromised repo signing key.That repo drops to UNTRUSTED and all of its apps are hidden.
Builder identityA compromised or decommissioned builder key.The ATTESTED badge is stripped from anything it signed; the tier falls back to whatever the catalog itself claims.
Artifact hashA specific build later found malicious.That exact version is flagged and blocked from install, even if its signature still checks out.
version (monotonic) + max_stale_secsThe staleness of the CRL fetch itself.The client refuses to treat a stale fetch as "no revocations" — it surfaces the staleness instead of hiding it.

Terms, defined once

Glossary.

Depot
Postern's attested distribution/update channel — a multi-repository client inspired by F-Droid, with a signed index, a revocation list, and reproducible-build attestation.
Repo
A signed index file (JSON) published by an operator — Postern or a third party — listing apps, versions, and artifacts; added to the client by URL plus a pinned public key.
CRL
revocations.json — a signed, versioned list of revoked repo keys, builder identities, and artifact hashes, checked against a freshness window (max_stale_secs).
ATTESTED badge
Granted only when an app in the attestable-nix tier has a reproducible-build record with a PASS verdict, signed by a builder the client has already pinned — never just a tier claim.
Reproducible build
Rebuilding an app from its pinned source commit, in a hermetic (Nix) environment, on at least two independent hosts, and comparing the result byte-for-byte against the published binary.
Builder trust store
A list of trusted builder identities pinned by the client/curator, each tied to a specific public key — prevents a forged record from declaring itself trustworthy.
Detached signature (Ed25519)
A cryptographic signature kept separate from the original file (e.g. catalog.json.sig), computed over the exact bytes served, verified in-browser via WebCrypto.
normalizeEntry / honesty spine
The honesty rules applied in code to every catalog entry from any repo: a proprietary app is never "hosted by us," the attestable badge is only for the attestable-nix tier, and a checksum is shown only when signing.state is published.

What's built, what's next

Status.

The signed-catalog verification, multi-repo add-by-key, the CRL schema and freshness check, and the builder-trust pinning logic all exist in code today, as a reference implementation — unaudited, like everything else on this site. What doesn't exist yet: automatic key rotation (that's TUF, and it's the acknowledged next step), and a populated catalog of ATTESTED, per-app builds — right now the mechanism is proven and the shelves are close to empty. We'd rather ship the honest trust plumbing first and fill the catalog after than backfill the trust story once apps are already listed.

What Depot does not claim

  • A reproducible build proves binary == source, not source is safe — reproducibility says nothing about whether the code itself is trustworthy or has been audited.
  • No automatic key rotation yet. That's TUF, and it's the acknowledged next step, not a shipped feature.
  • Revocation can be delayed. A well-positioned network attacker can suppress the CRL until max_stale_secs (14 days by default) expires — a real gap, disclosed, not hidden.
  • Zero fdroidserver (AGPL) code is used. Only the index-format concept was reused, reimplemented from zero under a permissive license.