Relay DHT core objects
Resource-key derivation rules
A DHT key identifies a logical resource in a particular network context. The resource-key input is an object used only for key derivation and not transmitted over the network:
{
"$type": "meshline.dht.resource.account.route",
"$context": "neo:860833102:0x...",
"resource_id": "neo:860833102:..."
}
| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
$type |
string | Yes | Resource type; see the account route resource rules |
$context |
string | Yes | The canonical string of the trusted network context, constructed locally |
resource_id |
string | Yes | Resource identifier; see the account route resource rules |
The DHT key is the raw 32 bytes of SHA-256(UTF8(Canonical JSON(resource-key input))). The same account produces different keys in different network contexts.
This protocol accepts only account route resources that can be fully validated under the AccountRoute rules. A relay may still execute FIND_NODE for any resource key under Kademlia routing rules without understanding its resource.
AccountRoute
AccountRoute is a routing declaration jointly signed by the account and its current home relay and published to the DHT by that relay. Other eligible storage nodes MAY copy valid documents unchanged under republication and persistence. Other relays use the declaration to locate the target relay for profile.resolve, device.state.resolve, device-status queries, and message delivery. Connection addresses and relay verification public keys come from the Registry and RelayDescriptor. Business permissions are determined by the relevant objects' account signatures, device signatures, and delivery credentials.
Client request submission and cross-relay forwarding follow the call and routing model.
{
"$type": "meshline.account.route",
"account": "neo:860833102:NU...",
"account_public_key": "base64url...",
"revision": 1730000000123,
"relay_id": "0x1234567890abcdef1234567890abcdef12345678",
"updated_at": 1730000000,
"expires_at": 1745552000,
"account_signature": "base64url...",
"relay_signature": "base64url..."
}
| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
$type |
string | Yes | Fixed to meshline.account.route |
account |
string | Yes | Account ID of the route owner; MUST conform to its chain's account rules and equal the account derived from account_public_key |
account_public_key |
string | Yes | Account public key from which account can be derived under the Account ID rules, in unpadded base64url; follows the public-key representation conventions in account signatures |
revision |
integer | Yes | The route record's nonnegative monotonic version; range and exhaustion rules follow monotonic versions and counters |
relay_id |
string | Yes | The current home relay's Relay ID in the Registry |
updated_at |
integer | Yes | Route signing time |
expires_at |
integer | Yes | Route expiry time |
account_signature |
string | Yes | Signature generated by account under the account-signature rules, in unpadded base64url |
relay_signature |
string | Conditional | A 64-byte signature generated under account signatures by the Neo N3 relay account corresponding to relay_id, in unpadded base64url; MUST be omitted from publication requests and present in final documents |
relay_id designates the unique current home relay for account state, profiles, device queries, route publication, and the message timeline. The protocol defines neither standby relays nor automatic takeover.
Signing order
AccountRoute is signed in the following order. Both signature inputs are constructed under network-bound JSON inputs:
- The account signature input excludes root
account_signatureandrelay_signature; the account generatesaccount_signature. - The relay signature input excludes only root
relay_signature, thereby covering the body and the exact receivedaccount_signature; the relay generatesrelay_signature.
Validation rules
The object MUST first satisfy the field-table constraints, followed by these checks:
- The complete Canonical JSON UTF-8 encoding of the final jointly signed document MUST NOT exceed 4 KiB (4,096 bytes);
- The publishing relay and every storage or querying node validating a DHT record MUST independently confirm that
updated_atdoes not exceed local Unix seconds plus the locally allowed future skew. A violation MUST be rejected even when both signatures are valid; the request or record MUST NOT raise the retained highest route version or change conflict state; expires_atMUST be later thanupdated_at; the difference MUST NOT exceed 10 years, using 365 days per year, namely 3,650 days or 315,360,000 seconds. The record must not have expired when used;- The account signature MUST be valid. A final document MUST also contain a valid relay signature whose public key derives
relay_id.
The verifier MUST confirm that the Registry record for relay_id has status set to active, obtain a verified current RelayDescriptor, and then use its public_key to verify the relay signature.
The DHT accepts only final jointly signed documents. The submitting peer MUST be an eligible public relay that has passed connection identity authentication.
DHT resource identification
AccountRoute derives its DHT key under the resource-key derivation rules, with the following resource-type rules:
| Item | Value | Semantics and constraints |
|---|---|---|
$type |
meshline.dht.resource.account.route |
Identifies the account route resource type |
resource_id |
The account owning the route document | An Account ID; all route versions of the same account use the same resource ID |
| Resource value encoding | UTF-8 JSON bytes of AccountRoute |
Transmitted as Kademlia Message.record.value; validated after decoding under the AccountRoute validation rules |
When validating a DHT record, the receiver MUST decode Message.record.value as UTF-8 JSON, parse the AccountRoute, derive the key again using its canonical account as resource_id, and confirm that the result exactly equals Message.record.key. Both record updates and candidate selection follow route version and conflict resolution.
Route version generation rules
revision represents version order for an account's routes. One account's single revision may correspond to only one set of route contents. Neither the account nor the relay may sign routes with different contents at the same version. A new record replacing a known route MUST use a strictly greater value. It MUST be a nonnegative safe integer and increase monotonically within the account.
highest_known_revision is the greater of the highest final route version fully verified by the client and this account's last signed value persisted locally. If neither exists, use -1.
Using current UTC Unix milliseconds as a lower bound is recommended, while ensuring that the new value exceeds the known version:
revision = max(highest_known_revision + 1, current_unix_time_milliseconds)
The client MUST ensure that multiple devices of one account do not use the same revision for different route contents. Different contents at the same version are handled by the route version and conflict-resolution rules below.
Route version and conflict resolution
Local version retention and rollback prevention
Relays MUST independently persist, by trusted network context and account, the highest revision of fully verified final routes, information sufficient to compare the route contents at that version, and unresolved same-version-conflict state. Only final jointly signed documents that are unexpired on receipt and pass all validation may update this information. Unverified publication drafts, invalid records, and records already expired on receipt MUST NOT raise the highest version.
The highest version may only increase. It MUST NOT decrease or be cleared because of route or DHT-value expiry, cache cleanup, account migration, or node restart. Once the corresponding complete route expires, it MUST NOT be returned, used, or republished as valid. Regardless of whether its local contents are retained, the version and conflict information MUST be retained permanently. A relay MUST persist the corresponding version information before acknowledging acceptance of a write, returning a resolution result, or using a new route for business operations.
Records below the retained highest version MUST NOT be accepted again as the current route, nor returned or used as current through caches, DHT queries, or republication. Valid copies of the same version and contents MAY be reacquired or republished. If the highest-version route has expired and no new route is available, the account has no usable route and MUST wait for the account to sign a higher version. An older version cannot be reinstated even if it remains unexpired.
This information reflects only routes this relay has verified. Nodes that have not seen a new version must still discover it through DHT queries; local version retention is not proof that the entire network has revoked old copies.
Candidate comparison and conflict handling
Route contents are compared using network-bound JSON after excluding account_signature and relay_signature; both documents' signatures must still be independently verified. Duplicate copies with identical contents do not constitute a conflict.
Process all candidate values for the same DHT key in this order:
- Discard records with mismatching keys or failing the validation rules. This does not clear retained version and conflict information;
- Exclude records below this relay's retained highest version, then select records with the greatest
revisionamong the remaining candidates. Same-version copies with identical contents count as one candidate route and are compared together with locally retained contents at that version; - Different contents at the greatest
revisionconstitute a protocol conflict. Conflict state MUST be persisted. A valid route MUST NOT be selected from them by digest or arrival order, and the relay MUST NOT fall back to a lower version.
When a valid higher-version candidate is obtained, advance the local highest version under the persistence requirements above. If that highest version has different contents, record the conflict and return none of those records as a valid route.
While an unresolved same-version route conflict exists, a relay MUST suspend services for that account that depend on its current route until it obtains a valid conflict-free route above the conflicting version. Expiry of conflicting records, or later queries seeing only one of them, does not constitute replacement by a higher version. If filtering leaves no usable candidate and no unresolved conflict exists, resolution returns no usable route.