Skip to main content

Module protocol

Module protocol 

Source
Expand description

mcpmesh-local/1 protocol types. Shared vocabulary between the daemon and its clients (porcelain, connect proxy, later the host shell). Wire framing is the family NDJSON codec — carried by the caller, not defined here.

Request/response asymmetry: requests are one typed, closed enum (Request); responses are per-method typed structs deserialized from the JSON-RPC result Value — StatusStatusResult, RegisterService → an ack, OpenSession → no JSON-RPC result at all: the socket STOPS being JSON-RPC and becomes a raw byte pipe.

Additive-only: new fields (capabilities on Hello, groups/user_id on PeerInfo, device on OpenSession) MUST land as #[serde(default, skip_serializing_if = ...)] so older payloads still deserialize.

Structs§

ActiveSession
One live mesh session, in a StreamFrame::Snapshot. Surface-clean: peer is the user_id-or-nickname the audit records carry, never an endpoint-id. opened_at is epoch seconds.
AuditRecord
One audit record — the union of the event classes, and the record payload of a StreamFrame::Event. ONE schema for the on-disk JSONL log and the live stream. Every field beyond ts/kind is optional and elided when absent (skip_serializing_if), so each class serializes to just its relevant keys (a session record has no method; a trust record has no bytes_out).
AuditSummaryResult
Result of Request::AuditSummary: LOCAL per-peer / per-service session counts aggregated from this node’s OWN audit log — NEVER transmitted (local-only). Surface-clean: peer names are nicknames / user_ids (NEVER EndpointIds), service names are the registered service names (NEVER transport vocabulary). A “session” is one SessionOpen record. per_peer / per_service are sorted ascending by name (deterministic). Tuples mirror kb’s InsightResponse::per_peer_contribution["bob", 2] on the wire.
BlobFetchParams
Params of Request::BlobFetch: the mcpmesh/blob/1 ticket and the LOCAL export path.
BlobFetchResult
Result of Request::BlobFetch: the verified hash + byte length written to dest_path. Additive-only.
BlobGrantParams
Params of Request::BlobGrant: the scope and the flat-namespace principal to grant it to.
BlobPublishParams
Params of Request::BlobPublish: the scope to publish into and the LOCAL file to add.
BlobPublishResult
Result of Request::BlobPublish: the copyable mcpmesh/blob/1 ticket + the blob’s blake3 hash. A ticket/hash here is blob-reference vocabulary (NOT a transport-vocab leak — the same carve-out as the pairing invite line). Additive-only.
BlobScopeList
Result of Request::BlobList: the daemon’s scopes. Additive-only.
Hello
The first exchange on any *-local/N socket (the family’s hello convention).
InviteParams
Params of Request::Invite: the services the minted invite grants. Rejects unknown fields (so {service: "kb"} — a singular typo — is a loud error, not a silently grants-nothing invite), and the daemon additionally rejects an empty/absent services list (an invite that grants nothing is useless — #34).
InviteResult
Result of an Request::Invite request: the copyable mcpmesh-invite: artifact (the ONE pairing artifact deliberately carved out of the transport-vocabulary blocklist, so this is NOT a transport-vocab leak) plus its absolute expiry in epoch seconds (≤ now + 24h).
OpenSessionParams
Params of Request::OpenSession: the peer/service target to dial. Both fields are defaultable — an empty target simply fails the dial (a clean -32055 error).
OrgJoinParams
Params of Request::OrgJoin: the [identity] pin. user_key is a LOCAL path — the key never crosses the API.
OrgJoinResult
Result of Request::OrgJoin — the pinned org id echoed back (surface-clean; the fingerprint is computed porcelain-side from the invite’s org_root_pk). Additive-only.
PairParams
Params of Request::Pair: the copyable mcpmesh-invite: line. Defaultable — an absent field reads as an empty line, which simply fails to decode (a clean pair error).
PairResult
Result of a Request::Pair request: the inviter’s suggested nickname (the redeemer’s local name for the new peer) plus the display-only short authentication code (SAS) — a few words the human reads aloud to a second channel to catch a whole-invite forgery / address-swap MITM. The SAS is a pairing-ceremony artifact (like the invite line), NOT a transport-vocabulary leak.
PeerAddParams
Params of Request::PeerAdd (reserved/internal — see the variant): a raw endpoint_id (iroh base32) plus the nickname and service allow list to install it under.
PeerInfo
A known peer as reported by status (nickname only — never the EndpointId).
PeerReachability
Advisory reachability of a paired peer (pairing-mode liveness). Surface-clean: a nickname + a bool + latency/age NUMBERS — never an endpoint-id, key, or transport path.
PeerRemoveParams
Params of Request::PeerRemove: the nickname to unpair.
PeerRenameParams
Params of Request::PeerRename: the contact to rename — every device sharing user_id when given, else the single provisional nickname entry — and the new nickname to.
PeerServicesParams
Params of Request::PeerServices (#52): the peer to query — a nickname, an eid: device principal, or a b64u: user_id.
PeerServicesResult
Result of Request::PeerServices (#52): the services the queried peer CURRENTLY grants the caller — computed authoritatively on the peer (which owns the truth), always current, only the caller’s own admitted services (never the peer’s full registry).
PresencePeer
One reachable roster peer device as reported by status (the advisory presence read). ADVISORY — this is a display convenience, never an authorization surface. Surface-clean: FLAT vocabulary ONLY — a user_id, a human device_label, its role word, and an online boolean. It carries NO EndpointId / pubkey / hash / ALPN or any transport vocabulary.
RecentPairing
One recently completed INVITER-side pairing, surfaced by status so the inviter’s human can read the short authentication code (SAS) and compare it with the redeemer’s out-of-band — the pairing ceremony is “both humans compare the code”: the redeemer sees it in its PairResult; this is the inviter’s porcelain surface for the same words. DISPLAY-ONLY ceremony state: held in-memory by the daemon (a small ring), lost on restart, NEVER an authorization input or trust data. Surface-clean: a nickname + the SAS wordlist words + an epoch — never an EndpointId.
RegisterServiceParams
Params of Request::RegisterService: the [services.*] entry to write/update.
RosterInstallParams
Params of Request::RosterInstall: the LOCAL roster file path, plus the org-root pin on FIRST install (b64u:; omit once pinned — config carries it).
RosterInstallResult
Result of a Request::RosterInstall request (the manual install path): the installed roster’s org id + serial (roster-status vocabulary the confirmation line is permitted to render) plus how many live sessions the install severed. Surface-clean: NO keys / EndpointIds / paths.
RosterStatus
Roster-mode status. Surface-clean roster VOCABULARY only: org_id, serial, a plain state word, and the pinned org-root FINGERPRINT in short words — never raw keys/EndpointIds/serials- as-transport-vocab. Absent in a pure-pairing daemon.
ScopeInfo
One scope in a BlobScopeList: its name + the hashes it contains + the principals it grants. Flat vocabulary ONLY — no EndpointId/pubkey/ALPN. Additive-only.
ServiceAllowParams
Params of Request::ServiceAllowGrant / Request::ServiceAllowRevoke (#44): toggle a single stable principal (b64u:/eid:) on a single service’s allow list, WITHOUT unpairing. The per-peer “sharing” switch primitive the embedder drives.
ServiceInfo
A registered service as reported by status (no transport vocabulary).
SetAppMetadataParams
Params of Request::SetAppMetadata: this node’s opaque app-metadata blob (#39). The daemon NEVER interprets it — the embedder structures its own bytes (a version string, small JSON, …). Capped at 256 bytes; "" clears it. Roster-mode only (it rides the signed presence heartbeat); a pure-pairing daemon accepts + stores it but never gossips it.
SetNicknameParams
Params of Request::SetNickname: this node’s new self-nickname (#37). Display-only semantics: it names this node in FUTURE invites/presentations; peers keep the nickname they stored at pairing time until a re-invite.
SetRosterUrlParams
Params of Request::SetRosterUrl: the HTTPS roster URL to pin.
StatusResult
UnregisterServiceParams
Params of Request::UnregisterService (#50): the persistent (or ephemeral) service name to remove — the deregistration mirror of register_service.

Enums§

AuditKind
The event class of an AuditRecord (the four audit event classes). An additive discriminant on top of the base record schema: it removes no field and makes the JSONL self-describing so a consumer can filter by class without guessing from which optional fields are present.
BackendKind
The kind of backend answering a service — the two valid values, enforced at the type level and kept in lockstep with BackendSpec’s variants. Status reports the kind only, never the command/path (no transport vocabulary).
BackendSpec
How a service is answered. Mirrors the config [services.*] kinds; Config→BackendSpec is a hand-written match, not a serde passthrough.
Request
Control-API requests. Serialized as { "method": "...", "params": {...} } (JSON-RPC-shaped; the id/jsonrpc envelope is added by the transport layer).
StreamFrame
One frame of the Request::Subscribe stream (pairing liveness & health telemetry). Tagged on type (snake_case), so a frame is {"type":"snapshot",...} / {"type":"event",...} / {"type":"lagged",...}. Event.record is the AuditRecord verbatim, so the stream and the on-disk log carry ONE schema. The daemon serializes these; an embedding consumer deserializes them (see docs/local-protocol.md “Live event stream”).

Constants§

API_MINOR
The integer MINOR of API_VERSION — see there. Bumped from 0 to 1 when params validation became strict (#34); to 2 with the set_nickname verb + StatusResult.self_nickname (#37); to 3 when allow/grant strings became STABLE principals — b64u:/eid:/roster names, never nicknames (#38); to 4 with the set_app_metadata verb + PresencePeer.meta (#39); to 5 with PeerReachability.meta — pairing-mode app metadata on the probe pong (#40); to 6 with PeerInfo.principal — the peer’s eid: device principal on status (#41); to 7 with PeerReachability.principal — the same on reachability rows (#42); to 8 with the service_allow_grant/service_allow_revoke per-peer access verbs (#44).
API_NAME
API_VERSION
The protocol-compatibility version as "MAJOR.MINOR", distinct from the crate/stack version.

Functions§

method_of
Extract the method tag from a raw request value without deserializing the whole message. The daemon’s dispatcher uses this: match on the method string, then deserialize params per-method — which tolerates omitted / null / {} params for parameterless methods (adjacent tagging rejects params:{} on unit variants).