General conventions
This document defines the scope, normative terminology, data representations, and cryptographic conventions shared by all top-level protocols in Meshline Protocol 1.0. Protocol roles, business objects, network methods, state machines, error handling, resource policies, and transport behavior are defined by their respective protocols.
Scope
This specification defines the network interoperability rules between clients, account devices, and public relays.
This specification does not cover:
- User interfaces, or how wallets and keys are stored or protected locally;
- Application-specific business objects, precise presentation of message bodies, or attachment hosting services;
- Commercial settlement between relay operators;
- Fuzzy nickname searches, global user directories, or contact recommendations;
- Selection and operation of blockchain RPC nodes;
- Specific libp2p, database, web-framework, programming-language, or SDK implementations.
Normative terminology
This specification uses MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY to distinguish mandatory requirements, prohibitions, recommendations, discouraged behavior, and optional behavior. These uppercase keywords are interpreted according to RFC 2119 and RFC 8174.
Network context
Format and representation
A conforming Meshline Network is jointly identified by the Neo N3 network magic and the script hash of the MeshlineRegistry contract on that network. Its canonical string representation is:
neo:<reference>:<registry>
neois the fixed lowercase prefix;referenceis the Neo N3 network magic, expressed as a decimal integer without leading zeros, in the range0..4294967295;registryis the script hash of theMeshlineRegistrycontract on that network, expressed as lowercase0xfollowed by 40 lowercase hexadecimal characters; Neo represents this value asUInt160.
The complete string MUST match ^neo:(0|[1-9][0-9]{0,9}):0x[0-9a-f]{40}$, and the network magic MUST satisfy the range above. Invalid formats MUST be rejected and MUST NOT be accepted after trimming whitespace, changing case, removing leading zeros, or applying any other normalization.
Cryptographic inputs carry this string in the reserved $context field. For example:
{
"$context": "neo:860833102:0x5979ba79431672a38a18a32cdc48fd7317818b70"
}
Trusted sources and isolation requirements
Protocol objects, signatures, hashes, and network records in different network contexts MUST be isolated from each other. The verifier MUST obtain the expected network number and Registry contract address from trusted configuration, construct the canonical context string, and compare complete strings. It MUST NOT derive the expected context from values declared by the object being verified or by a remote node, and MUST NOT determine that two contexts are identical using only a network name. Consequently, a signature generated by the same account key over the same object contents cannot verify on another Neo network or against another Registry deployment on the same Neo network.
A Neo JSON-RPC client MUST confirm that the connected node's network magic matches the expected network context.
JSON and field representations
- Protocol JSON uses UTF-8;
$typeand$contextare reserved protocol fields. - Binary values use canonical base64url without
=padding; both senders and receivers must follow the base64url encoding rules below. - Fields representing instants default to integer UTC Unix seconds; where a field explicitly specifies another unit, its definition takes precedence.
- Objects MUST NOT contain duplicate field names. No node in a JSON tree may have a depth greater than 16; node depth is defined by RFC 9535 Section 1.1. Invalid UTF-8, non-Unicode-scalar values, trailing non-whitespace data, and type mismatches MUST be rejected.
- Unknown fields covered by a complete-object signature or network-bound digest MUST be preserved recursively, including their names, JSON values, and presence. Copying or merging unknown fields into target state must follow the field mapping rules below.
In field tables, array<T> denotes a JSON array of elements of type T. The Required column specifies whether a field must be present: Yes means it MUST be present, No means it MAY be omitted, and Conditional means its field semantics determine the requirement. Omission is not equivalent to explicit null. Unless expressly permitted by the method, object, or transport rules, null is invalid.
- In a complete object, a field that permits both omission and an empty array MAY be omitted or encoded as
[]. Business processing treats both as the same empty collection, but MUST NOT substitutenull. Presence is determined by the Required column; an array required to be nonempty MUST NOT use[]when present. - Arrays representing sets MUST NOT contain duplicate elements.
- Unless the field definition specifies an order, array order has no business semantics; signatures and hashes are nevertheless generated using the object's actual array order.
- User-facing text is validated against each field's UTF-8 byte limit. Text limits MUST NOT be applied to ciphertext, base64url bytes, or external content.
Safe integers and counter advancement
A JSON integer MUST use the shortest decimal form -?(0|[1-9][0-9]*). -0, fractional forms, and exponent forms are invalid. Values MUST lie in [-(2^53-1), 2^53-1].
When advancing a protocol field using safe integers, the producer MUST perform exact integer arithmetic and confirm that the result remains within the permitted range before signing, publishing, or writing it. If no conforming next value exists, it MUST stop the operation that needs to advance that value and retain the current valid state. It MUST NOT overflow, wrap around, truncate, use floating-point approximations, or reuse an existing value. If a client detects exhaustion during local generation, it stops generation and reports the reason to the caller without constructing and submitting an out-of-range object.
Text whitespace characters
When a user-text field states that it MUST NOT consist only of whitespace, or MUST contain at least one non-whitespace character, whitespace is fixed to the following ranges from Unicode 17.0.0 White_Space, comprising 25 Unicode code points. Ranges include both endpoints:
U+0009..U+000D U+0020 U+0085 U+00A0 U+1680
U+2000..U+200A U+2028 U+2029 U+202F U+205F U+3000
A nonempty string consists only of whitespace if every code point belongs to this set. It passes the non-whitespace check if at least one code point is outside the set. Whether an empty string is permitted remains determined by the field definition.
base64url encoding
Fields or content explicitly specified as base64url MUST use the canonical encoding defined by RFC 4648 Sections 3.5 and 5, omitting = padding:
- Encoded text permits only the ASCII characters
A-Z,a-z,0-9,-, and_; it MUST NOT contain=, whitespace,+,/, or any other character; - The character count modulo 4 may only be 0, 2, or 3;
- If the remainder is 2, the low 4 bits of the last character's 6-bit value MUST be zero; if the remainder is 3, its low 2 bits MUST be zero.
The receiver MUST reject text that fails these conditions and cannot accept it after removing characters, ignoring nonzero trailing pad bits, or rewriting the encoding. Canonical encoding is unique: for example, byte 00 encodes as AA; AB MUST be rejected even if a permissive decoder produces the same byte. Passing a field's regular expression alone does not establish compliance with this section. The empty string is the canonical encoding of an empty byte string, but is valid only when the field permits an empty value.
Object types
For an object that defines $type, its $type MUST equal the fixed string specified in the object definition. Receivers select the applicable structure, field semantics, and validation rules by matching the complete case-sensitive string; different strings represent different types.
Receivers MUST NOT infer an object's type solely from its field shape, the current interface, or undefined properties. Unknown types are retained, rejected, or cause state application to be suspended according to the applicable message, method, or event rules. They MUST NOT be treated as known types or trigger unsupported protocol state changes.
Substructures without their own $type are interpreted under the rules selected by the parent object. Nested objects with their own $type MUST be validated according to that type; the outer type does not substitute for the inner type.
Inputs for signatures, hashes, identity or resource-identifier derivation, AAD, and KDF info MUST use the fixed $type specified for each input and MUST NOT have a default format version added. Verifiers MUST preserve $type and unknown fields within the covered scope. Except for prescribed input-construction operations, they MUST NOT add, remove, or rewrite fields to match another type.
Incremental edits
For modifiable fields in an incremental edit, omission retains the current value, explicit null deletes the field, and other values add or replace it. Fields that cannot be deleted do not accept null. The updated object must still satisfy its own constraints.
Field mapping
When reconstructing another object from request parameters or applying an incremental request to existing state, fields may only be mapped as defined by the method.
Request extension properties may only be copied or merged into the target object when explicitly allowed by the method. Such extensions MUST NOT share a name with a defined target-object field or a field belonging only to network-bound inputs; a collision MUST be rejected even if the value is identical or null.
Other unknown properties of a signed request remain preserved under the original signature rules and MUST NOT be used as patches to target state.
JSON-RPC extension fields
JSON-RPC requests, responses, and notifications on the client WebSocket and relay RPC transports MAY carry undefined extension fields in the root object. Receivers MUST ignore these fields and MUST NOT reject a message solely because they are present. Extensions must still satisfy the JSON and field representation rules and count toward the JSON depth limit. Duplicate fields and other invalid JSON representations must still be rejected. The types, presence requirements, values, and mutual-exclusion rules of defined fields continue to apply. Unknown fields MUST NOT replace defined fields or change method dispatch, response correlation, or business authorization.
This rule applies only to the root of the JSON-RPC envelope. Business objects within params, result, and error.data continue to be validated under their own rules. Unknown fields in signed business objects must still be preserved and included in the applicable signature or digest.
Canonical JSON
Canonical JSON in this specification MUST be generated according to the RFC 8785 JSON Canonicalization Scheme, subject to the restriction that only safe integers are permitted:
- Recursively order object fields by field name in unsigned lexicographical order of UTF-16 code units;
- Preserve array order;
- Exclude omitted fields; preserve permitted explicit
nullvalues as given; - Preserve field presence, array elements, and all unknown fields from the input;
- Emit no extra whitespace;
- Do not apply Unicode normalization to strings. Encode U+0000 through U+001F using lowercase
\uhhhhor the specified short escapes under RFC 8785. Encode double quotes and backslashes as\"and\\, respectively; output other Unicode scalar values unchanged and then encode them as UTF-8; - Permit only safe integers as numbers, retaining their shortest decimal form.
Omitted fields, explicit null, and [] produce different Canonical JSON. Before verifying signatures or hashes, a verifier MUST NOT convert between these representations. Outside the input-construction and canonicalization operations prescribed by this specification, it MUST NOT add or remove fields, rewrite field values, or change array order.
Network-bound JSON inputs
Signatures and network-bound hashes of protocol JSON objects use the same input. Signers, hash producers, and verifiers MUST construct $context from the trusted network context and add it directly to the root of the object being processed.
The root of a protocol object transmitted over the network MUST NOT carry $context. $context exists only in locally constructed cryptographic inputs and is not added to the transmitted object.
Input construction
Construct the network-bound object as follows:
- Copy all root fields of the input object. Before constructing a signature input, remove the signature fields specified by the object rules; for a hash input, use the complete hash target specified in the object's section;
- Add
$contextto the same root object, with the expected canonical context string as its value; - Generate Canonical JSON for the resulting single object.
The network-bound JSON input is the Canonical JSON UTF-8 bytes of the merged object:
network_bound_json_bytes(object) =
UTF8(Canonical JSON(network_bound_object(object)))
network_bound_json_hash(object) =
"sha256:" + base64url(SHA-256(network_bound_json_bytes(object)))
Signature and digest rules
The general signature input for a complete JSON object is network_bound_json_bytes(object_without_signature). Signature fields are removed only from the root of the object being signed; nested objects and their independent signatures MUST be preserved unchanged.
Unless the object's section specifies otherwise, signatures, content digests, request-body digests, and replay-prevention digests of protocol JSON objects MUST all use the input above. Digests of raw bytes, such as external content and attachments, are computed directly over those bytes without constructing a network-bound JSON input.
SHA-256 digest representation
The common textual representation of a SHA-256 digest is:
sha256:<base64url(SHA-256(bytes))>
Account signatures
Signature generation and public-key representation
Account signatures use network-bound JSON inputs as the message input; field-exclusion rules defined by each object and method continue to apply. The signing algorithm and byte formats of public keys and signatures MUST follow the chain specification corresponding to the namespace in Account ID. The signer signs this entire byte sequence using the account private key and MUST NOT add another message envelope. Account public keys and signatures are both represented in protocol fields using unpadded base64url.
When the public-key encoding specification supports a compressed representation, account public keys MUST use it.
ECDSA nonces SHOULD be generated deterministically under RFC 6979 using the hash algorithm specified by the signature scheme. They MAY instead be generated using a cryptographically secure random method satisfying ECDSA requirements. Both methods use the same signature-verification rules.
Verification and signature reuse
The verifier MUST verify the signature over the same message input and check the binding between the public key and account ID under the chain's account rules. Relays use Neo N3 account signatures and verify the binding between the Neo N3 single-signature script hash derived from the public key and the Relay ID.
Signature validity is determined by the selected algorithm's verification result, not by whether its bytes equal those of another generated signature. Signatures still participate in complete-object hashes, nested signatures, and request-content comparisons under the object rules. When a method requires an unchanged retry, the original complete object and its signature MUST be reused.
X25519 shared-secret validation
Message key boxes, client group-secret boxes, and relay secret boxes use X25519 as defined in RFC 7748. During sender encapsulation and receiver decapsulation, every X25519 operation MUST check the shared secret before using it in HKDF. If the result is the 32-byte all-zero value, the current key-box operation MUST be aborted. The box MUST NOT be generated or accepted, and encapsulation-key derivation and delivery of decrypted results MUST NOT continue.
This rule strengthens the optional all-zero check in RFC 7748 Section 6.1 into a mandatory protocol requirement. The check applies to the operation's result; rejecting only an all-zero public-key encoding is insufficient, because nonzero low-order inputs can also produce an all-zero shared secret. Other validation requirements for public-key length, base64url encoding, device certificates, business signatures, and key-box authentication continue to apply.
Message length-prefix encoding
Relay RPC and DHT messages use Multiformats unsigned-varint for their outer length prefix, indicating the byte count of the following message payload:
- Encode the unsigned integer starting at its least significant bits, 7 bits at a time. The low 7 bits of each byte carry data; a high bit of 1 indicates another byte follows, while 0 terminates the prefix;
- The range is
0through2^63 - 1, and a prefix has at most 9 bytes. If the high bit of the ninth byte is still 1, the prefix is invalid; - Senders MUST use the shortest encoding, and receivers MUST reject non-shortest encodings. The final byte of a multibyte encoding MUST NOT be
00; zero has only the single-byte encoding00. For example,01means 1, whereas81 00MUST be rejected even if a permissive decoder produces the same value; - An incomplete prefix when the stream ends, an excessively long encoding, or an out-of-range value MUST be rejected.
This range describes only the binary prefix's representational capacity. Even a validly encoded declared length must satisfy the message-size limit and payload requirements of the applicable transport. An invalid prefix is a message-framing failure; the receiver MUST close or reset the corresponding stream without producing an application-layer response.
External specifications
Unless explicitly tightened or replaced by the applicable protocol, encoding and cryptographic behavior follows these specifications:
- RFC 2119 and RFC 8174: normative keywords;
- RFC 8259: JSON;
- RFC 8785: JSON Canonicalization Scheme;
- RFC 4648: base64url;
- FIPS 180-4: SHA-256;
- SEC 1 v2.0: elliptic-curve public-key representation and ECDSA;
- RFC 6979: deterministic ECDSA nonce generation.