TL;DR: CuK + Shield of Privacy make non-interactive ZK proofs usable at scale - local verify, issuer-silent, and pairwise-unlinkable with an auditable breadcrumb when lawfully required.
Missed Part II?
Read: Part II – ZK Proofs for a Human-Only, Privacy-First Web
In Part I, we made the case for a different web: one where communities and markets ask for proofs, not profiles – “18+?”, “in-country?”, “one human?” – and get a cryptographic YES/NO instead of a dossier. In Part II, we showed the backbone that makes this real at web scale: non-interactive zero-knowledge proofs (NIZK) so verification is local, issuer-silent, and unlinkable by design with accountability using Concordium's Idp-based model.
This final part answers the question every operator asks when the cryptography fades and the dashboards appear: How do we run that privacy promise day-to-day – fast for good users, auditable when it matters, and impossible to bend into tracking?
The answer is Contextual Uniqueness – implemented in AesirX as a tiny, disposable pseudonym called the Contextual Uniqueness Key (CuK) and a lightweight client component, the Shield of Privacy (SoP). CuK gives a site just enough to recognize this proof in this context, while refusing to be useful anywhere else. Shield of Privacy (SoP) is the lightweight client component that derives and manages CuKs in the browser, entirely client-side.
Note: CuK is currently under development in partnership with Concordium.
What “context” really means (and why it matters)
When we say context, we mean a precise tuple – (relying party, action, time window, policy scope). A forum gating DMs is a different context from a marketplace gating listings; a Danish content release is a different context from a US one.
CuK is derived on the user’s device from inputs that reflect that context. Conceptual derivation:
Context tuple for CuK derivation
- RP origin salt (pairwise, origin-bound)
- Action/purpose (e.g., “create_account”, “post_dm”)
- Time-window nonce (limits replay)
- Policy fingerprint (optional, binds local policy)
CuK = H( proof_hash ∥ rp_salt ∥ action ∥ window_nonce [ ∥ jurisdiction/policy ] )
- proof_hash: the outcome of the NIZK (“18+”, “in DK”), not identity.
- rp_salt: a value bound to the relying party (e.g., canonical eTLD+1), so two sites never see the same pseudonym.
- action: the gated surface (sign-up, DM unlock, listing, payout).
- window_nonce: a fresh value for temporal uniqueness and replay resistance.
- jurisdiction/policy (optional): to isolate legally distinct flows.
Because CuK includes rp_salt and action, colluding sites can’t line up their logs and learn anything about a person. Because it includes a fresh window_nonce, from minutes to hours, configurable by policy, replaying an old event won’t work. And because it’s all hashed client-side, nothing personal leaves the device.
Verification runs locally, issuers remain silent, credentials stay in the wallet, CuK is context-scoped, and any on-chain anchoring is hash-only.
Contextual Uniqueness Key (CuK) – Verification (Diagram 1)
The CuK lifecycle: from proof to practice
First arrival. The page asks a narrow question. The wallet generates a non-interactive proof locally. SoP derives CuK from the proof outcome and the current context, the site verifies the proof without contacting an issuer, authorizes the action, and logs a verdict with a timestamp – not an identity.
Optional anchoring. For auditability, the verifier can anchor a fingerprint of the event – hash(proof_hash ∥ CuK ∥ domain ∥ timestamp) – to Concordium. This creates an immutable breadcrumb that refers to an event, not a person: no raw proof, no PII, nothing reusable across properties.
Contextual Uniqueness Key (CuK) – Reuse (Diagram 2)
Return visits. If the user comes back to the same site within a valid window, SoP can recognize an encrypted, origin-bound pseudonym in the browser and validate it client-side for instant entry. If nothing valid is present, the wallet flow runs again. Either way, there’s no issuer telemetry on normal use.
Change over time. People move countries and have birthdays. A fresh wallet proof simply leads to a new CuK; past pseudonyms don’t resurrect. If the gate is jurisdictional, the policy parameter is part of the derivation so each legal surface remains isolated.
Contextual Uniqueness Key (CuK) – Cross-Site Use (Diagram 3)
Re-use without re-identification (opt-in, client-only)
Sometimes people want to skip the wallet step on other AesirX-enabled sites. Re-use is possible only on the user’s terms and it still refuses to become a universal handle.
When a user opts in, SoP creates a small encrypted token tied to the browser’s origin/keystore. On a new site, SoP computes:
CuK_new = H( token ∥ new_rp_salt ∥ action ∥ new_window_nonce )
The second site sees a valid, domain-scoped pseudonym that cannot be matched to the first. Clear storage or decline reuse and the system falls back to a fresh wallet proof. Convenience never eclipses unlinkability.
Contextual Uniqueness Key (CuK) – Reveal by Court Supervision (Diagram 4)
Auditability without surveillance (the due-process path)
Privacy by default survives because accountability is narrow and lawful. Court orders and disclosure governance vary by jurisdiction; the model describes the intended due-process pattern. If a serious incident demands it, law enforcement targets a specific event – “the 18+ proof used on example.com at 15:03 UTC.” Using the anchored fingerprint, the verifier supplies its presentation record (challenge, response/commitment, time, domain). The issuer confirms credential validity, not a usage history. Designated privacy guardians, independent of issuer and verifier, complete an N-of-M disclosure under court supervision in the IdP-based model on Concordium. There’s no issuer usage logging, no corporate backdoor – only a precise unmasking for that one event.
Threat model and how the design holds
- Colluding sites. Pairwise salts ensure CuKs are different across relying parties; anchors carry only hashed fingerprints. Collusion yields nothing linkable.
- Issuer observability. Verification is local; issuers aren’t pinged at proof time. Revocation is checked via anonymous status lists or cryptographic accumulators that the client fetches on a fixed schedule; there are no per-user callbacks to the IdP, so the flow remains issuer-silent.
- Replay & theft. Window nonces and proof freshness make replays fail; reuse tokens are encrypted, origin-bound, and short-lived.
- Script abuse. Consent and verification are first-party; CSP and origin binding keep third-party beacons out of the loop.
- Data minimization. Verifier logs store verdict + timestamp, not identity; Anchors never contain raw proofs or PII; they are hash-only and scoped to a single event.
The net result: useful to an operator, useless to a surveillance graph.
Operating at the edge (performance that users feel)
Because proofs are non-interactive, verification can run at the CDN/edge with cached public parameters. P95 latencies remain low even on mid-range phones and bad connections. Status lists cache well; anchors are tiny. You get safety that feels instant and doesn’t leak.
Where teams go wrong (and how not to)
- Reusing global IDs. If your app tries to “help itself” by storing a user identifier, you’ve undone unlinkability. Treat CuK as disposable, context-scoped truth.
- Issuer callbacks in production. If verification calls back to an issuer, you’ve built a locator beacon. Keep verify local; refresh status anonymously on a schedule.
- Logging more than verdicts. If you store proofs or PII, you’ll inherit breach and compliance risk with no benefit. Log the decision and the time – nothing else.
- Third-party scripts in consent/verify. Run it first-party or you’ll leak the very moments privacy law cares about most.
- Salt management hygiene. Rotate rp_salt when changing domains/tenants.
How Part I, II, and III fit together
- Part I set the stakes and the operating model: verify the attribute, not the person.
- Part II supplied the engine: NIZK for local, issuer-silent, unlinkable proofs.
- Part III makes it run operationally: CuK + SoP give each proof a context-scoped pseudonym for UX, rate-limits, and audits – without handing anyone a cross-site handle.
That’s the trilogy: why, how, and how it stays honest at scale - part IV will explain how we can avoid chat control by ID verification.
Age verification without surveillance
Ask the narrow question: 18+. The answer is produced locally in the IdP wallet as a zero-knowledge proof, and the flow remains issuer-silent. The browser, not the site, derives an origin-bound CuK for this action and time window. The site verifies locally, authorizes or denies, and discards the proof. No names, no addresses, no account numbers move. Anchors never contain raw proofs or PII; they are hash-only and per-event. Revocation relies on anonymous status lists or accumulators fetched on a schedule, never per-user callbacks. If real harm occurs, due-process unmasking is available: a court order, N-of-M privacy guardians, and a simple issuance confirmation by the IdP, with no usage logs.
Start where abuse hurts most: sign-ups, DM unlocks, listings, payouts. Measure pass rate, false positives, chargebacks, first-contact attempts, moderator hours, and law-enforcement lead quality tied to event-specific anchors. You reduce spam and fraud not by harvesting more data, but by asking only what policy requires.
The bots will keep rising. Surveillance will not save the web. Private verification that ships can. Keep people private and communities safe, and keep the web human.
Ronni K. Gothard Christiansen
Technical Privacy Engineer & CEO, AesirX.io