ZMSG Protocol v4
Message format specification for ZChat's Zcash-based messaging
Overview
ZMSG (ZChat Message Protocol) is the wire format for messages sent via Zcash shielded transaction memos. Each message is encoded as a pipe-delimited string within the 512-byte memo field of a shielded transaction.
Message Format
ZMSG|4|<type>|<conv_id>|<sender_hash>|<payload>
ZMSGProtocol identifier (literal string)4Protocol versiontypeMessage type (see types below)conv_id8-character conversation identifier (A-Z, 0-9), uniquely identifies a conversation threadsender_hash12 hex characters, first 6 bytes of SHA-256 of sender's Zcash addresspayloadType-specific contentMessage Types
| Type | Name | Description | Example Payload |
|---|---|---|---|
INIT | Initial Message | First message to start a conversation. Includes sender's full address. | <sender_address>|<message_text> |
DM | Direct Message | Standard text message in an existing conversation. | <message_text> |
KEX | Key Exchange | E2E encryption key exchange with signature for MITM prevention. | <pubkey_b64>|<signature_b64> |
RXN | Reaction | Emoji reaction to a specific message. | <target_msg_id>|<emoji> |
RCV | Receipt | Read/delivery receipt. | <target_msg_id>|<receipt_type> |
RPL | Reply | Quote-reply to a specific message. | <target_msg_id>|<reply_text> |
REQ | Payment Request | Request ZEC payment from a contact. | <amount_zatoshi>|<memo> |
STT | Status | User availability status update. | <status_text> |
CHK | Check-in | Periodic check-in / heartbeat message. | <timestamp> |
INITInitial MessageFirst message to start a conversation. Includes sender's full address.
<sender_address>|<message_text>DMDirect MessageStandard text message in an existing conversation.
<message_text>KEXKey ExchangeE2E encryption key exchange with signature for MITM prevention.
<pubkey_b64>|<signature_b64>RXNReactionEmoji reaction to a specific message.
<target_msg_id>|<emoji>RCVReceiptRead/delivery receipt.
<target_msg_id>|<receipt_type>RPLReplyQuote-reply to a specific message.
<target_msg_id>|<reply_text>REQPayment RequestRequest ZEC payment from a contact.
<amount_zatoshi>|<memo>STTStatusUser availability status update.
<status_text>CHKCheck-inPeriodic check-in / heartbeat message.
<timestamp>Chunking (v4c)
When a message exceeds the 512-byte memo limit, it is split across multiple Zcash transactions. The recipient's client reassembles chunks using the sequence numbers (M/N). All chunks in a message are sent as separate shielded transactions.
ZMSG|v4c|1/N|<conv_id>|<type>|<sender_hash>|<payload_part>
ZMSG|v4c|M/N|CONT|<payload_part>
ZMSG|v4c|N/N|CONT|<payload_part>
The first chunk carries the full header (conversation ID, type, sender hash) while continuation chunks use the CONT marker to reduce overhead and maximize payload space.
Encryption Layers
Zcash Protocol Encryption
The shielded pool encrypts the entire memo field at the protocol level. Sender, recipient, amount, and memo are all hidden from third-party observers on the blockchain.
Application E2E Encryption (optional)
End-to-end encryption using secp256r1 ECDH key agreement with AES-256-GCM via HKDF for symmetric key derivation.
- Key exchange via
KEXmessage type with digital signatures for MITM prevention - Group messages:
AES-256-GCMwith a shared group key, distributed via ECIES
Encryption flow
Plaintext Message
|
v
[AES-256-GCM Encrypt] <-- ECDH shared secret via HKDF
|
v
ZMSG Payload (encrypted)
|
v
[Zcash Shielded Memo] <-- Protocol-level encryption
|
v
Blockchain (opaque to observers)Conversation ID
Format
8 characters, uppercase alphanumeric (A-Z, 0-9)
Entropy
368 ≈ 2.8 trillion possible values (~41 bits)
Generation
Randomly generated by the conversation initiator
Scope
Shared between both participants of a conversation
Example
A7K3BX9RConstraints
Source Code
The canonical implementation is in the Android app:
github.com/decentrathai/zchat-androidProtocol parsing
ZMSGProtocol.ktEncryption
E2EEncryption.ktZMSG Protocol v4 — ZChat Message Format Specification