# Prove who you are without handing over your data

> Keep identity in your wallet and reveal only what a verifier asks for, trust keys without a certificate authority (KeyZ), verify every request at the reverse proxy (KeyZ Gate), keep FTP, HTTP and LDAP clients working. Run the demo in your browser.

Source: https://cappz.ai/tutorials/identity-and-trust

Your identity lives in your wallet. An issuer vouches once; every verifier after that sees only what it asks for, sealed to it and useless to anyone else. Keys are trusted because your wallet says so — and stop being trusted the moment you revoke them. Old systems keep working while you move them over.


## KeyZ versus X.509 certificates

KeyZ is how CAPPZ trusts keys without a certificate authority: your wallet signs which keys speak for it, for what and until when, and revokes them with a signed record. KeyZ Gate is the small edge gateway that checks every request against those declarations, next to nginx.

The biggest difference is not speed: it is how long a key keeps working after you revoke it. A certificate is trusted until the verifier's revocation list or cached OCSP answer expires; a KeyZ revocation is a signed record the verifier applies as soon as it arrives.


### How long a revoked key is still accepted

Run the demo to measure both on this device and see the revocation timeline.


### Where each one is better — and what to keep

Where X.509 stays, the table says why. Mostly it is policy — a law, standard or browser root program names the certificate — not something KeyZ, KeyZ Gate and the WAS Protocol cannot do. Policies and standards can be revised; until they are, comply and collect the evidence.

- **X.509 certificates**
- **KeyZ declarations**
- **Better here**
- Revocation takes effect
- When the verifier next fetches a CRL or its cached OCSP answer expires: hours to days; many verifiers fail open when the responder is down
- When the signed revocation record reaches the verifier (replication: seconds); checked offline, nothing to fail open on
- KeyZ
- What is proven per request
- mTLS proves who opened the connection; the request body is not signed
- Each request is signed over method, path, host and a body digest (RFC 9421 + RFC 9530); replay is refused by nonce
- KeyZ
- Answer a verifier gets
- Valid, or a reason code (expired, revoked, untrusted issuer)
- Signature state, key trust (declared, delegated, operator-listed, undeclared, revoked, expired), content digest, ledger anchor — separately
- KeyZ
- Who vouches
- One CA hierarchy; a CA compromise affects every certificate it issued
- The wallet itself, plus any number of attestations; a compromise is one wallet (guardian recovery rotates it)
- KeyZ
- Running cost
- Issuance and renewal per client, or a private CA (HSM, audits, staff)
- No issuance: the wallet signs its own declarations; verification runs on your own node
- KeyZ
- Rotation and old signatures
- After expiry, old signatures need a timestamp authority to stay verifiable
- A new declaration supersedes the old one; earlier signatures stay verifiable against the key valid then
- KeyZ
- Public websites (browser HTTPS)
- Required: browsers trust only public CA roots
- Not a replacement today: keep a public-CA certificate on nginx/Caddy
- Keep X.509 for now
- Policy, not technologyBrowser root programs and the CA/Browser Forum Baseline Requirements decide which certificates browsers accept, and the visitor’s browser enforces it. Technically a browser can already trust a key by its hash (WebTransport certificate hashes, which KeyZ Gate offers). Way forward: Keep the public certificate for public pages; use KeyZ behind it and for WebTransport; propose key-declaration trust to the IETF, W3C and browser root programs, with the revocation measurements as evidence.
- Regulated credentials
- Required where law names it (eIDAS qualified certificates and seals, PSD2 QWAC/QSealC, PIV/CAC)
- Keep them for compliance; KeyZ can carry the certificate hash in the declaration and add per-request signing beside it
- Keep X.509 for now
- Policy, not technologyThe rule names the instrument (eIDAS qualified certificates and seals, the PSD2 RTS for QWAC/QSealC, FIPS 201 for PIV/CAC). The limit is that the rule requires a certificate, not that a key declaration cannot do the job. Way forward: Comply today, and bring evidence (shadow reports, revocation windows) to the reviews: ETSI ESI standards, EBA reviews of the PSD2 RTS, NIST FIPS 201 revisions. EU digital identity wallets already accept SD-JWT VC, the format the identity vault uses.
- Hardware and tooling
- Every HSM, smart card, OS and language ships it
- Needs the SDK or any RFC 9421 library (Python, PHP, Go, Java exist); keys can live in WebAuthn authenticators
- Keep X.509 for now
- Adoption, partly technologyHSMs and smart cards sign with P-256 and Ed25519 keys that KeyZ can declare; their enrolment, management tools and audits are built around certificates. Partly technology: many do not support the secp256k1 wallet key itself. Way forward: Keep the wallet key in a WebAuthn authenticator or software vault and delegate P-256/Ed25519 keys held in the HSM or card; add KeyZ support to the tools as vendors adopt RFC 9421.
- Works with existing mTLS clients
- Yes
- Yes: a wallet-projected certificate is an ordinary X.509 client certificate, trusted as far as the wallet declared its key
- Both


## Retire PHP signature checks on evidence

Put KeyZ Gate in front of one PHP-verified route, verify in both, compare, and retire the PHP check only when the results match. Measured on Intel(R) Core(TM) i9-14900KS (x86_64, not ARM) with 12,000 requests per path at concurrency 32: mostly valid traffic plus tampered bodies, unknown keys, replays and a revoked key that PHP's key list still holds.


### What each deployment let through

- **Traffic**
- **PHP verifies**
- **KeyZ Gate (proxy)**
- **KeyZ Gate (auth_request)**
- Valid request
- served (200×10561)
- Tampered body
- all refused (401×360)
- all refused (422×360)
- Unknown key
- all refused (401×360)
- all refused (403×360)
- Replayed request
- 359 let through (200×359)
- all refused (401×359)
- Revoked key
- 360 let through (200×360)
- all refused (403×360)

Shadow report: 11,332 verdicts agree, 719 KeyZ stricter (revoked ×360, replayed ×359), 0 CAPPZ looser. Retire gate: ready (report 1a8c696618e9…).


### Throughput on the same machine (requests per second)

- **Deployment**
- **req/s**
- **p50 ms**
- **p95 ms**
- PHP verifies (today)
- 8,566
- 2.28
- 3.63
- Shadow comparison (temporary)
- 2,883
- 10.61
- 12.76
- KeyZ Gate proxies to the app
- 6,298
- 3.48
- 8.65
- auth_request + 4 KeyZ Gate processes
- 8,915
- 2.78
- 3.99

Honest reading: PHP's own check is native C and very fast (verify 0.083 ms). One KeyZ Gate process started at 3,028 req/s; buffering upstream responses, native Ed25519, a per-wallet evidence index and four processes hashed on cappz-wallet (so each signer's replay cache stays exact) brought the auth_request deployment to 8,915 req/s against PHP's 8,566. Cost per million verifications is a fraction of a cent either way ($0.0083 vs $0.0004 at an assumed $0.034/hour); the difference that matters is the revoked keys and replays.


### Run the comparison on your own instance


### PHP security note (CVE-2026-109618 / CVE-2026-109619)


## Try it with your own passkey

Biometrics stay on your device. Your phone or laptop matches your face or fingerprint; CAPPZ records only a hash of the passkey's id, which an issuer can cite when it vouches for you.


## How it works


### Identity vault

Each claim is a salted digest plus ciphertext in your identity wallet. Issuers sign digests (SD-JWT VC, ES256K, did:key) at an evidence level. You reveal claims one by one; the answer is sealed to the verifier and bound to its nonce.


### KeyZ: keys, not certificates

Your wallet signs which keys speak for it, for what, until when — and revokes early with a record every verifier replays offline. A certificate is only an envelope around such a key.


### KeyZ Gate: checked at the edge

A small gateway next to nginx verifies the signature, the key and the body hash for every request — microseconds, no database. The app only sees traffic that passed, with the reason attached.


## Costs and value

CAPPZ lines come from the published price sheets. The inputs below are assumptions — change them to yours.

Run the demo to price what it moved.


## Use it in your system

Install @cappz/sdk (subpaths /identity, /edge, /transports), or run the edge node next to your proxy. Every sample below is a file in packages/cappz-sdk/examples/; the TypeScript and Python ones run in the test suite.


### 1 · Run the edge node (KeyZ Gate, DMZ drop box, FTPS, LDAP)

```text
git clone https://github.com/CAL-CAPPZ-Attribution-Liberation/graph-ledger-notary cappz && cd cappz node -e 'crypto.subtle.generateKey({name:"Ed25519"},true,["sign"]).then(async k=>{ console.log("CAPPZ_NODE_KEY="+Buffer.from(await crypto.subtle.exportKey("pkcs8",k.privateKey)).toString("base64")); console.log("CAPPZ_NODE_PUB="+Buffer.from(await crypto.subtle.exportKey("raw",k.publicKey)).toString("hex"))})' > node.env set -a; . ./node.env; set +a CAPPZ_POW=<your node POW address> CAPPZ_NODE_DATA=./node-data CAPPZ_TLS_AUTO=1 \ deno run --allow-net --allow-read --allow-write --allow-env --allow-run=curl \ --allow-sys=networkInterfaces --unstable-net packages/cappz-edge-node/main.ts \ --relay 41445 --verify 41446 --legacy 41447 --ftps 41449 --ldap 41389 # try it curl -T statement.xml http://127.0.0.1:41447/legacy/v1/drop/statement.xml
```


### 2 · Identity flow in TypeScript (browser, Node, Deno, Bun)


### 3 · nginx: verify every request at the edge


### 4 · Legacy clients: curl drops, FTPS, LDAP


### 5 · Sign requests from Python


### 6 · Sign drops from PHP (replaces PHP-side checks)


## Keep what you have; move when ready

- **Today**
- **Works unchanged**
- **Next step**
- **Native**
- FTP drop folder
- inbox/ keeps working
- FTPS push or FTPS/SFTP pull into quarantine, with a receipt
- CPZ1 replication
- Raw HTTP POST
- unsigned drop: sender check "not-evaluated"
- sign with HTTP Message Signatures (Python, PHP, any language)
- CPZ1 relay
- Signature checks in PHP / the app
- nginx auth_request to KeyZ Gate
- verified headers only
- CA-issued client certificates
- wallet-projected client certificate (mTLS)
- KeyZ declarations
- Operator key list
- honoured, shown as "operator-listed"
- wallet declarations
- presented with each request
- TLS PEM files
- still win; reloaded when renewed
- CAPPZ_TLS_AUTO=1, rotated every 6 days
- delegated TLS key
- LDAP address book
- read-only LDAP face of the directory
- the ontology directory


## What it does not do

- Public websites still need a certificate from a public CA: browsers trust nothing else. Wallet-projected certificates are for peers, mTLS and enterprise clients.
- "Verified" means the issuer's signature, the digests, the key binding and the nonce all hold — not that the issuer checked well. The evidence level says how it checked; ledger anchoring is a separate check.
- Multicast discovery stays on the local network; across networks copies use the member table and the relay (replicate through firewalls).


## Related pages

- [replicate through firewalls](https://cappz.ai/tutorials/replicate-through-firewalls)

---

CAPPZ.AI — reliable AI anchored on-chain (COOL + COPE on the XRP Ledger). Core patent: [US 11,645,632 B2](https://patents.google.com/patent/US11645632B2/en). More: [https://cappz.ai/llms.txt](https://cappz.ai/llms.txt)
