⚠️ Alpha · testnets only. ChainRender is in active alpha and deployed on testnets only — Ethereum Sepolia, the Robinhood Chain Testnet, and the Circle Arc Testnet — not on any mainnet. Contracts, addresses, and APIs may change. Do not use with real funds or production assets.
Concepts

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

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).