# zkac-node CLI Install the `zkac` wheel from the repo root first (`maturin develop` or `pip install .`), then: ```bash pip install -e ./cli zkac-node --help ``` ## Quick start ```bash # 1. Create identities (one per machine / actor) zkac-node identity init # on admin machine zkac-node identity init # on recipient machine (separate ~/.zkac) # Recipient shares their issuance public key out-of-band: zkac-node identity show # prints issuance pk (hex) # 2. Start the server (separate machine or same, different data-dir) zkac-node serve --data-dir /var/lib/zkac --port 9800 & # 3. Pin the server's public key (printed at startup) zkac-node server pin localhost:9800 --key # 4. Create a registry (admin side) zkac-node registry create localhost:9800 --roles analyst,operator # 5. Grant recipient the 'analyst' role (only needs their public key) zkac-node grant --server localhost:9800 \ --registry --role analyst --to # 6. Recipient lists pending credentials zkac-node credentials list --server localhost:9800 # 7. Recipient collects (host:port:registry_id:role) zkac-node collect localhost:9800::analyst # 8. Recipient authenticates anonymously zkac-node auth --registry --role analyst --server localhost:9800 ``` ## Commands | Command | Description | |---------|-------------| | `identity init` | Generate issuance keypair under `~/.zkac/` | | `identity show` | Show issuance pk + owned registries + credentials | | `serve --data-dir D` | Run as a ZKAC server storing data in D | | `server pin --key ` | Pin a server's public key | | `registry create --roles r1,r2` | Create a new registry (fresh BBS+ issuer) | | `registry update --registry R --add-roles r3` | Add roles to a registry you own | | `registry get --registry R` | Fetch registry state from a server | | `registry list` | List locally owned registries | | `grant --server S --registry R --role X --to ` | Issue credential encrypted to recipient's pk | | `credentials list [--server S ...]` | Show local credentials + pending grants | | `collect ` | Fetch + finalize one pending credential | | `auth --registry R --role X [--server S]` | Authenticate via ZKAC handshake | ## Protocol All connections use a single encrypted channel: 1. **Anonymous handshake** (X25519 ephemeral DH + Schnorr server identity proof verified against a pinned public key) establishes an encrypted session. 2. The first encrypted frame selects the mode: - `{"op": "mgmt"}` — management commands (JSON request/reply loop) - `{"op": "auth", ...}` — BBS+ role authentication Admin-only commands (`post_grant`) require a BBS+ presentation of the registry's `__admin__` credential bound to the session transcript hash — unlinkable across grants by construction. ## Threat model The server is a **trustless, zero-information** relay: - All traffic is encrypted and server-authenticated (same handshake as role auth). - Registry state is opaque bytes with a BBS+ state certificate. The server cannot mutate state or impersonate the admin without the BBS+ issuer secret. - Grants are end-to-end encrypted from admin to recipient using ephemeral X25519 ECDH. The server stores only `(recipient_pk, eph_pk, ciphertext)` per grant — no registry ID, role name, or credential material. - Admin identity is unlinkable across `grant` calls (BBS+ presentations are rerandomized per call). - Recipient's authenticated session uses a fresh ephemeral transport key + an unlinkable BBS+ presentation, so the server cannot correlate sessions with the mailbox pk used at collect time. - No user IDs exist on the server. Clients are identified only by ephemeral keys (handshake) or issuance public keys (mailbox addressing). ## Storage layout Client (`~/.zkac/`): ``` identity.json issuance keypair (long-term secret) admin/.json BBS+ issuer + admin credential per owned registry credentials/_.json BBS+ credentials granted to this identity servers/.json pinned server public keys ``` Server (`--data-dir`): ``` server_key.json Schnorr keypair (long-term server secret) registries/.state raw RegistryState bytes registries/.cert raw state cert bytes mailbox/.json [{grant_id, eph_pk_b64, ciphertext_b64}, ...] ```