Terminology
The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" in this document are to be interpreted as described in BCP 14 1 when, and only when, they appear in all capitals.
The terms "Unified Address", "Unified Viewing Key", "Receiver", "Metadata Item", "Typecode", "Producer", and "Consumer" are to be interpreted as defined in ZIP 316. 2 An "Orchard Receiver" is a Receiver with Typecode .
- compactSize
- A variable-length integer as used in the Bitcoin protocol, 3 in minimal encoding, with a value of at most .
- Sender wallet
- The wallet that creates and deposits an Envelope.
- Recipient wallet
- The wallet an Envelope is encrypted to.
- Envelope
- An encrypted message, padded to a size class, as deposited in a Mailbox.
- Mailbox
- A service that holds Envelopes for a Recipient wallet until it fetches them.
- Mailbox Descriptor
- The data a Sender wallet needs to deposit an Envelope in a Mailbox.
- Transport Binding
- A specification of the Mailbox operations over one anonymity network,
identified by a
binding_id. - Payload
- The typed content carried inside an Envelope.
Abstract
This ZIP specifies an off-chain channel between Zcash wallets. A Sender wallet that holds a Recipient wallet's Unified Address deposits an encrypted Envelope in a Mailbox over an anonymity network, and the Recipient wallet fetches it later. Neither wallet reveals its network identity to the other, and the Mailbox operator does not learn who sent an Envelope.
Envelopes are encrypted to the Orchard Receiver of the Unified Address, using the Orchard key agreement with a domain-separated key derivation, so no new keys have to be exchanged. A new ZIP 316 Metadata Item carries the Mailbox Descriptor in the Unified Address. Payload types are defined for acknowledgements, text, ZIP 321 payment requests, and PCZTs, with codes reserved for FROST signing rounds and off-chain note delivery. Mailbox credentials are derived from the wallet seed with ZIP 32 registered key derivation.
Anyone who holds the recipient's Orchard incoming viewing key can read its Envelopes, and a Mailbox Descriptor links every Unified Address that carries it. Transport Bindings are specified separately; Appendix A sketches one for the Nym mixnet. 4
Motivation
Several wallet protocols need a direct channel between wallets. Each wallet that wants one currently builds its own, so wallets from different vendors cannot talk to each other:
- Multisig and hardware-signer flows pass PCZTs 5 between parties.
- FROST signing (ZIP 312 6) runs over a confidential, authenticated channel between the Coordinator and the signers whose mechanism ZIP 312 leaves out of scope. This ZIP supplies the confidential, anonymous transport; a FROST payload would have to carry its own authentication.
- Payment requests (ZIP 321 7) and acknowledgements have no wallet-to-wallet transport.
- Project Tachyon 8 9 proposes removing in-band secret distribution from transactions, which makes an off-chain delivery channel a requirement rather than an add-on.
On-chain memos are the only standard channel today. ZIP 231 10 proposes to lift their 512-byte size cap, but memos would still cost fees, be stored by every full validator forever, and need a block confirmation per round, which rules out interactive protocols such as PCZT and FROST exchanges.
ZIP 324 11 delegates delivery to a channel the parties already share, such as a secure messenger or a QR code, for the case where the sender has no address for the recipient. It does not help when the sender does hold a Unified Address, which is the case this ZIP covers.
The idea is not new. Hornby's 2020 light wallet privacy review 12 lists out-of-band transaction delivery over a mixnet as an option and notes that scalable Zcash will need a mixnet or similar technology anyway. zips#629 13 suggests a mixnet plus a "private mailbox" to keep offline reception. The Nym × Zcash integration guide 14 covers routing a wallet's chain-sync and transaction-submission traffic through the mixnet, not wallet-to-wallet delivery. This ZIP fills that gap.
Privacy Implications
Who can read Envelopes. Envelopes are encrypted to an Orchard Receiver, so anyone holding the account's Orchard incoming viewing key can decrypt them. That is the trust model of incoming notes, but Envelopes can carry more: a PCZT reveals a transaction the recipient is about to sign, including its outputs. Users who share incoming viewing keys with auditors or watch-only services should not expect Envelopes to be private from them.
Linkability. A Mailbox Descriptor is a stable identifier. Two Unified
Addresses that carry the same descriptor are linked to one recipient by anyone
who sees both, which defeats diversified addresses. Unlinkable addresses
therefore need separate mailboxes, at the cost of more credentials to derive
and more mailboxes to fetch from. A reply_to address inside an Envelope has
the same property for the sender.
Network observers and the Mailbox operator. The anonymity network hides the sender's network identity from the operator and the recipient, and the recipient's from the sender. The operator still sees how many Envelopes a mailbox receives, their size classes, and when they are deposited and fetched. In bindings where the recipient connects to its own mailbox directly, as in the Nym sketch of Appendix A, the operator also learns the recipient's network identity unless the connection is tunnelled, for example over Tor. Network privacy is only as good as the anonymity set: while few wallets use a binding, cover traffic carries most of the weight.
Timing. A deposit made at the same moment as a related on-chain broadcast, a fetch triggered by opening the app, or an immediate acknowledgement each leak timing that an observer can correlate with chain activity. The wallet behaviour rules in the specification reduce but do not remove this.
No sender authentication and no forward secrecy. Envelopes do not authenticate their sender, so a Text payload can impersonate anyone; the specification forbids presenting it as coming from a named party. Envelope keys derive from long-lived keys, so compromise of an incoming viewing key exposes every Envelope the adversary has recorded, as for on-chain notes.
Denial of service. Anyone holding a Unified Address can fill its mailbox. The cost falls on the mailbox's storage and the recipient's bandwidth; trial decryption itself is cheap. Bindings are expected to document quotas, and the unencrypted Envelope header lets a wallet see size classes before fetching bodies where a binding allows that.
Requirements
- A sender holding only a recipient's Unified Address can deliver a message with no prior contact.
- The recipient need not be online when the message is sent.
- No long-term key material is needed beyond what the wallet derives from its seed, and a wallet restored from seed can recover its mailboxes on a best-effort basis.
- Neither the Mailbox operator nor a network observer learns the sender's network identity or links a sender to a recipient. The sender does not learn the recipient's network identity.
- An Envelope reveals nothing about its content beyond its size class.
- Wallets that implement ZIP 316 but not this ZIP can still parse and pay a Unified Address that carries a Mailbox Descriptor, provided the Typecode registration requested in the Specification is granted.
- The core protocol does not depend on any one network or token.
Non-requirements
- Sender authentication at the Envelope layer. Payload types that need it carry their own.
- Guaranteed delivery. Delivery is best-effort; protocols built on top have to handle loss.
- Forward secrecy.
- Group messaging.
- Hiding the recipient's network identity from its own Mailbox operator. A binding may provide this, and a wallet can add Tor, but the core does not require it.
Specification
Overview
Sender wallet Recipient wallet
| ^
| 1. read Orchard Receiver and Mailbox |
| Descriptor from the recipient's UA |
| 2. build and encrypt the Envelope | 5. trial-decrypt
| 3. Deposit over the anonymity network | with ivk
v |
[ Mailbox ] -------------- 4. Fetch over the anonymity network
Mailbox Descriptors
A Mailbox Descriptor is encoded as:
| Field | Type | Description |
|---|---|---|
binding_id | u16 (LE) | Identifies the Transport Binding |
descriptor_len | compactSize | Length of descriptor |
descriptor | byte[descriptor_len] | Binding-specific bytes |
Registered binding_id values:
binding_id | Binding |
|---|---|
| Reserved | |
| Nym mixnet (Appendix A, non-normative) | |
| – | Experimental; MUST NOT be used in production |
Unified Address Metadata Item
This ZIP defines a ZIP 316 Metadata Item, Mailbox Descriptors, with a Typecode in the Non-MUST-understand Metadata range to , to be assigned by the ZIP Editors. 15
The Item's value MUST parse as one or more Mailbox Descriptors, concatenated
with no trailing bytes, in the recipient's order of preference. Descriptors
with an unrecognised binding_id MUST be skipped, not rejected. A Sender
wallet SHOULD use the first descriptor whose binding it supports.
A Unified Address that carries this Item MUST also carry an Orchard Receiver; Envelopes are encrypted to that Receiver. The Item MUST NOT appear in a Unified Viewing Key.
A Mailbox Descriptor is a stable identifier. A wallet MUST NOT put the same descriptor into two Unified Addresses that the user expects to be unlinkable. Wallets SHOULD include the Item only in addresses generated for a purpose that calls for messaging, and SHOULD NOT include it by default.
If a binding's descriptors can go stale (for example in Appendix A, when the recipient changes gateway), the wallet SHOULD also set an Address Expiry Time Metadata Item 16 on the address. Address Expiry Typecodes are MUST-understand, so such an address can only be paid by Consumers that implement ZIP 316 Revision 2; see Open issues.
Typecode registration
The ZIP 316 Typecode Registry 17 does not currently permit Producers to include Non-MUST-understand Metadata in any address type, and additions to the registry are made by 2xxx-series ZIPs. This ZIP requests the following row, to be applied when it reaches Proposed status:
| Typecode | Description | R0 | R2 zu | R2 tu |
|---|---|---|---|---|
| TBD | Mailbox Descriptors | ✅ | ✅ | ✅ |
The Item has no effect on transaction construction, so it meets ZIP 316's
condition for zu addresses. Permitting it in Revision 0 addresses is what
lets wallets that predate this ZIP pay the address, because Consumers ignore
Items with Typecodes they do not recognise. If the ZIP Editors permit it only
in Revision 2 addresses, the compatibility requirement holds only for
Consumers that implement ZIP 316 Revision 2.
Envelope encryption
Let be the recipient's Orchard Receiver. Let 18, 19, and 20, and 21 be as defined in the Zcash Protocol Specification, and let be used to encode Pallas points as 32-byte strings as in . 22
The sender:
-
Computes .
-
Chooses uniformly at random from . MUST NOT be reused across Envelopes.
-
Computes , , and .
-
Derives the 32-byte key
This is with a different personalization, so Envelope keys are domain-separated from note encryption keys even though they share the key agreement.
-
Chooses the smallest size class (see Envelope format) whose padded size fits the plaintext, and pads the plaintext with zero bytes to exactly bytes.
-
Computes
with and . The zero nonce is safe because is unique to each .
The recipient, for each Orchard incoming viewing key it holds:
- Computes and rejects the Envelope if the result is or the identity .
- Computes . Since , this equals the sender's value.
- Derives as above and attempts decryption with the same . On failure, the Envelope is not for this key.
- Rejects the plaintext if it does not parse as described in Plaintext format, or if any padding byte is nonzero.
- Rejects the Envelope unless the plaintext's is the
diversifier of an address of this account that this wallet issued either
with a Mailbox Descriptors Item or as a
reply_to.
A wallet processes an Envelope under the first for which decryption succeeds. Steps 1 and 2 cost one point decompression and one scalar multiplication per Envelope and account.
Envelope format
| Field | Type | Description |
|---|---|---|
version | byte | |
size_class | byte | See table below |
ephemeralKey | byte[32] | |
ciphertext | byte[N + 16] | AEAD output; is set by size_class |
size_class | Padded plaintext size | Intended use |
|---|---|---|
| 2048 bytes | Acks, URIs, short text | |
| 32768 bytes | Small PCZTs, FROST rounds | |
| 262144 bytes | Large PCZTs |
Other values are reserved. Recipients MUST reject Envelopes with an unknown
version or size_class, or whose length does not match size_class.
Plaintext format
| Field | Type | Description |
|---|---|---|
payload_type | u16 (LE) | See Payload types |
flags | byte | Bit 0: ack_requested; see below |
d | byte[11] | Diversifier of the Orchard Receiver the Envelope was sent to |
message_id | byte[32] | Uniformly random |
in_reply_to | byte[32] | message_id of the Envelope this one answers, or all zero |
expiry_time | u64 (LE) | Unix seconds; means no expiry |
reply_len | compactSize | Length of reply_to |
reply_to | byte[reply_len] | A Unified Address, or empty |
payload_len | compactSize | Length of payload |
payload | byte[payload_len] | Type-specific |
padding | byte[] | Zero bytes up to |
Senders MUST set bits of flags other than bit 0 to ; recipients MUST
ignore bits they do not recognise.
d lets the recipient attribute the Envelope to one of its addresses without
trusting reply_to, and in_reply_to lets protocols with several rounds
match answers to requests.
reply_to, if present, is the UTF-8 string encoding of a Unified Address
24 whose Human-Readable Part matches the network of the
Envelope's recipient. It MUST carry an Orchard Receiver and MAY carry a
Mailbox Descriptors Item. A reply is encrypted to
that Receiver. It is deposited in the mailbox named by the Item if there is
one, or sent by a transport-level anonymous reply if the binding provides one
(see Mailbox operations); if neither is available, no
reply is possible. A sender that wants replies without linking them to its
other addresses SHOULD use a Unified Address and mailbox dedicated to replies.
Senders SHOULD round expiry_time to the hour, to avoid leaking clock skew.
Recipients:
- MUST drop Envelopes whose
expiry_timeis nonzero and in the past. - MUST drop Envelopes whose
message_idthey have already processed, and SHOULD remember processedmessage_ids at least until their expiry, or for a wallet-chosen period whenexpiry_timeis . - MUST ignore the payload of an Envelope with an unknown
payload_type, but still record itsmessage_id.
Payload types
payload_type | Name | Payload |
|---|---|---|
| Reserved | — | |
| Ack | Empty; in_reply_to names the acknowledged Envelope | |
| Text | UTF-8 text for display to the user | |
| Payment request | UTF-8 ZIP 321 URI 7 | |
| PCZT | Serialized PCZT 5; format reference TBD | |
| Reserved | FROST round messages (ZIP 312 6); format TBD | |
| Reserved | Off-chain note delivery 8; format TBD | |
| – | Experimental | MUST NOT be used in production |
An Ack MUST NOT set ack_requested.
Payloads MUST NOT carry spend authority, such as spending keys or ZIP 324 URIs 11, because every holder of the recipient's incoming viewing key can read them.
Payload types that cause side effects SHOULD carry their own replay
protection; message_id deduplication and expiry_time only bound replay.
Mailbox operations
A Transport Binding MUST provide these operations:
- Deposit(descriptor, envelope): deliver an Envelope to the Mailbox named by the descriptor.
- Fetch(credential) → envelopes: retrieve held Envelopes.
- Delete(credential, handles): remove fetched Envelopes, unless the binding documents that Fetch removes them implicitly.
A Transport Binding MUST hide the sender's network identity from the Mailbox operator and from the recipient, and MUST hide the recipient's network identity from the sender. It SHOULD hide the recipient's network identity from the Mailbox operator, and SHOULD resist timing correlation by a global passive adversary. Bindings SHOULD rate-limit Deposit and SHOULD bound the storage a mailbox can consume.
A Transport Binding specification MUST state:
- the descriptor encoding;
- how the 64-byte mailbox key from Mailbox key derivation is turned into the binding's credentials;
- the retention period, or how a wallet learns it;
- per-mailbox storage quotas, if any;
- which size classes it supports;
- how replies work: by depositing to a
reply_tomailbox, by a transport-level anonymous reply, or both, and how long each path stays usable; - rate limiting and cost, if any;
- what network identity, timing, and size information each party can observe.
Mailbox key derivation
Mailbox credentials are derived from the wallet seed with ZIP 32 registered key derivation 25, using and equal to the number assigned to this ZIP:
where is the ZIP 32 coin type of the network 26, so that Mainnet and Testnet mailboxes are unlinkable; is the ZIP 32 account index of the Orchard key whose addresses carry the descriptor; and is a per-binding, per-account counter that the wallet increments for each new mailbox. is 64 bytes; each binding states how it is split into credentials.
A wallet restored from seed can rebuild its credentials by scanning from with a gap limit, subject to the binding's own limits (for example, knowing which gateway each mailbox used).
Wallet behaviour
Sending:
- Wallets SHOULD NOT deposit an Envelope at the same moment as a related on-chain broadcast, and SHOULD add a random delay when the use is not interactive.
- Wallets MUST NOT fall back to a direct connection to a Mailbox when the binding's anonymity network is unavailable.
Fetching:
- Wallets SHOULD fetch on a randomized schedule that does not depend on user actions, including when no message is expected.
- Wallets SHOULD cap the bytes they fetch and trial-decrypt per Fetch.
Acknowledgements:
- Wallets SHOULD send an Ack only when
ack_requestedis set, and SHOULD delay it by a random amount.
Display:
- Envelopes are not authenticated. Wallets MUST NOT present a message as coming from a named party unless the payload type itself authenticates the sender, and SHOULD NOT open links in Text payloads without user confirmation.
Viewing keys:
- Wallets that export an account's incoming viewing key to a third party SHOULD NOT advertise mailboxes on that account's addresses.
Rationale
Why reuse Orchard key agreement? It needs no new keys and no prior contact: any Unified Address with an Orchard Receiver is already a public encryption key. The cost is that incoming viewing key holders can read Envelopes; a dedicated messaging key in the descriptor would avoid that at the cost of a larger Item.
Why fixed size classes? Exact lengths would leak payload types and content. Three classes keep the anonymity set per class large while still allowing PCZTs.
Why no ZIP 324 payload? A sender that can use this channel already holds the recipient's Unified Address and can pay it on chain, and a ZIP 324 URI carries spend authority that every incoming viewing key holder could use.
Why a transport-agnostic core? Tying a standard to one network and its token is a long-term risk. Nym is sketched first because it is live, packet-based, uses cover traffic, stores messages for offline clients, and offers sender-anonymous replies.
Reference implementation
None yet.
Open issues
- Assignment of the Metadata Item Typecode, whether it is permitted in Revision 0 addresses, and this ZIP's number, which the key derivation depends on.
- Whether to encrypt to a dedicated messaging key carried in the descriptor instead of to the Orchard Receiver, so that incoming viewing key holders cannot read Envelopes.
- Address Expiry Items make an address unpayable by ZIP 316 Revision 0 Consumers, while the compatibility requirement assumes they can pay it. Whether to recommend expiry, shorter-lived descriptors, or neither.
- The cost of running one mailbox per unlinkable address, and whether wallets should offer a deliberately linkable "contact" mode.
- A stable reference for the PCZT serialization.
- The FROST round message format and its authentication layer.
- The off-chain note delivery format, to be aligned with Tachyon.
- Support for Sapling-only recipients.
- A Tor onion-service binding.
- Whether the Nym binding in Appendix A becomes normative in this ZIP or in a separate one, once its TBDs are resolved.
Appendix A: Nym transport binding (non-normative)
This appendix sketches a binding for the Nym mixnet so that the core protocol
can be evaluated against a real network. It is not a complete Transport
Binding specification: the items marked TBD have to be resolved before it can
be made normative. binding_id is reserved for it.
Descriptor. The 96-byte binary form of a Nym client address, 27 28 laid out as follows:
| Field | Type | Description |
|---|---|---|
| client identity key | byte[32] | Ed25519 public key |
| client encryption key | byte[32] | X25519 public key |
| gateway identity key | byte[32] | Ed25519 public key of the gateway |
Credentials. The first 32 bytes of would be the Ed25519 seed of the client identity key, and the last 32 bytes the X25519 secret key 29 of the client encryption key. The Nym Rust SDK accepts externally supplied client keys through a custom key store. 30
Deposit. The sender sends the Envelope as a Nym message to the descriptor's address through the mixnet, from an ephemeral Nym client with fresh keys rather than from its own mailbox client, attaching reply SURBs 31 if it expects a prompt reply. Each ephemeral client has to register with an entry gateway and hold bandwidth credentials; the cost and latency of doing so per message are unverified (TBD).
Fetch. The recipient runs a Nym client with the derived keys, connected to the gateway named in the descriptor, with cover traffic on. The gateway stores messages while the client is offline and delivers them when it connects. 27
Delete. Implicit on delivery by the gateway (TBD: confirm).
Retention. Not documented by Nym (TBD).
Size classes. All. Nym splits a message into fixed-size Sphinx packets, so larger classes take more packets and leave a larger timing footprint.
Replies. SURB reply keys expire after 24 hours, 31 so SURBs
serve interactive exchanges only. A recipient that fetches later can reply
only to a reply_to address that carries a Nym descriptor.
Rate limiting and cost. Sending through Nym consumes bandwidth credentials. Who pays, and whether they can be bought with shielded ZEC, is TBD.
Gateway changes. The gateway is part of the descriptor. If the recipient moves gateway, old descriptors stop working and a new Unified Address has to be issued. How a wallet restored from seed learns which gateway each mailbox index used is TBD.
Observability. The recipient's gateway, which is the Mailbox operator, sees the recipient's connection, including its IP address unless the connection is tunnelled over Tor, and the count and size of stored messages. The sender's entry gateway sees the sender's IP address but not the destination. Mix nodes see packets only. A global passive adversary faces Nym's mixing delays and cover traffic.
References
Footnotes
-
Information on BCP 14 — "RFC 2119: Key words for use in RFCs to Indicate Requirement Levels" and "RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words" ↩
-
Partially Created Zcash Transactions (librustzcash
pcztcrate) ↩ ↩2 -
Sean Bowe and Ian Miers, "A Note on Notes: Towards Scalable Anonymous Payments via Evolving Nullifiers and Oblivious Synchronization", IACR ePrint 2025/2031 ↩ ↩2
-
Sean Bowe, "Tachyon: Scaling Zcash with Oblivious Synchronization" (Zcash Community Forum thread) ↩
-
Taylor Hornby, "Fixing Privacy Problems in the Zcash Light Wallet Protocol" (2020) ↩
-
zips#629: Scalability Design Explorations around changing Payment Flows ↩
-
ZIP 316: Unified Addresses and Unified Viewing Keys — Metadata Items ↩
-
ZIP 316: Unified Addresses and Unified Viewing Keys — Address Expiration Metadata ↩
-
ZIP 316: Unified Addresses and Unified Viewing Keys — Typecode Registry ↩
-
Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.5.5: Orchard Key Agreement ↩
-
Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions ↩
-
Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.9.6: Pallas and Vesta ↩
-
Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 4.2.3: Orchard Key Components ↩
-
Zcash Protocol Specification, Version 2026.8.0 [NU6.3]. Section 5.4.5.6: Orchard Key Derivation ↩
-
ZIP 316: Unified Addresses and Unified Viewing Keys — Encoding of Unified Addresses ↩
-
ZIP 32: Shielded Hierarchical Deterministic Wallets — Specification: Registered key derivation ↩
-
ZIP 32: Shielded Hierarchical Deterministic Wallets — Key path levels ↩