Privacy & Credentials
ChainRender's privacy model is about identity and claims: proving something about a user ("verified member," "over 18," "holds this credential") without revealing the underlying data. This is distinct from transaction privacy (hiding value transfers), which is a separate layer.
Reveal the claim, not the witness
The core principle (structural data minimization): the render only ever sees the result of a verification, never the private input. Heavy verification happens off-chain (at attestation time) or via a zero-knowledge proof; on chain, the resolver reads only a boolean or a statement label. The interpreter, the RPC, and the rendered output never touch the witness.
The built primitives
- Attested claims — identity as attested statements; trust delegated to an ownerless attester registry.
- Selective disclosure — member-of-set proofs (Semaphore-style, Groth16): prove you're in a set without revealing which member.
- Bound, single-use credentials — non-transferable (bound to a holder) and single-use (a nullifier prevents replay), fail-closed on a tampered proof.
These are unified behind a common convention (IPrivacyResolver) so an app gates on a family of privacy proof
without knowing which circuit backs it.
The built privacy primitives (in detail)
MemberSetVerifier— a Groth16 verifier for member-of-set proofs (Semaphore-style). A user proves they're in a set (an allowlist, a DAO, a credential set) without revealing which member — the proof's public signals are the set root + a nullifier, never the identity.CredentialResolver— renders a selective-disclosure credential: a valid proof resolves to the checkmarked statement label; a tampered proof or wrong root renders "Unverified," never a badge.BoundCredentialResolver+NullifierRegistry— bound (non-transferable, tied to a holder via the signal hash) and single-use (a nullifier prevents replay). Consuming the credential (spending the nullifier) is a separate, honestly-graded WRITE. A tampered proof, a wrong root, or an unknown statement all fail closed.AttesterRegistry/AttestationAdapter— attested claims: identity as statements an attester vouched for, with trust delegated to the ownerless attester set (+ EAS via the adapter).IPrivacyResolver— a unifying convention over the above, so an app gates on a family of privacy proof (member-of-set / attested-claim / selective-disclosure) without knowing which resolver backs it.
Adding a new statement (the zkAgeOver family) — generic by construction
The credential path is generic: CredentialResolver (kind credential) takes
abi.encode(statementId, a, b, c, pub), resolves the verifier for statementId from the immutable
StatementRegistry (the caller never supplies the verifier — anti-spoof), calls verifyProof on chain, and renders
✓ <label> or fails closed to Unverified. Adding a new statement — zkAgeOver, zkBalanceOver,
zkHoldsCredential — therefore needs only a circuit + its trusted setup + the generated Groth16 verifier contract,
and a registry entry (statementId, verifier, label, nPublic). No change to the resolver, the credential kind, or
the viewer — rendering a new statement is already wired (proven by test/ZkStatementGeneric.test.js, which registers
a new age-over-18 statement against an unchanged resolver and renders ✓ Age over 18).
The honest status: these statements are resolver-ready, circuit-pending. The trust-bearing artifacts are the
circuit and its trusted setup, which must be authored and audited (or produced by a ceremony) — never fabricated. Until
a real verifier is registered, the viewer renders Unverified (fail-closed), never a fabricated badge. The resolver's
one caveat is nPublic: it supports single-public-input statements today; a statement needing more than one public
input either folds them into one committed field or takes a small, additive registry/resolver extension.
The honest boundary
Privacy of the claim is provided; privacy of the read path is not — an RPC still sees which app you loaded. ChainRender states this plainly rather than overclaiming. See the internal privacy audit for the full analysis.
Related
- Resolvers & Data Binding — credentials are resolvers.
- Reference → Resolver Catalog
Optional project encryption (builder)
The visual builder can encrypt your saved projects at rest in the browser (IndexedDB) with a password you set (WebCrypto AES-GCM + PBKDF2). It is opt-in and honest about the tradeoff: there is no password recovery -- if you forget it, the encrypted projects are cryptographically unrecoverable. The tool offers genuine (non-fake) safety nets instead: change-password (while unlocked), export a decrypted backup (while unlocked), and delete-and-restart (if locked out). It protects your in-progress creative work; it is not tied to keys or funds (AI keys are session-only, never persisted).