Skip to main content

Architecture

The components zkCoins is built from, which repository each lives in, and the boundaries between them. Every claim here maps to a normative rule in Specification §§4, 6, and 7 — components and seams in §6, the naming layer in §4.3, the wire and RPC contracts in §7 — and each section below links to the one it rests on. This page is the map, not the norm.

The picture

Repositories

RepositoryWhat it is
nodeThe trustless kernel. Bitcoin scanner, nullifier accumulator, proof verification, state store, prover, publisher, Nostr transport. Speaks kernel.v1 gRPC on an operator-internal channel. Owns the value-bearing database.
apiThe public REST surface. Consumes the kernel RPC, enforces the capability gate, owns a non-value-bearing database. Its operator enables features individually.
sdkTypeScript client: BIP-39/32 derivation, Schnorr signing, typed REST client, account adapter. Keys stay on the device.
appThe end-user wallet. Holds the seed, presents receive identities, payees, and contacts only as names (Implementation Mandate).
explorerStateless frontend that renders a disclosed view. Holds no keys.
docsThis site, including the normative Specification.
research · plonky2Protocol research and upstream references · the proving stack.

bitcoind and the Nostr relay are upstream software, run by the operator or reached externally.

The four boundaries

Custody sits in the app. The key that authorises spending exists only on the user's device and reaches no node and no server (Requirement 5). The kernel holds viewing keys and the account's Nostr key; it detects, decrypts, proves, and transports, and it cannot spend.

The kernel speaks gRPC, the API speaks REST. kernel.v1 runs on loopback, a private container network, or mTLS between containers. Every public request — wallet, SDK, app, explorer — arrives at the API layer, which terminates it and enforces the capability gate. The kernel remains the sole reader and writer of the value-bearing store, so a compromised API layer can forge no proof, mint no coin, and double-spend nothing (§6.1, §7.8). It is not harmless: like any component between the wallet and the prover it relays a send's output_templates, and the thin wallet runs no Poseidon and so cannot recompute output_coins_root from what it posted. A compromised relayer can therefore redirect or drop the outputs of a send the wallet then cooperatively signs — a correctness failure, not a custody one, whose effect on the sender is nonetheless the same as theft (§6.6 Send-intent integrity).

Names live above the kernel. The app and API layers resolve an account's email-style NIP-05 name, and an API running wallet issues names for the accounts it serves. The kernel works from op_pubkey, nprofile, and addr_sig-carrying objects, which keeps DNS and certificate authorities out of the trustless core and lets a publisher back end run on a key, a relay presence, and its own hand-off endpoint (§4.3).

The explorer reads, it is not privileged. It fetches the public chain projection and encrypted blobs, then applies a bearer view secret — zkview for one transaction, zkavk for a history — in the browser. Bearer secrets are not node authorisations; they widen only what their holder can already decrypt (§5.1, §6.4).

API features

Each feature is off until the operator enables it, and GET /v1/info advertises exactly the enabled set. A client treats anything absent from that array as absent, and a request against a disabled feature is answered 404 feature_disabled.

FeatureWhat it opens
walletProving, submission, and capability-gated pull for the accounts this API serves, plus their NIP-05 names
explorerThe public read surface: chain projection, accumulator, inclusion proofs, blob fetch
publisherThe §7.6 hand-off, forwarded to a kernel whose publisher part is on
lightning_bridgeLightning ⇄ zkCoins swaps at the operator edge (Lightning bridge)
mail_bridgeSMTP interop for the account's NIP-05 identifier (Mail bridge)

Publishing and proving are kernel work in every case; the feature opens the door, the kernel does the job and owns the state.

Deployments

DeploymentKernelAPIServes
Sovereign personalprover onwallet, own account onlyits owner
Public serviceprover on, publisher optionalwallet for delegating accounts, plus whatever else the operator choosesits users
Validating nodeverification and accumulator onlynonenobody — it follows and checks the chain
Publisher back endpublisher onpublisher alonespenders handing over nullifiers
Explorer hostverification and accumulatorexplorer alonethe explorer frontend

The boundary is between components, not processes. A small deployment runs kernel and API as one binary against one database process, provided each component owns only its own schema and the public listener is the API's. A public service splits them into separate repositories, containers, and instances — the arrangement §6.1 Running a node describes.

Where to read further