Prove You're Over 18 Without Showing Your Birthday
Designing selective disclosure with BBS+ signatures โ no trusted issuer in the loop
There is a quiet indignity built into most digital credentials: to prove one small thing, you have to reveal everything. Hand over your driver's licence to prove you're over 18, and you've also disclosed your exact birth date, your address, your licence number, and a photo. The verifier asked one question; you answered with your whole identity.
The cryptographic fix for this has a name โ selective disclosure โ and a particularly elegant instrument: BBS+ signatures. The promise is simple to state and surprisingly deep to build: prove a single attribute from a signed credential, reveal nothing else, and don't phone home to the issuer to do it. This is the story of how NAOMS approached that, what it actually built first, and โ because the Honesty axiom demands it โ exactly where the math is real and where, for now, it's stood in for.
The all-or-nothing problem with ordinary signatures
Start with how a normal verifiable credential works. An issuer (say, a hive, or a government) takes a bundle of claims โ name, birthdate, github_handle, over_18 โ serializes the whole thing, and signs it with one signature over the entire blob. That signature is wonderful for integrity: change one byte and it breaks. But it has a side effect that's easy to miss.
Because the signature covers the whole serialized document, you can only verify it by presenting the whole document. The signature and the data are welded together. There's no way to lop off the fields you'd rather not share and still have the signature check out โ remove birthdate and the signature no longer matches what was signed. So ordinary signatures quietly enforce all-or-nothing disclosure. To prove over_18 is true and signed, you reveal birthdate too.
The usual workaround is to ask the issuer for a fresh, narrow credential each time ("please sign a credential that says only over_18"). That works, but it drags the issuer into every interaction โ they learn every time and everywhere you prove something โ and it makes the issuer a permanent, online dependency. We wanted to avoid both.
What BBS+ changes
BBS+ is a signature scheme with a property ordinary signatures don't have: it signs a list of messages in a way that lets the holder โ not the issuer โ later produce a proof about a chosen subset.
The shape, in plain terms:
- The issuer signs your credential as an ordered list of attributes:
[name, birthdate, github_handle, over_18]. One BBS+ signature, computed once. - Later, when a verifier asks "are you over 18?", you โ the holder โ take that signature and derive a brand-new proof that reveals only
over_18and keeps the rest hidden, while still proving they were all part of the same issuer-signed credential. - The verifier checks the proof against the issuer's public key. It comes back true. They learn
over_18 = trueand nothing else. The issuer was never contacted.
Two things make this powerful. First, the issuer is offline โ they signed once, long ago, and play no part in the moment of disclosure. Second, the proof can be made unlinkable: each presentation can look fresh, so a verifier today and a verifier tomorrow can't trivially collude to recognize "the same person proved something here and there." You prove the predicate, not your fingerprint.
This is the same north star as the verifiable-claim work elsewhere in NAOMS โ don't make the verifier trust a middleman โ pushed one level deeper: not even the issuer needs to be in the loop.
The design NAOMS settled on: dual proof
Here's the first architectural decision worth dwelling on, because it's where pragmatism met purity.
NAOMS did not rip out its existing Ed25519-signed credential path and replace it wholesale with BBS+. Instead, the design is dual-proof: a credential carries both a conventional Ed25519 signature (the path everything else already understands and trusts) and a BBS+ signature (the one that enables selective disclosure).
Why carry two? Because a verifier that doesn't know or care about selective disclosure can still validate the credential the ordinary way, and a verifier that does want a single-field proof can use the BBS+ leg. It's a compatibility seam: the new capability is additive, not a flag-day cutover. The cost is two signatures to manage and keep consistent; the benefit is that nothing downstream breaks while the privacy feature grows up.
The first phase landed with 71 passing tests across four parts:
- The BBS+ operations themselves, plus opaque key handles (you pass around a handle, never the raw key material) and a kill switch for the whole subsystem.
- Credential creation in the dual-proof shape, with economic and governance guards on issuance.
- Building the selective-disclosure presentation, including forced field disclosure (more on this below).
- Verifying a presentation against required disclosures and a community's norms.
And it was wired into the broader credential pipeline the same day.
The free preview ends here. Below the line: the honest disclosure about Phase-1 simulation versus real BBS+, the nine ethics constraints (AE-01 โฆ AE-09) and why a privacy primitive needed an ethics layer at all, the "forced disclosure" design tension, and the threat model that shaped it.
โ premium โ
The honest part: Phase 1 simulates the math
If you take one thing from this piece, take this, because it's the kind of detail that's easy to bury and dishonest to omit.
Phase 1 does not run real BBS+ cryptography. It uses an HMAC-based proof simulation with the full API contract in place for a later phase to swap in real BBS+ math. In other words, the interfaces are real โ create, present, verify, the dual-proof shape, the disclosure logic โ and 71 tests exercise them. But the actual zero-knowledge BBS+ proof is stood in for by a simulation that has the same contract (same inputs, same outputs, same API) so that a later phase can swap in the real cryptographic library (zkryptium, via a Rust binding) without the rest of the system noticing.
This is a legitimate engineering pattern โ build the system around the seam, prove the plumbing, then drop in the hard crypto โ but it must never be described as if the privacy guarantee is already live. As of this writing, the feature is still in active development, not declared finished: the design and the scaffolding shipped; the production cryptographic guarantee is the explicitly-deferred second phase. A verifier in the first phase gets the workflow of selective disclosure; the unforgeability and unlinkability that make it secure arrive with the real BBS+ swap. Anyone deploying this for real privacy must wait for that swap โ and the design was built precisely so they can, without re-architecting.
A note on sourcing: there are no retained chat logs for this week, so any sense of "the team chose pragmatism here" is inferred from the work itself, not from a quoted decision. What's verified is the code; the motivation we ascribe to it is reasonable reconstruction, not a recovered quote.
Why a privacy primitive needed nine ethics constraints
You might expect a selective-disclosure feature to be ethically unambiguous โ it reduces what you reveal, surely that's all upside? The design disagreed, and that disagreement is the most interesting thing about it. The work shipped with nine ethics constraints enforced directly in the architecture, folded in during an explicit alignment pass that integrated a structured risk process and a failure-modes analysis.
The reason is that powerful privacy is dual-use. The same machinery that lets an honest person prove "over 18" without their birthdate also lets a credential be sliced in ways the issuer never intended โ or lets a verifier demand disclosures that defeat the privacy point. So the architecture encodes guardrails directly, rather than leaving them to policy text. Two are worth calling out:
- Forced field disclosure: some fields can be marked as always revealed in any presentation derived from a credential. This is the counter-weight to pure selectivity โ an issuer can insist that, say, a revocation pointer or an expiry is never hidden, so selective disclosure can't be used to present a stale or revoked credential as fresh.
- Required disclosures and community norms: verification is evaluated against what a given community requires be disclosed, so a group can set a floor ("here, you must reveal X to do Y") without breaking the holder's control over everything else.
The lesson here generalizes: a privacy mechanism without an ethics layer isn't neutral, it's just uncontrolled. Encoding the constraints in the architecture โ not in a README โ is what keeps "selective disclosure" from quietly becoming "disclose whatever is convenient and hide the rest."
Fellow travelers
NAOMS did not invent any of this, and the honest move is to say so generously.
- BBS+ signatures themselves are the product of a long line of academic and standards work (the IRTF/W3C credentials community has been refining BBS for verifiable credentials for years). The whole feature is an adoption of their primitive. What we took: the entire selective-disclosure capability. What we did differently: we wrapped it in a dual-proof seam and an ethics layer, and โ honestly โ we shipped a simulation of it first.
- UCAN and Biscuit come at "prove a narrow right without the issuer online" from the capability direction rather than the attribute direction: a holder can attenuate a token offline, narrowing what it permits, and any party with the issuer's public key verifies it without a callback. What we learned: the offline-attenuation instinct โ that the holder, not the issuer, should be the one narrowing scope at presentation time. What we did differently (and why): BBS+ selective disclosure narrows what you reveal about yourself, whereas Biscuit/UCAN narrow what you're allowed to do; NAOMS uses both ideas in different places, and chose BBS+ here because the question was about disclosing identity attributes, not delegating authority.
- KERI (keri.one) shares the deepest value: the issuer should not be a permanent online dependency. Its key-event logs make identity auditable without a registry; BBS+ makes attributes provable without an online issuer. Same spirit, different layer.
None of these are competitors. They're a community converging โ from capability tokens, from credential signatures, from key management โ on one stubborn principle: the person presenting the proof should need the issuer as little as possible.
What this buys, and what's still owed
What the design buys, today: a coherent dual-proof credential architecture, a worked-out disclosure-and-verification surface, an ethics layer enforced in code, and 71 tests proving the plumbing โ all built around a clean seam so the real cryptography can drop in.
What's still owed, honestly: the real BBS+ math. The first phase is a faithful simulation with the right contract; the unforgeable, unlinkable cryptographic guarantee is the second phase (the zkryptium swap), and the feature stays in active development until that lands. The privacy is designed; it is not yet proven by the math in production.
That gap is not a failure to hide โ it's the most honest thing in the whole architecture. The system was built so the day the real signatures arrive, nothing above them has to change. Until then, the right description is the precise one: we built the shape of selective disclosure, with the ethics baked in, and a place for the cryptography to land.
Written by AI agents from real project logs; owned and edited by Mujo.