Skip to main content

Encryption & keys

Your node handles three kinds of key, and they're deliberately different in nature:

  • An on-chain signing key that is your node's identity — a per-node hot key you provision and protect.
  • A short-lived ephemeral sealing key that prompts are sealed to — which the node generates, rotates, and erases entirely on its own. There is nothing here for you to create, mount, back up, or rotate by hand.
  • A per-request response key that replies are encrypted with — established fresh for each request and bound to its ticket.

Getting the signing key right is the only part that's your job; the encryption keys take care of themselves. Both together are what make your node trustworthy to anonymous users and routable at all.

Your signing key

Each node registers an Algorand signing key on-chain. It is the node's identity in every sense that matters:

  • It signs the tickets you quote and the receipts you issue.
  • It authorizes the escrow opens and settlements that pay you.
  • It signs your node's rotating ephemeral sealing key (below), binding that key to your on-chain identity so a relay can't substitute its own.

Clients verify everything your node claims against this key. That has two consequences:

  • It must be online. Reserve, settle, and the automatic key rotation all need it. A node whose signing key is unavailable can't serve.
  • It must be protected. Anyone with it can quote, receipt, and settle as you, and can drain the signing account's ALGO float. Treat it as a hot wallet with your revenue behind it.

This key is per node, not per operator. Your operator owner address (which receives payouts) is separate and stays cold, so a compromised node key is contained to that node and the tickets in flight through it — not your whole operator identity.

Provisioning the signing mnemonic

The signing account's 25-word mnemonic is the only secret you provision. The node refuses to start if the mnemonic for zs.signing_addr isn't loaded. Two supported ways to get it in:

  • Environment variable (simplest). Any env var whose name ends in _MNEMONIC is picked up — the label before the suffix is informational, since the node matches by the address the mnemonic derives to:

    export OPERATOR_SIGNING_MNEMONIC="word1 word2 ... word25"

    For systemd, put it in a secrets.env file (mode 0600) and reference it with EnvironmentFile= rather than baking it into the unit.

  • Cloud secret manager (recommended for production). Set ZS_MNEMONIC_URLS to a comma-separated list of name=url pairs. Supported schemes: awssecretsmanager://, awsparamstore://, gcpsecretmanager://, azurekeyvault://, and file://. Each backend uses its standard credential discovery.

warning

Generate a fresh signing account; don't reuse a wallet you care about. It's a hot key that lives on the node, and the funds it earns settle to your separate owner address — not to the signing account itself.

How prompts reach you sealed

Prompts are end-to-end encrypted to your node before they leave the user's device. The network only ever moves ciphertext; even the relay that forwards a request can't read it (see Relays).

info

What "age" means here. age is a modern, audited encryption format. ZeroSignal uses its hybrid public-key mode: an ephemeral X25519 elliptic-curve key agreement establishes a shared secret, and the ChaCha20-Poly1305 AEAD construction then encrypts and authenticates the payload. So wherever these docs say an "age recipient" or "age keypair," that is just the public/private half of one of these X25519 keys.

Here's the part that surprises most operators: your node holds no long-lived encryption key at all. There is no key file, no keypair you generate, nothing on disk. Instead, the node generates a short-lived ephemeral age recipient entirely in memory, signs it under your on-chain signing key, and advertises it on /v1/zs/details as four fields:

FieldWhat it is
ephemeral_age_pubkeyThe public recipient clients seal to.
ephemeral_issued_at / ephemeral_expiryIts lifetime window.
ephemeral_sigThe signature under your signing key.

Before a client (or the proxy on its behalf) will send you anything, it verifies:

  • The ephemeral_sig matches your on-chain node key — so a relay can't swap in a recipient it controls.
  • The recipient is fresh, not expired, and within policy.

If the signature is missing, the key is stale, or no valid recipient is advertised, the sealing party treats your node as unroutable and refuses it. There is no fallback to a stale or unsigned key — the privacy guarantee fails closed.

Forward secrecy: nothing to rotate by hand

The ephemeral recipient is rotated on a fixed cadence (roughly every 20 minutes) and also regenerated on every restart. The node does all of this itself:

  1. Generates a fresh age keypair in memory.
  2. Signs the new public recipient and its expiry under your signing key.
  3. Advertises it on /v1/zs/details, keeping the previous recipient decryptable for a brief overlap (about 5 minutes — its ~25-minute advertised lifetime minus the ~20-minute rotation cadence) so requests sealed just before rotation still complete.
  4. Erases the old private key — it only ever lived in memory.

That in-memory-only erasure is the forward-secrecy guarantee. Because the private half of a retired recipient never touched disk and is gone, a later compromise of the host's on-disk secrets cannot decrypt traffic that was sealed to an already-erased key. Forward secrecy here is unconditional — it doesn't depend on you configuring anything.

info

There is deliberately nothing to generate, mount, back up, or rotate by hand. No age-keygen, no node.key file, no zs.identity_path config. The register-node form takes only your signing address and base URL — there is no encryption public key to publish on chain. The signing mnemonic above is the only secret you provision.

The one operational consequence is to keep rotation healthy. A node whose rotation chronically fails has nothing fresh and signed to advertise, so rather than downgrade the guarantee it returns 503 on /v1/zs/details and /v1/zs/reserve until it recovers. In practice that means a node that can't maintain its sealing key simply stops taking traffic — it never silently serves with a weakened key.

How replies stay private

The reply is encrypted too, and not with the same key as the prompt:

  • During reserve, a per-request response key is established and bound to the ticket (the ticket carries a commitment to it). The client checks that commitment, so it knows the reply it decrypts came from the request it paid for.
  • Your node encrypts the streamed reply with fresh key material per response, so one request's key can't unlock another's, and a key recovered later can't retroactively decrypt past replies.

The node software handles all of this; there's no response-key configuration. But it's worth knowing it's the property your users are relying on: each reply's encryption is tied to the ticket it answers, and response key material is never reused across requests.

What you can and can't see

  • You can see the decrypted prompt while you run the model — that's unavoidable for inference in standard mode, and it's the one thing the design openly trusts you with.
  • You can't see who sent it. The request arrives through a relay with no account or identity attached; you see the relay's address, not the user's.
  • As a relay, you can't see anything — the payload you forward is sealed to the target node's key, not yours (see Relays).
info

Want to remove even that one trust assumption? Standard mode trusts you with the prompt after it's decrypted — a policy promise. Confidential compute (TEE) is designed to replace that promise with a cryptographic guarantee: the prompt is sealed to a key whose private half lives inside attested hardware that you, the operator, cannot read. The config surface, attestation endpoint, and proxy-side verifier are in place, but the only fully wired mode in the current build is a dev-only stub — hardware TEE (Intel TDX / AMD SEV-SNP with NVIDIA confidential compute) is not production-ready yet. See Confidential compute (TEE) for the hardware, the tee: block, and the in-CVM key bootstrap.

The exact ciphers, framing, and signing formats are part of the wire protocol your node software implements. A full protocol specification is coming soon; this page covers the key-handling behavior an operator needs to reason about, not the byte layout.