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 version
typeMessage type (see types below)
conv_id8-character conversation identifier (A-Z, 0-9), uniquely identifies a conversation thread
sender_hash12 hex characters, first 6 bytes of SHA-256 of sender's Zcash address
payloadType-specific content

Message Types

INITInitial Message

First message to start a conversation. Includes sender's full address.

<sender_address>|<message_text>
DMDirect Message

Standard text message in an existing conversation.

<message_text>
KEXKey Exchange

E2E encryption key exchange with signature for MITM prevention.

<pubkey_b64>|<signature_b64>
RXNReaction

Emoji reaction to a specific message.

<target_msg_id>|<emoji>
RCVReceipt

Read/delivery receipt.

<target_msg_id>|<receipt_type>
RPLReply

Quote-reply to a specific message.

<target_msg_id>|<reply_text>
REQPayment Request

Request ZEC payment from a contact.

<amount_zatoshi>|<memo>
STTStatus

User availability status update.

<status_text>
CHKCheck-in

Periodic 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.

// First chunk
ZMSG|v4c|1/N|<conv_id>|<type>|<sender_hash>|<payload_part>
// Middle chunks
ZMSG|v4c|M/N|CONT|<payload_part>
// Last chunk
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

1

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.

2

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 KEX message type with digital signatures for MITM prevention
  • Group messages: AES-256-GCM with 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

A7K3BX9R

Constraints

512-byte memo limitZcash ZIP 231
Transaction fee per messageEach message costs a Zcash transaction fee
~75 second block timeMessage delivery latency tied to block confirmation
No message deletionBlockchain is immutable
Chunked message costChunked messages require multiple transaction fees

Source Code

The canonical implementation is in the Android app:

github.com/decentrathai/zchat-android

Protocol parsing

ZMSGProtocol.kt

Encryption

E2EEncryption.kt

ZMSG Protocol v4 — ZChat Message Format Specification

ZMSG Protocol v4 Specification - ZChat Message Format