> For the complete documentation index, see [llms.txt](https://parad0xlabs.gitbook.io/parad0xlabs-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://parad0xlabs.gitbook.io/parad0xlabs-docs/dark-null-protocol-zk-privacy-and-identity/kvac.md).

# KVAC Credential

**Prove you are authorized — with no account, and no handle that follows you around.**

**For someone new to crypto:** imagine a paid API that needs to know you are allowed in — you bought the "pro" tier, you get 100 calls an hour — but you do not want to hand it a username, a wallet you reuse, or anything that lets it stitch your visits together into a profile.

A KVAC credential lets you do exactly that. The service issues you a credential once. After that, every time you call, you present a fresh proof that says "I hold a valid credential for this tier" — and nothing else. Two visits look completely unrelated, even to the service that issued the credential. You are anonymous, but still accountable: the proof is real, and a single-use stamp stops you from replaying one credential as if it were many.

**For the technically inclined:** this is a **keyed-verification anonymous credential** — an algebraic MAC over the Ristretto group (the MAC-GGM scheme, Chase–Meiklejohn–Zaverucha, CCS 2014 / eprint 2013/516; instantiated over ristretto255 by the Signal Private Group System, eprint 2019/1416). The issuer holds a secret key and MACs a set of attributes. The holder later presents a zero-knowledge proof that it possesses a valid MAC over those attributes — revealing nothing that links one presentation to another — and the verifier checks the proof **with the same secret key**.

***

## Why a keyed credential and not a SNARK

The whole point of x402 is one gateway that both **sells** access and **checks** it. When the same party issues and verifies, you do not need a credential designed for a stranger to verify. That changes the cryptography you should reach for.

| Approach                       | Trusted setup?       | Verify cost                       | Fit for an x402 gateway                                                          |
| ------------------------------ | -------------------- | --------------------------------- | -------------------------------------------------------------------------------- |
| Groth16 SNARK credential       | **Yes** — a ceremony | one pairing                       | works, but the ceremony is a liability with no upside here                       |
| BBS+ / PS credential           | No                   | a **pairing**                     | Solana has no cheap pairing                                                      |
| `SHA256(...)` "anon-cred" stub | No                   | a hash                            | proves nothing in zero knowledge; trivially linkable — **not** a real credential |
| **KVAC (this page)**           | **No**               | a handful of group-ops + one hash | the verifier already holds the key, so no ceremony and no pairing                |

Keyed verification means **the verifier is the issuer** (or shares its key). So there is **no trusted setup** — nothing to ceremony, nothing for a single party to quietly subvert — and verification is a few Ristretto group operations instead of a pairing. Ceremony-free and cheap, by construction, precisely because issuer and verifier are the same gateway.

The cost of that convenience: the verifier needs the secret key, so **only the gateway can verify** — this is not a credential a random third party can check. That is the right trade for "one gateway issues and checks," and the wrong one if you need public verifiability.

***

## How a call works

```mermaid
sequenceDiagram
    participant A as Agent (holder)
    participant G as Gateway (issuer + verifier, holds sk)
    participant C as Solana (nullifier record)
    A->>G: authenticate once
    G-->>A: issue credential (MAC over tier, rate-limit, ms)
    Note over A,G: later — a paid x402 call
    A->>G: ZK presentation + per-context nullifier
    G->>G: verify proof with sk (off-chain)
    G->>C: record nullifier (single-use)
    C-->>G: ok / AlreadyRecorded
    G-->>A: access granted (or rejected as replay)
```

Two pieces matter:

* **Verification runs off-chain, in the gateway.** Keyed verification needs the secret key, and the gateway already has it. So the proof is checked on the gateway's server — there is no secret key on-chain and no per-call proof-verification cost on Solana.
* **The chain only records the nullifier.** Each presentation carries a per-context nullifier `n = ms·H_ctx`, derived from a hidden attribute `ms` the gateway never learns. Recording it on-chain single-use is what stops replay: the same credential, in the same context, twice, is rejected.

In the x402 integration, the context binds to the resource being paid for: `context = SHA256(domain ‖ resource-scope ‖ epoch)`. So the nullifier becomes an **anonymous rate-limit key** — one action per credential per resource per epoch — and it replaces the identity-leaking payer pubkey that a naive payment proof would expose.

***

## The construction (briefly)

Three attributes: `tier`, `spend_cap`, and a hidden secret `ms`.

* **MAC:** `V = W + (x0 + x1·t)·U + Σ yi·Mi`, credential `(t, U, V)`.
* **Presentation:** the holder re-randomizes with a fresh blind `z`, commits to each attribute, and proves a sigma protocol over the relations that tie the commitments to a valid MAC — including `Z = z·I`, which the verifier recomputes as `Z = CV − (W + x0·Cx0 + x1·Cx1 + Σ yi·Cyi)` using `sk`.
* **Nullifier binding:** `n = ms·H_ctx` for the **same** `ms` committed in the presentation, tied together by a shared nonce across the two announcements so the nullifier cannot be forged independently of the credential.
* **Fiat–Shamir:** the challenge hashes every public point — generators, commitments, context, the nullifier, the announcements — so nothing can be swapped without breaking the proof.

Issuance is clear-attribute (the gateway already knows the agent at issue time), but `ms` is withheld via `M3 = ms·Gm3` plus a proof-of-knowledge — so the gateway can never precompute your nullifiers. Wire format is a fixed **448 bytes** with canonical-encoding checks on every field. Blind issuance (hiding attributes at issue time too) and an on-chain **tier predicate** (set-membership) are documented as v2 — see Status.

Full derivation and the cross-checked danger zones live in the crate's `SCHEME.md`.

***

## What is proven on devnet

The host scheme is implemented and tested, and the single-use record is proven on devnet against the shared nullifier program `dark_nullifier_record` (`AFTuz5s58FEwQoQBxAdvWFrXAVnS9XzC43XQgL2Canpg`):

| Scenario               | Context                   | Result                                                           |
| ---------------------- | ------------------------- | ---------------------------------------------------------------- |
| Record nullifier `n_A` | `gateway/quote : epoch-7` | recorded (41-byte PDA)                                           |
| **Replay `n_A`**       | same                      | reverts `AlreadyRecorded` — `Custom(10)`, the Sybil bound        |
| Record `n_B`           | `gateway/infer : epoch-7` | recorded — same credential, different context, a fresh nullifier |

The same credential produces unlinkable presentation bytes across contexts but the same nullifier *within* a context — so it rate-limits without revealing identity. The host ceremony (issuance proof + `ms` PoK + two-context present/verify + adversarial checks) verifies end to end.

**Mainnet cost is measured, not estimated** — rent is a protocol constant identical on both clusters. Going live would be **≈0.52 SOL one-time** program deploy (or \~0 reusing an existing nullifier-record program) plus **≈0.00118 SOL per paid call** — of which almost all is *recoverable* rent; the only truly spent cost is the 0.000005 SOL transaction fee, because the verifier is off-chain and adds no compute. At a million calls that is single-digit SOL truly burned. (Routing nullifiers through ZK-compressed accounts would cut the locked rent \~100–1000× — an in-design optimization, not built here.)

***

## What it does and doesn't cover

This buys **anonymous, accountable access** to a paid x402 endpoint: prove entitlement without an account, and have a single-use stamp stop replay. It is one privacy leg — the **identity/access** leg. It hides *who* is calling; it does not by itself hide the payment itself or the network path.

Unforgeability holds in the **generic-group model** (the right model for MAC-GGM) and the nullifier is pseudorandom under DDH in the random-oracle model. That is a security *argument*, not a guarantee against a real adversary, and the code is **Public Beta, non-custodial, not yet audited**. The credential custodies no funds, so a failure is a recoverable access/privacy issue (rotate the key, re-issue) rather than fund loss — but it should not carry real identity or value until it has had external review. Never describe it as audited.

***

## Status

| Component                                                                           | State                                                                                     |
| ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Host KVAC scheme (issue / present / verify / nullifier, 448-byte wire)              | **Implemented**, 26 tests green (13 lib + 13 scheme; 29 incl. the x402 integration crate) |
| Gateway verification (off-chain, holds `sk`)                                        | **Devnet**                                                                                |
| x402 binding (`context = SHA256(domain ‖ scope ‖ epoch)`, anonymous rate-limit key) | **Devnet**                                                                                |
| Single-use nullifier recorded on-chain (`AFTuz5s5…`, replay → `AlreadyRecorded`)    | **Devnet-proven**                                                                         |
| Mainnet launch cost                                                                 | **Measured** (\~0.52 SOL deploy + \~$0.21 recoverable/call) — *not* deployed to mainnet   |
| Tier predicate (set-membership) · blind issuance (v2)                               | **In design**                                                                             |
| External audit                                                                      | **Not yet** — Public Beta, non-custodial, not yet audited                                 |

The scheme is **upgradeable** and stays on devnet by design until a reviewer has looked at the gateway verifier and the nullifier program.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://parad0xlabs.gitbook.io/parad0xlabs-docs/dark-null-protocol-zk-privacy-and-identity/kvac.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
