RNS Link API
Generic Reticulum Link transport over the MeshChatX WebSocket.
MeshChatX exposes a generic Reticulum Link transport on the main WebSocket (/ws). External apps and plugins can open links, run request/response exchanges, send packets, and tear links down without going through NomadNet helpers.
When to use it#
Your app or plugin needs a live RNS Link
|
--> Not NomadNet page browsing
--> Not LXMF messaging
|
--> Use rns.link.* over /ws
or plugin managers rnsLink.*
Address peers by destination hash and aspect. Do not invent IP or hostname shortcuts.
Auth#
When password auth is enabled, every rns.link.* client message needs an authenticated session. Same rule as other WebSocket mutators.
Link lifecycle#
Client sends rns.link.open
|
--> MeshChatX finds or opens path to destination
|
--> Link cached under (aspect, destination_hash)
|
--> Optional auto_identify
|
--> success / failure reply on same type + request_id
|
+--> rns.link.request / rns.link.send on the cached link
|
+--> rns.link.close tears down and uncaches
|
+--> disconnect cancels in-flight open / request for that client
Cache notes:
- Key is (aspect, destination_hash)
- Cap is 64 active links
- Idle links expire after about 30 minutes
- Repeated request failures recycle the cached link so the next call re-opens
Client to server#
All messages need a unique request_id so replies can be matched.
| type | Required fields | Optional | Behaviour |
|---|---|---|---|
| rns.link.open | destination_hash, aspect, request_id | auto_identify | Open or reuse a cached link. Streams phase then success / failure. |
| rns.link.identify | destination_hash, aspect, request_id | Call link.identify(local_identity) on the cached link. | |
| rns.link.request | destination_hash, aspect, path, request_id | data_b64, timeout | Ensure the link is open, then link.request(path, data=…). |
| rns.link.send | destination_hash, aspect, payload_b64, request_id | Send a raw packet on the cached link. | |
| rns.link.close | destination_hash, aspect, request_id | Teardown and uncache the link. |
Field details:
- destination_hash: hex string of the peer destination
- aspect: dot-separated RNS app name + sub-aspects, for example microrn.mgmt
- data_b64 / payload_b64 / reply body_b64: msgpack payloads, base64-encoded (size-capped on the server)
- path: request path string on the remote link endpoint
- timeout: seconds for the request wait
Optional binary frames: send { "type": "ws.caps", "binary_rns_link": true } first. After that, binary WebSocket frames carrying msgpack dicts with the same fields as the JSON messages are accepted. JSON remains the default and is always supported.
Example open:
{
"type": "rns.link.open",
"destination_hash": "aabbccddeeff00112233445566778899aabbccdd",
"aspect": "microrn.mgmt",
"request_id": "req-1",
"auto_identify": true
}
Example request:
{
"type": "rns.link.request",
"destination_hash": "aabbccddeeff00112233445566778899aabbccdd",
"aspect": "microrn.mgmt",
"path": "/status",
"request_id": "req-2",
"data_b64": null,
"timeout": 15
}
Server to client#
Per-request_id replies reuse the same type with a status:
| status | Meaning |
|---|---|
| phase | Progress step while opening or requesting |
| progress | Additional progress detail when available |
| success | Operation finished |
| failure | Operation failed (includes an error message) |
Broadcast events (not tied to one request_id):
| type | event | Notes |
|---|---|---|
| rns.link.event | packet_received | Includes payload_b64 |
| rns.link.event | link_closed | Cached link removed |
Inbound packet on a cached link
|
--> Broadcast rns.link.event / packet_received
|
Link torn down or evicted
|
--> Broadcast rns.link.event / link_closed
Plugins#
Plugins call the same transport through HTTP invoke instead of speaking WebSocket types directly.
Plugin Worker
|
--> POST /api/v1/plugins/{id}/invoke
method: "callManager"
|
--> PluginManager checks granted managers
|
--> RnsLinkManager open / identify / request / send / close
Declare managers in plugin.json:
| Manager | Maps to |
|---|---|
| rnsLink.open | Open or reuse link |
| rnsLink.identify | Identify on cached link |
| rnsLink.request | Request/response |
| rnsLink.send | Raw packet send |
| rnsLink.close | Teardown |
Subscribe to async traffic with:
{
"permissions": {
"hooks": ["rns.link.event"],
"managers": ["rnsLink.open", "rnsLink.identify", "rnsLink.request", "rnsLink.send", "rnsLink.close"],
"storage": "isolated",
"network": "none"
}
}
Hook delivery:
RnsLinkManager event
|
--> PluginManager.dispatch_hook("rns.link.event", …)
|
--> WebSocket plugin.event to the UI
|
--> Plugin Worker on_hook / event handler
External app pattern#
Connect to MeshChatX /ws (auth cookie / session as required)
|
--> Send rns.link.open with request_id
|
--> Wait for matching success
|
--> Send rns.link.request or rns.link.send
|
--> Listen for rns.link.event broadcasts
|
--> Send rns.link.close when finished
Keep one request_id per outstanding call. Cancel or ignore replies after you disconnect. MeshChatX cancels in-flight open/request work for that WebSocket client on disconnect.
Limits and failure behaviour#
- Missing path or unreachable peer returns failure on the open/request reply
- After repeated request failures on one cached link, MeshChatX recycles that link
- Idle unused links are swept after about 30 minutes
- Over-cap eviction drops the oldest unused links first
Implementation map#
/ws rns.link.*
|
--> meshchat.py WebSocket dispatch + per-client task tracking
|
--> rns_link_manager.py cache, open, identify, request, send, close
|
--> plugin_manager.py capability wrappers + hook fan-out
See also#
- Plugins for install, grants, and invoke flow
- Architecture and design for WebSocket and plugin runtime overview
- Identities, privacy, and security for auth and session rules