Module 13 · Zero-Knowledge identity & private protocols
Decentralized identity, ZK and private soulbound tokens
A decentralized identifier (DID) is a self-controlled identifier whose controller proves control with a key, no central registrar required. A verifiable credential (VC) is a claim signed by an issuer about a subject (usually a DID). Zero-knowledge lets a holder present VCs selectively and unlinkably — the credentials lesson, now anchored to self-sovereign identifiers. Soulbound tokens (SBTs) are non-transferable tokens meant to represent reputation, membership or credentials; done naively on a public chain, they are a surveillance engine, because every SBT a wallet holds is visible and combinable into a full profile. Private SBTs replace the public token with a commitment plus a ZK membership proof, so a holder proves "I hold an SBT of this class" without revealing their wallet or their other tokens. The honest boundary is the recurring one: the stack proves control and signed, unrevoked possession — never the truth of a claim or the wisdom of trusting its holder.
Learning objective
See how decentralized identifiers (DIDs), verifiable credentials (VCs) and zero-knowledge proofs combine into a privacy-respecting identity stack, and why naive on-chain "soulbound" tokens (non-transferable reputation/credential tokens) are a privacy disaster that ZK membership proofs can repair. Then place the whole stack on the privacy/compliance spectrum and draw the honest boundary: the stack proves control of an identifier and possession of signed, unrevoked claims — not that the claims are true or that a holder should be trusted.
- Track
- Research
- Module
- 13 · Zero-Knowledge identity & private protocols
- Prerequisites
- Anonymous credentials and selective disclosure · Semaphore: anonymous membership and signalling · Non-membership proofs, compliance and proof-of-innocence
Conceptual introduction
The anonymous-credentials lesson gave us issuer-signed attributes shown selectively and unlinkably. This lesson anchors that machinery to identifiers people actually control, and then confronts a cautionary design — the public soulbound token — that shows how quickly an identity primitive becomes surveillance if privacy is an afterthought.
Two standards frame the stack. A decentralized identifier (DID) is an identifier you control by holding a key, resolvable to a document listing your public keys and endpoints, with no central authority issuing or revoking it. A verifiable credential (VC) is a signed claim about a subject (identified by a DID). Add zero-knowledge and the holder proves possession and satisfies predicates without revealing the raw credential or linking presentations. The private-SBT problem then shows why that ZK layer is not optional.
Controlling a DID proves you hold its key, and a VC proves an issuer signed a claim about a subject. Neither proves the claim is true, nor that the DID maps to a particular real person. Identity cryptography binds keys to claims; it does not bind claims to reality.
DIDs, VCs and the ZK layer
- Decentralized identifier (DID)
- A self-controlled identifier (a URI) resolving to a DID document of public keys and service endpoints. Control is proven by signing with the corresponding private key; there is no central registrar.
- Verifiable credential (VC)
- A tamper-evident claim set signed by an issuer about a subject (usually a DID): "this DID is over 18", "this DID holds degree X". The W3C VC data model is the common standard.
- Holder / wallet
- The subject stores VCs and produces presentations, choosing what to disclose and deriving ZK proofs — the self-sovereign counterpart to the holder role from the credentials lesson.
- ZK presentation
- A proof that discloses only chosen attributes or predicates over a VC while proving a valid issuer signature and control of the subject DID, unlinkable across showings.
The combination is the point. A DID gives a stable, self-controlled anchor; VCs attach issuer-signed claims to it; ZK ensures a presentation reveals only the needed predicate and cannot be correlated across verifiers. A user proves "I control a DID that holds an issuer-signed credential asserting I am over 18" and nothing more — not the DID string to a verifier that does not need it, not the other credentials in their wallet, not a link to last week's presentation.
synthetic — a ZK VC presentation shape (illustrative)# Issuer signs a VC about the subject DID vc = { subject: did:example:abcd, claims: {age>=18, country: AA, ...}, issuer: did:example:gov, signature: <issuer_sig> } # Holder presents, proving control + a predicate, hiding the rest proof = present(vc, prove_control_of = subject_did, # signs a challenge disclose = { }, # reveal no raw values predicates = { age >= 18 }) # reveal only the predicate # Verifier checks: issuer_sig valid, subject controls the DID, # predicate holds, presentation unlinkable. Learns only "age>=18 holds".
The soulbound-token trap, and the private fix
A soulbound token (SBT) is a non-transferable token bound to an account, proposed to represent reputation, memberships, credentials or affiliations — things that should not be sellable. The intent is good; the naive on-chain realisation is a privacy catastrophe.
Because a public SBT is just a token in a visible wallet, every SBT a wallet holds is public, and the combination is a dossier. A wallet holding a medical-history SBT, a criminal-record SBT, an employer SBT and a neighbourhood SBT has published a profile that any service can read and discriminate on. Non-transferability — the very property that makes SBTs meaningful — also makes them impossible to compartmentalise: you cannot move the sensitive one to a separate wallet. Public SBTs turn "portable reputation" into "permanent, correlatable surveillance".
The private-SBT fix reuses the membership machinery from this module. Instead of a visible token, the SBT is a commitment held in a Merkle tree per class, and the holder proves in zero knowledge "I hold an SBT of this class" (and optionally predicates about it) without revealing their wallet or any other SBT. It is Semaphore-shaped: identity commitment in a group tree, ZK membership proof, nullifier to prevent double-use where single-use matters.
synthetic — public SBT vs private SBT (illustrative)# PUBLIC SBT (leaks): token visible in wallet owner 0xWALLET holds: [MedicalSBT, RecordSBT, EmployerSBT] # -> anyone reads the full profile, cannot compartmentalise (non-transferable) # PRIVATE SBT (ZK membership): commitment in a per-class tree commitment = Poseidon(secret, sbt_class) classTree[sbt_class].insert(commitment) # Holder proves: "I hold an SBT of class = AlumniOf(X)" # via a ZK membership proof against classTree[AlumniOf(X)].root # revealing neither the wallet nor any OTHER class membership.
Private SBTs inherit every caveat of the membership pattern. The anonymity set is the number of holders of that class; a rare SBT held by three people hides little. And the issuer/admin who populates each class tree is the root of trust — a wrongly or maliciously issued SBT is a valid membership proof of a false credential.
The privacy/compliance spectrum
These tools do not force an all-or-nothing choice between transparency and opacity; they let a system sit at a chosen point on a spectrum and, importantly, make that point explicit. At one end, fully transparent public identity (everything visible). At the other, fully opaque (nothing revealed, no selective auditability). In between: selective disclosure (reveal only needed attributes), predicate proofs (reveal only that a condition holds), viewing/audit keys (grant scoped read access to a specific party), and compliance proofs (prove membership or non-membership against a curated set).
The design question is never "privacy or compliance?" but "who should be able to learn what, under what authority, and when?" The cryptography lets you answer precisely — this verifier learns this predicate, this auditor holds a viewing key for this scope — rather than defaulting to total exposure or total opacity. Making the chosen point explicit, and naming who holds which key, is itself part of a trustworthy design.
What the identity stack attests to (RootRaven)
When a verifier accepts a ZK identity presentation, it has established a precise statement: the presenter controls the subject DID, holds a credential (or SBT) signed by this issuer and not revoked as of the checked state, the disclosed predicates hold, and the presentation is unlinkable — under the signature scheme and circuit assumptions. That is a strong, checkable basis for access decisions.
It does not establish that the claim is true (the issuer could be wrong or deceived), that the DID corresponds to a particular real human (identifiers bind keys, not people), that the issuer or class admin is honest, or that trusting the holder with the requested action is safe. For RootRaven the discipline holds across the whole track: a valid identity proof is evidence of control and of signed, unrevoked possession under stated assumptions — not evidence that the underlying facts are real or that granting access is the right decision. Report the issuer, the revocation/freshness check, the anonymity set and the chosen point on the privacy spectrum as separate pieces of the evidence, and keep the access decision a human-governed judgment that the proof informs but does not make.
Exercise
A platform lets users gate features on soulbound tokens. v1 issues public SBTs for "verified professional", "health-plan member" and "community moderator", and checks them by reading the wallet. A privacy review forces a redesign. Explain concretely why v1 is a surveillance problem (including why non-transferability makes it worse), describe the ZK private-SBT redesign, and state exactly what a passing private-SBT proof does and does not establish.
Expected direction: v1 problem: every SBT sits visibly in the wallet, so anyone can read the full set — "verified professional" + "health-plan member" + "community moderator" together form a correlatable profile that any service can discriminate on, and because SBTs are non-transferable the user cannot move the sensitive one to a separate wallet to compartmentalise. The property that gives SBTs meaning (non-transferability) is exactly what makes the leak permanent and uncontainable. Redesign (ZK private SBTs): replace each visible token with a commitment = Poseidon(secret, class) held in a per-class Merkle tree; to gate a feature the holder produces a ZK membership proof against that class's root (optionally with predicates and a nullifier for single-use), revealing neither their wallet nor any other class membership. This is Semaphore-shaped membership. A passing proof establishes: the presenter holds an SBT of the proven class (membership under that class tree's root), unlinkably, under the circuit/commitment assumptions — and optionally that a predicate holds and that this is their first use in a context. It does NOT establish that the credential is true, that the issuer/class admin is honest or issued it correctly, who the holder is, that the anonymity set (number of that class's holders) is large enough to hide them, or that granting the gated feature is safe. The issuer remains the root of trust and the access decision stays a governed judgment the proof informs.
Check your understanding
- What do a DID and a VC each provide, and what does the ZK layer add?
- A DID is a self-controlled identifier proven by holding its key, with no central registrar; a VC is an issuer-signed claim about a subject (usually a DID). The ZK layer lets the holder present VCs selectively and unlinkably — disclosing only chosen attributes or predicates and proving control of the DID and a valid issuer signature, without revealing the raw credential or correlating across verifiers.
- Why are naive public soulbound tokens a privacy problem, and why does non-transferability worsen it?
- Every public SBT in a wallet is visible, so the combination of SBTs forms a readable, correlatable profile services can discriminate on. Non-transferability — the property that makes SBTs meaningful as reputation — prevents compartmentalising sensitive tokens into separate wallets, so the exposure is permanent and uncontainable.
- How does a private SBT work, and what caveats does it inherit?
- The SBT becomes a commitment in a per-class Merkle tree; the holder proves in zero knowledge "I hold an SBT of this class" (optionally with predicates/nullifier) without revealing their wallet or other memberships — Semaphore-shaped membership. It inherits the membership caveats: the anonymity set is the number of that class's holders (a rare SBT hides little), and the class admin/issuer is the root of trust for whether the credential is validly held.
- Name the main points on the privacy/compliance spectrum and the real design question.
- From transparent to opaque: full public identity, selective disclosure, predicate proofs, viewing/audit keys (scoped read access), compliance membership/non-membership proofs, full opacity. The real question is not "privacy vs compliance" but "who should learn what, under what authority, and when?" — which the cryptography lets a system answer precisely and make explicit.
References
- [1]Banco ZK — Vol. III (2025), Chapter 1 (DIDs, verifiable credentials, private SBTs) and the privacy/compliance spectrum — external educational reference
- [2]W3C — Decentralized Identifiers (DIDs) Data Model
- [3]W3C — Verifiable Credentials Data Model
- [4]Weyl, Ohlhaver & Buterin — Decentralized Society: Finding Web3's Soul (soulbound tokens)
References point to public, well-known technical sources for further study.
Related articles
