Overview

As a developer using ADAPT, you will spend a considerable amount of time crafting and debugging MUFL code. During this process, you will frequently encounter system logs that contain protocol messages. While it is not necessary to manually interact with the ADAPT messaging protocol on a regular basis, having a deep understanding of its structure and operation is valuable. Understanding the protocol’s components, the different types of messages, and their significance will help your debugging efforts.

The ADAPT messaging protocol is used for communication between nodes in the ADAPT network. Messages defined by the protocol can request data, invoke transactions, respond to requests, and more.

For example, an ADAPT packet in a web browser might, in response to a user clicking a Submit button, use the messaging protocol to initiate a message to an ADAPT packet in a different ADAPT node to process the submission.

Message Structure

Every message in the ADAPT messaging protocol follows this specific structure:

(
    $msg_type_id -> str,
    $protocol_version -> [int, int],
    $body_wrapper -> (
        $from -> global_id+,
        $to -> global_id+,
        $message_hash -> hash_code+,
        $ip_document -> identity_proof_document::type+,
        $body -> any
    )
)

This code serves as a general template, applicable to all messages. Specific messages add message-specific information in the form of a message sub-type.

The variation between messages primarily lies in the body_wrapper field. This field houses numerous nullable fields and a body field of type any, defined by each specific message. In addition, certain messages within the protocol make some body_wrapper fields non-nullable. For instance, a transaction request message (w2b_transaction) necessitates the presence of both from and to fields, as the sender and recipient packets are known. Conversely, when an ADAPT wrapper interacts with the backup storage component, the recipient container remains undefined because the recipient is not an ADAPT node.

For information about all messages defined by the protocol, refer to the message reference.

Serialization Limits

The binary serialization used by the protocol enforces a set of hard limits on every encoded value. They bound an untrusted stream so that a malformed or hostile message cannot drive unbounded recursion, allocation, or iteration on the receiving node. Each limit is enforced identically on serialization (emit) and on deserialization (decode), on every target that participates in consensus (native, WebAssembly, and the rv32 / zkVM guest), so the two sides can never disagree about what is valid.

Limit Value
Maximum object/array nesting depth 4096
Maximum number of items in a single object or array 1,048,576 (220)
Maximum number of items in a stream 16,777,216 (224)
Maximum length of a single string just under 4 GiB (232 − 1 bytes)
Maximum length of a single binary value just under 4 GiB (232 − 1 bytes)

A value that exceeds any of these limits is rejected with an error (fail-closed) rather than being partially decoded. Nesting depth is measured with the root value at depth 1, and a value nested exactly at the maximum depth is accepted while anything deeper is rejected.

WebAssembly / browser clients apply a tighter default nesting-depth limit of 256. A WebAssembly client decodes on the host JavaScript engine’s native call stack, which overflows well below the 4096 consensus limit, so the SDK clamps the effective depth on those clients to stay safely within the engine stack. This clamp can only tighten the limit, never raise it above the consensus value of 4096; it admits all legitimate data with margin. Native and rv32 targets keep the full 4096.

Domains Allowed in External Messages

The body of a message is typed any, but not every MUFL data domain may cross the wire. On decode, an externally received message is restricted to the following domains, and any value of another domain is rejected:

NIL, TRUE, FALSE, INTEGER, STRING, immutable DICTIONARY, BINARY, CRYPTO_ELEMENT, TIME, HASH_CODE, and blinded DICTIONARY.

This allow-list is enforced on the deserialization path, so it constrains what an external peer can place in a message body regardless of the sender’s behavior.