Skip to content

HTTP API: mesh

The mesh of this process: discovery — authenticated observations of other running Majordomus instances in one registry — and cooperation — authenticated links to trusted runtimes of the same repository, replicating sessions, claims, handovers and reviews through one journal every runtime folds into the same state. Discovery grants nothing; a link is admitted per peer, and nothing a peer sends executes anything here.

Rendered from docs/generated/openapi.json, which the executable generates from its capability registry and CI refuses stale. The raw document is at /openapi.json; a running server serves the same document at its own /openapi.json and Swagger UI at /swagger.

mesh

The mesh of this process: discovery — authenticated observations of other running Majordomus instances in one registry — and cooperation — authenticated links to trusted runtimes of the same repository, replicating sessions, claims, handovers and reviews through one journal every runtime folds into the same state. Discovery grants nothing; a link is admitted per peer, and nothing a peer sends executes anything here.

GET /api/v1/mesh mesh.status

The mesh, at a glance

Whether the mesh runs in this process and why not when it does not; this node's public identity; every discovery provider with its state and counters; the registry's tallies; and the refused datagrams by reason. The one status every surface shows.

  • query
  • experimental
  • benchmark required
  • cache disabled
  • MCP tool majordomus_mesh
  • MCP resource {"name": "mesh", "uri": "majordomus://mesh"}

No parameters.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/mesh"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 MeshStatus The mesh, at a glance: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
POST /api/v1/mesh/claims mesh.claim

Claim a scope across the mesh

Claim repository paths for a session. An exclusive claim that meets a live exclusive claim of another session — on this runtime or any runtime this one has heard — is refused as `claim_conflict` with the claims it meets; an advisory claim is recorded and its overlaps reported. A claim lives while its session is open and its runtime beats: a crashed holder's claim expires everywhere on its own. Writes this runtime's journal only.

  • command
  • experimental
  • benchmark waived: transient_state
  • cache disabled
  • MCP tool majordomus_mesh_claim

Request body — ClaimInput, JSON

example exclusive

{"session":"bench","scope":["docs"]}

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s -X POST "http://127.0.0.1:8741/api/v1/mesh/claims" -H "content-type: application/json" -d '{"session":"bench","scope":["docs"]}'

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 Written Claim a scope across the mesh: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
422 ErrorBody refused: The command was understood and turned down for the reason the message gives.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
POST /api/v1/mesh/claims/release mesh.release

Release a claim

Release a claim this runtime's current run holds, by its key. A claim written elsewhere is refused as `not_own`: only its holder releases it, and a dead holder's claim expires instead. Writes this runtime's journal only.

  • command
  • experimental
  • benchmark waived: transient_state
  • cache disabled
  • MCP tool majordomus_mesh_release

Request body — ReleaseInput, JSON

example unknown

{"claim":"none/c-0"}

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s -X POST "http://127.0.0.1:8741/api/v1/mesh/claims/release" -H "content-type: application/json" -d '{"claim":"none/c-0"}'

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 Written Release a claim: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
422 ErrorBody refused: The command was understood and turned down for the reason the message gives.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/mesh/cooperation mesh.cooperation

Cooperation, at a glance

Whether this runtime cooperates and why not when it does not: its runtime and stream, the repository identity links are matched on, the link protocol and features, the heartbeat and expiry, every linked peer with its link state, last exchange, round trip and failures, every refused candidate with the rule that refused it, and the counters of handshakes, syncs, reconnects, expiries, replicated and refused events.

  • query
  • experimental
  • benchmark required
  • cache disabled
  • MCP tool majordomus_mesh_cooperation

No parameters.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/mesh/cooperation"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 CooperationStatus Cooperation, at a glance: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/mesh/doctor mesh.doctor

The mesh self-check

Every prerequisite proved on this machine alone — the declaration parses, the identity loads and, under an allowlist, is on it, the repository has a mesh identity, a UDP socket binds, the multicast group joins, broadcast enables, and the discovery and link protocols sign and verify end to end in memory — and then the server's own verdict: in a shared server, whether an enabled declaration actually activated the mesh and, when it did not, the server's reason, which fails the report. Each failed check names its impact and its remedy. Deterministic, no second node required.

  • query
  • experimental
  • benchmark required
  • cache disabled
  • MCP tool majordomus_mesh_doctor
  • $ mesh doctor

No parameters.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/mesh/doctor"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 MeshDoctorReport The mesh self-check: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/mesh/events mesh.events

The cooperation journal

The journal's events above a Lamport stamp, in Lamport order, at most a page: each with its stream, sequence, stamp, repository, signing key, kind and body. The answer's lamport is the next page's `after`.

  • query
  • experimental
  • benchmark required
  • cache disabled
  • MCP tool majordomus_mesh_events

Query parameters

nametyperequireddescriptionexamples
after integer (uint64) no Only events whose Lamport stamp is above this.
limit integer (uint64) no At most this many (default 100, at most 1000). default: 100
newest boolean no The newest `limit` events above `after` instead of the first ones: what a reader who wants the journal as it stands now asks for. Still in Lamport order, and `lamport` is then the newest stamp the journal holds, so resuming after it reads only what comes next. Without it the journal is read forward from `after`, a page at a time.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/mesh/events?limit=100"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 EventList The cooperation journal: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/mesh/firewall mesh.firewall

The host firewall, as the mesh needs it

What inbound traffic the declaration implies this machine must admit — the multicast group's port, the broadcast port when it is a permitted fallback, every declared hub port whose address is this machine's, and the server's own port when it listens beyond loopback — from the private networks the declaration names; which firewall front this host runs (ufw, nftables, the macOS application firewall); the commands that admit the plan there; what the firewall itself says about the plan now (present, missing, inactive, or unobservable without root); and what the kernel logged it dropping toward those ports in the last five minutes. Fails when a rule is observed missing or a drop was logged. Reads the host; changes nothing.

  • query
  • experimental
  • benchmark waived: external_dependency
  • cache disabled
  • MCP tool majordomus_mesh_firewall
  • $ mesh firewall

Query parameters

nametyperequireddescriptionexamples
port integer (uint16) no The port this checkout's server listens on beyond loopback, when it does: a peer dials it there on the link protocol, so it needs admitting too. Omitted, the plan holds the discovery ports and the hub ports the declaration gives this machine.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/mesh/firewall"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 FirewallReport The host firewall, as the mesh needs it: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
POST /api/v1/mesh/handovers mesh.handover.publish

Publish a handover to the mesh

Publish a handover record of this checkout — the newest, or the one named under .ai/local/state/handovers/ — to every linked runtime: its task, branch, head and time from its front matter, the issue and milestone given, and its Markdown body, bounded and identified by the body's digest. No path of this machine travels, and no file outside the handovers directory is ever read.

  • command
  • experimental
  • benchmark waived: transient_state
  • cache disabled
  • MCP tool majordomus_mesh_handover_publish

Request body — HandoverPublishInput, JSON

example latest

{}

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s -X POST "http://127.0.0.1:8741/api/v1/mesh/handovers" -H "content-type: application/json" -d '{}'

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 Written Publish a handover to the mesh: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
422 ErrorBody refused: The command was understood and turned down for the reason the message gives.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
POST /api/v1/mesh/handovers/consume mesh.handover.consume

Consume a handover from the mesh

Take a handover another runtime published: record the consumption on the mesh, and write it into this checkout's handovers directory as a record `majordomus handover --resolve` finds on the same branch — once, however often it is consumed.

  • command
  • experimental
  • benchmark waived: transient_state
  • cache disabled
  • MCP tool majordomus_mesh_handover_consume

Request body — HandoverConsumeInput, JSON

example unknown

{"handover":"00000000000000000000000000000000","session":"bench","materialize":false}

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s -X POST "http://127.0.0.1:8741/api/v1/mesh/handovers/consume" -H "content-type: application/json" -d '{"handover":"00000000000000000000000000000000","session":"bench","materialize":false}'

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 ConsumeAnswer Consume a handover from the mesh: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
422 ErrorBody refused: The command was understood and turned down for the reason the message gives.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/mesh/identity mesh.identity

This machine's node identity

The node identity kept under the user's state directory, public half only: node id, public key, display name. The signing key appears in no projection. Absent is an answer, not an error — the identity is created when a mesh first activates.

  • query
  • experimental
  • benchmark required
  • cache disabled
  • MCP tool majordomus_mesh_identity
  • $ mesh identity

No parameters.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/mesh/identity"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 MeshIdentityReport This machine's node identity: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/mesh/nodes mesh.nodes

The discovered nodes

Every runtime this process has observed, one record per node and runtime slot, deduplicated across every discovery source, in node-id order: identity, trust, presence, endpoints, capabilities, repositories, provenance and versions. In memory, gone with the process.

  • query
  • experimental
  • benchmark required
  • cache disabled
  • MCP tool majordomus_mesh_nodes

No parameters.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/mesh/nodes"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 NodeList The discovered nodes: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/mesh/peer mesh.peer

One runtime

One runtime by its key (`<node>-<runtime>`, or a node id for its first runtime): the machine it runs on, its liveness, its link from here, its sessions and claims, and any refusal recorded against it. Not found is an answer with the reason.

  • query
  • experimental
  • benchmark required
  • cache disabled
  • MCP tool majordomus_mesh_peer

Query parameters

nametyperequireddescriptionexamples
runtime string yes The runtime, `<node>-<runtime>` (a node id alone matches its first runtime). unknown: "0000000000000000000000000000000000000000000000000"

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/mesh/peer?runtime=0000000000000000000000000000000000000000000000000"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 PeerDetail One runtime: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/mesh/peers mesh.peers

Who cooperates, machine by machine

Machine → runtime → session → claim: every runtime this one is linked to or has heard through the journal, grouped by machine, local first; each runtime with its liveness, the milliseconds since its beat rose, its link and its newest live claim. The default `summary` view lists only the active sessions, each with its live claims counted and its intent cut; `view=full` lists every session with every claim. `session` and `runtime` narrow either. Remote and local runtimes share one shape, and the whole fold's digest closes the answer: equal digests, equal state.

  • query
  • experimental
  • benchmark required
  • cache disabled
  • MCP tool majordomus_mesh_peers

Query parameters

nametyperequireddescriptionexamples
view MeshView | null no `summary` (the default) or `full`. full: "full"
claims MeshClaimFilter | null no `all` (the default) or `live`: whether ended claims are kept.
after integer (uint64) no Only sessions, claims and handovers whose latest Lamport stamp is above this. Reviews and overlaps carry no stamp of their own and are kept or dropped with their sessions.
session string no Only what belongs to this session: its key (`<stream>/<session>`) or its bare id.
runtime string no Only what belongs to this runtime: `<node>-<runtime>`, or a node id for every runtime of that machine.
kind MeshKind | null no Only this collection; the others are answered empty.
limit integer (uint) no At most this many of each collection: the newest by Lamport stamp where it has one, kept in the fold's own order.
open boolean no Only what is still open: active sessions, live claims, review requests nobody has answered and handovers nobody has taken.
matching string no Only what mentions this text, ignoring case: a path, a branch, an issue, a pull request, a word of an intent or a subject.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/mesh/peers?view=full"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 PeerTree Who cooperates, machine by machine: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
POST /api/v1/mesh/register mesh.register

Register with this node's mesh

Present one signed envelope; it is verified exactly as a datagram — bounds, staleness, signature, trust policy — and recorded as a rendezvous observation when it holds. The answer carries this node's own envelope and the candidates its registry holds, each verifiable end to end on its own signature. Registration grants nothing: the caller becomes a record, never an authorization. Changes this process's memory only.

  • command
  • experimental
  • benchmark required
  • cache disabled
  • MCP tool majordomus_mesh_register

Request body — RegisterInput, JSON

example refused

{"envelope":{"v":0}}

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s -X POST "http://127.0.0.1:8741/api/v1/mesh/register" -H "content-type: application/json" -d '{"envelope":{"v":0}}'

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 RegisterAnswer Register with this node's mesh: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
422 ErrorBody refused: The command was understood and turned down for the reason the message gives.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
POST /api/v1/mesh/reviews mesh.review.request

Ask the mesh for a review

Ask for a review of a branch, commit or pull request, optionally of one named runtime — which must be linked and carry the `reviews` feature, or the request is refused as `feature_unsupported`. Replicated to every linked runtime. Writes this runtime's journal only.

  • command
  • experimental
  • benchmark waived: transient_state
  • cache disabled
  • MCP tool majordomus_mesh_review_request

Request body — ReviewRequestInput, JSON

example open

{"session":"bench","subject":"feature/bench","scope":[]}

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s -X POST "http://127.0.0.1:8741/api/v1/mesh/reviews" -H "content-type: application/json" -d '{"session":"bench","subject":"feature/bench","scope":[]}'

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 Written Ask the mesh for a review: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
422 ErrorBody refused: The command was understood and turned down for the reason the message gives.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
POST /api/v1/mesh/reviews/answer mesh.review.answer

Answer a review request

Answer a review request from any runtime with approved, changes_requested or commented, and a note. Replicated to every linked runtime. Writes this runtime's journal only.

  • command
  • experimental
  • benchmark waived: transient_state
  • cache disabled
  • MCP tool majordomus_mesh_review_answer

Request body — ReviewAnswerInput, JSON

example unknown

{"request":"none/r-0","session":"bench","verdict":"commented"}

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s -X POST "http://127.0.0.1:8741/api/v1/mesh/reviews/answer" -H "content-type: application/json" -d '{"request":"none/r-0","session":"bench","verdict":"commented"}'

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 Written Answer a review request: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
422 ErrorBody refused: The command was understood and turned down for the reason the message gives.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
POST /api/v1/mesh/reviews/withdraw mesh.review.withdraw

Withdraw a review request

End a review request this runtime asked for — done, void or superseded — with an optional result note the request then carries. Any session of the asking runtime may (a reconnected asker is another session of it); no other runtime may, and it is never counted as an answer. An unanswered request also reads expired while its runtime has stopped beating. Replicated to every linked runtime. Writes this runtime's journal only.

  • command
  • experimental
  • benchmark waived: transient_state
  • cache disabled
  • MCP tool majordomus_mesh_review_withdraw

Request body — ReviewWithdrawInput, JSON

example unknown

{"request":"none/r-0","session":"bench","note":"superseded"}

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s -X POST "http://127.0.0.1:8741/api/v1/mesh/reviews/withdraw" -H "content-type: application/json" -d '{"request":"none/r-0","session":"bench","note":"superseded"}'

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 Written Withdraw a review request: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
422 ErrorBody refused: The command was understood and turned down for the reason the message gives.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
POST /api/v1/mesh/sessions mesh.session.open

Open or update a session on the mesh

Say what a session of this runtime is: its client, worker, intent, task, issue, milestone, branch, head, dirtiness and context revision. Replicated to every linked runtime; a later word replaces an earlier one. Writes this runtime's journal only.

  • command
  • experimental
  • benchmark waived: transient_state
  • cache disabled
  • MCP tool majordomus_mesh_session_open

Request body — SessionInput, JSON

example named

{"session":"bench","intent":"benchmark"}

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s -X POST "http://127.0.0.1:8741/api/v1/mesh/sessions" -H "content-type: application/json" -d '{"session":"bench","intent":"benchmark"}'

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 Written Open or update a session on the mesh: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
422 ErrorBody refused: The command was understood and turned down for the reason the message gives.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
POST /api/v1/mesh/sessions/close mesh.session.close

Close a session on the mesh

End a session of this runtime; every claim it holds ends with it, on every linked runtime. Writes this runtime's journal only.

  • command
  • experimental
  • benchmark waived: transient_state
  • cache disabled
  • MCP tool majordomus_mesh_session_close

Request body — SessionCloseInput, JSON

example named

{"session":"bench"}

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s -X POST "http://127.0.0.1:8741/api/v1/mesh/sessions/close" -H "content-type: application/json" -d '{"session":"bench"}'

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 Written Close a session on the mesh: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
422 ErrorBody refused: The command was understood and turned down for the reason the message gives.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/mesh/state mesh.state

The state every linked runtime converges on

The journal folded. The default `summary` view is bounded whatever the history: sessions and claims counted by standing, each runtime with its liveness, its counts and its newest live claim, the open review requests and the handovers nobody has taken, and the digest two runtimes compare. `view=full` is every session, every claim with its standing (held, released, expired, conflicted with its winner), the advisory overlaps, every handover with who consumed it, every review request with its answers, and every stream's liveness on this runtime's clock. `claims=live`, `after` (a Lamport stamp), `session` and `runtime` narrow either view; the digest is always the whole fold's.

  • query
  • experimental
  • benchmark required
  • cache disabled
  • MCP tool majordomus_mesh_state

Query parameters

nametyperequireddescriptionexamples
view MeshView | null no `summary` (the default) or `full`. full: "full"
claims MeshClaimFilter | null no `all` (the default) or `live`: whether ended claims are kept.
after integer (uint64) no Only sessions, claims and handovers whose latest Lamport stamp is above this. Reviews and overlaps carry no stamp of their own and are kept or dropped with their sessions.
session string no Only what belongs to this session: its key (`<stream>/<session>`) or its bare id.
runtime string no Only what belongs to this runtime: `<node>-<runtime>`, or a node id for every runtime of that machine.
kind MeshKind | null no Only this collection; the others are answered empty.
limit integer (uint) no At most this many of each collection: the newest by Lamport stamp where it has one, kept in the fold's own order.
open boolean no Only what is still open: active sessions, live claims, review requests nobody has answered and handovers nobody has taken.
matching string no Only what mentions this text, ignoring case: a path, a branch, an issue, a pull request, a word of an intent or a subject.

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/mesh/state?view=full"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 MeshStateAnswer The state every linked runtime converges on: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
POST /api/v1/mesh/verify mesh.verify

Prove cooperation now

Run a live verification: cooperation is active, the repository identity resolves, the endpoints are reachable beyond loopback, the heartbeat beats, the journal holds no stuck gap; then one sync round with every peer this runtime dials, timed, and whether both sides now hold the same high-water marks. Each failed check names its impact and remedy. Changes nothing but the journal's replication.

  • command
  • experimental
  • benchmark waived: external_dependency
  • cache disabled
  • MCP tool majordomus_mesh_verify

Request body — Empty, JSON

example default

{}

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s -X POST "http://127.0.0.1:8741/api/v1/mesh/verify" -H "content-type: application/json" -d '{}'

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 MeshVerifyReport Prove cooperation now: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
422 ErrorBody refused: The command was understood and turned down for the reason the message gives.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.

Schemas

The components only this tag's operations use. Each row is the type's own field, its doc comment the description; a type another page owns links there.

AdmitCommand

object
One command that admits a rule: the rule's label, its argument vector exactly as apply executes it, and the same quoted as one line a person pastes into a root shell. The two are projections of one vector, so what the report shows is what runs.

use std::net::Ipv4Addr;
use majordomus_cli::mesh::firewall::{plan, render, AdmitCommand, Backend};
use majordomus_cli::mesh::MeshConfig;

let config: MeshConfig = serde_json::from_value(serde_json::json!({
    "schema": "mesh/v1", "kind": "mesh-declaration", "id": "doc", "enabled": true,
})).unwrap();
let p = plan(Some(&config), &[Ipv4Addr::new(10, 0, 0, 2)], None);
let commands: Vec<AdmitCommand> = render(Backend::Ufw, &p, None);
assert_eq!(commands[0].argv[0], "ufw");
assert_eq!(commands[0].rule, "udp/7741 to 239.255.77.77 from any");
assert_eq!(commands[0].line, "ufw allow in proto udp to 239.255.77.77 port 7741 comment 'majordomus mesh: multicast discovery'");
propertytyperequireddescription
rule string yes The rule it admits.
argv array of string yes The program and its arguments, as executed.
line string yes The same, quoted for a shell.

Used by FirewallReport

Backend

"ufw" | "nftables" | "application_firewall" | "none"
The firewall front this host runs, as far as the executable can tell without privilege: decided by detect from the platform and the path, and the thing render, observe and apply are specific to.

use majordomus_cli::mesh::firewall::Backend;

assert_eq!(serde_json::to_value(Backend::ApplicationFirewall).unwrap(), "application_firewall");
assert_eq!(Backend::None.as_str(), "none");
  • ufwUncomplicated Firewall, the Debian and Ubuntu front: ufw is on the path.
  • nftablesnftables without ufw in front: nft is on the path.
  • application_firewallmacOS: the application firewall (socketfilterfw), which admits executables, not ports.
  • noneNo firewall front this executable knows; nothing is observed and nothing is applied.

Used by FirewallReport

ClaimInput

object
The input of mesh.claim.
propertytyperequireddescription
session string | null no The claiming session; the calling MCP session's own when omitted. Opened when new.
client string | null no The client, for a session this opens; cli when omitted.
scope array of string yes Repository-relative paths.
intent string | null no What the claim is for.
mode ClaimMode | null → ClaimMode no exclusive (the default) or advisory.
issue string | null no The issue the claim is for.
task string | null no The task, for a session this opens.

Used by mesh.claim

ClaimMode

"exclusive" | "advisory"
Whether a claim excludes others or only tells them. Both are useful and they answer different questions: an exclusive claim is a worker saying "do not edit this while I am", an advisory one is saying "I am here". Exclusive is the default because a claim taken without a thought about the mode is a claim somebody expects to be respected.

use majordomus_cli::mesh::journal::ClaimMode;

assert_eq!(ClaimMode::default(), ClaimMode::Exclusive);
assert_eq!(serde_json::to_value(ClaimMode::Advisory).unwrap(), serde_json::json!("advisory"));
  • exclusiveExclusive: a second exclusive claim meeting this scope is refused while this one lives, on every runtime that knows of it.
  • advisoryAdvisory: an announcement, like the peer board's. Overlaps are reported, never refused.

Used by ClaimInput ClaimView MeshClaimBrief

ClaimOverlap

object
Two live claims of different sessions whose scopes meet, at least one advisory: reported, never refused.

An overlap is information, not a verdict — it is how two workers find out they are in the same place without either being stopped. The meeting paths are listed pairwise rather than summarized, because the useful question is which path of mine meets which path of theirs.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{ClaimMode, EventBody, Journal, StreamLiveness};
use majordomus_cli::mesh::state::{fold, ClaimOverlap};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
for (claim, session, mode) in [
    ("c1", "s1", ClaimMode::Exclusive),
    ("c2", "s2", ClaimMode::Advisory),
] {
    j.append_own(EventBody::ClaimAcquired { claim: claim.into(), session: session.into(),
        scope: vec!["apps".into()], intent: None, mode, issue: None }).unwrap();
}

let state = fold(&j.events(), &|_| StreamLiveness::Own);
let overlap: &ClaimOverlap = &state.overlaps[0];
assert_ne!(overlap.first, overlap.second, "two claims, two sessions");
assert_eq!(overlap.paths, [("apps".to_string(), "apps".to_string())]);
propertytyperequireddescription
first string yes The earlier claim's key.
second string yes The later claim's key.
paths array of array of any yes The paths that meet, as first ↔ second pairs.

Used by CooperationState

ClaimState

one of 4 variants
A claim's standing, and — where it ended or lost — what ended it. The two variants that carry a string exist because the bare verdict is not actionable: a worker told its claim expired needs to know whether its session closed or its runtime stopped beating, and one told its claim is conflicted needs the key of the claim that beat it, which is the same key on every runtime.

use majordomus_cli::mesh::state::ClaimState;

assert!(ClaimState::Held.is_live());
assert!(ClaimState::Conflicted("s/c1".into()).is_live(), "still held, and not the winner");
assert!(!ClaimState::Released.is_live());

// the detail travels with the verdict, so a reader is never left asking who won
let value = serde_json::to_value(ClaimState::Conflicted("s/c1".into())).unwrap();
assert_eq!(value["state"], "conflicted");
assert_eq!(value["detail"], "s/c1");
variant Live; for an exclusive claim, the winner of its scope.
state"held"yes
variant Released by its holder.
state"released"yes
variant Ended without a release: the holder's session closed or its runtime expired.
state"expired"yes
detailstringyes
variant Live, exclusive, and beaten to its scope by the claim whose key this names.
state"conflicted"yes
detailstringyes

Used by ClaimView

ClaimView

object
One claim anywhere in the mesh: who took what, for what, and where it stands. The acquisition's Lamport stamp and sequence are carried rather than derived on demand, because they are what exclusivity is decided by — every runtime ranks two competing claims by the same triple and so names the same winner, with no message passing and no "last packet wins".

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{ClaimMode, EventBody, Journal, StreamLiveness};
use majordomus_cli::mesh::state::{fold, ClaimState, ClaimView};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::ClaimAcquired { claim: "c1".into(), session: "s1".into(),
    scope: vec!["apps/majordomus-cli".into()], intent: Some("document the mesh".into()),
    mode: ClaimMode::Exclusive, issue: Some("#184".into()) }).unwrap();

let state = fold(&j.events(), &|_| StreamLiveness::Own);
let claim: &ClaimView = &state.claims[0];
assert_eq!(claim.scope, ["apps/majordomus-cli"]);
assert_eq!(claim.state, ClaimState::Held);
assert!(claim.acquired_lamport > 0, "the order exclusivity is decided in");
propertytyperequireddescription
key string yes <stream>/<claim>.
stream StreamId → StreamId yes The writing stream.
runtime string yes <node>-<runtime>.
session string yes The holding session's key.
scope array of string yes The claimed paths.
intent string | null no What it is for.
mode ClaimMode → ClaimMode yes Exclusive or advisory.
issue string | null no The issue it is for.
acquired_lamport integer (uint64) yes The Lamport stamp of the acquisition.
acquired_seq integer (uint64) yes The sequence of the acquisition within its stream: with the stamp and the stream, the total order in which exclusivity is decided.
state ClaimState → ClaimState yes Its standing.

Used by CooperationState SessionNode

ConsumeAnswer

object
The answer of mesh.handover.consume.
propertytyperequireddescription
handover HandoverView → HandoverView yes The handover, as the journal holds it.
written Written → Written yes The consumption event.
path string | null no The local record written, relative to the repository root, when materialized.

Used by mesh.handover.consume

CooperationCounters

object
The link and replication counters since start: what this runtime did, not what it holds. They are monotonic and never reset, so two readings a minute apart are a rate — which is the only way to tell a mesh that is working from one that is retrying, since both show the same peers in the same states.

The counts are split by direction throughout (handshakes_out against handshakes_in, refused_in against refused_out) because a runtime that refuses everyone and one that everyone refuses need opposite remedies.

#### use std::collections::BTreeMap;
#### use std::sync::{Arc, Mutex, Weak};
#### use majordomus_cli::mesh::config::{CooperationConfig, TrustConfig};
#### use majordomus_cli::mesh::cooperation::*;
#### use majordomus_cli::mesh::identity::NodeIdentity;
#### use majordomus_cli::mesh::link::{LinkReply, LinkTransport, Signed, HELLO_PATH};
#### use majordomus_cli::mesh::registry::MeshRegistry;
#### use majordomus_cli::mesh::repository::{of_root_commits, runtime_id};
#### use majordomus_cli::mesh::trust::TrustPolicy;
#### #[derive(Default)] struct Net(Mutex<BTreeMap<String, Weak<Cooperation>>>);
#### impl LinkTransport for Net {
####   fn post(&self, e: &str, p: &str, m: &Signed) -> Result<LinkReply, String> {
####     let peer = (self.0.lock().unwrap().get(e).and_then(Weak::upgrade))
####         .ok_or_else(|| format!("{e}: nobody there"))?;
####     Ok(if p == HELLO_PATH { peer.accept_hello(m) } else { peer.accept_sync(m) }) } }
#### let net = Arc::new(Net::default());
#### let runtime = |endpoint: &str| { let c = Cooperation::new(CooperationSetup {
####     identity: Arc::new(NodeIdentity::ephemeral().unwrap()), runtime: runtime_id(endpoint),
####     repository: of_root_commits(&["root".into()]), endpoints: vec![endpoint.into()],
####     version: "doc".into(), config: CooperationConfig::default(),
####     trust: TrustConfig { policy: TrustPolicy::Tofu, allow: vec![] }, journal_path: None,
####     registry: Arc::new(MeshRegistry::new()),
####     transport: Arc::clone(&net) as Arc<dyn LinkTransport>, board: None,
####     checkout: CheckoutFacts::default() }).unwrap();
####   net.0.lock().unwrap().insert(endpoint.into(), Arc::downgrade(&c)); c };
#### let (a, b) = (runtime("a:1"), runtime("b:1"));
let key = a.dial(&["b:1".into()]).unwrap();
a.sync_with(&key).unwrap();

let dialer: CooperationCounters = a.status().counters;
let answerer = b.status().counters;
assert_eq!((dialer.handshakes_out, dialer.syncs_out), (1, 1));
assert_eq!((answerer.handshakes_in, answerer.syncs_in), (1, 1), "the other side of it");
assert_eq!(answerer.handshakes_out, 0, "B never dialed anybody");
propertytyperequireddescription
handshakes_out integer (uint64) yes Handshakes this runtime completed as the dialer.
handshakes_in integer (uint64) yes Handshakes this runtime accepted.
dial_failures integer (uint64) yes Dials that reached no endpoint.
syncs_out integer (uint64) yes Sync rounds this runtime completed as the dialer.
syncs_failed integer (uint64) yes Sync rounds that failed in transport.
syncs_in integer (uint64) yes Sync rounds this runtime answered.
events_sent integer (uint64) yes Events pushed in sync requests.
events_served integer (uint64) yes Events served in sync answers.
reconnects integer (uint64) yes Re-handshakes after an expiry or a dropped link.
restarts integer (uint64) yes Peer restarts observed.
peers_expired integer (uint64) yes Links expired.
claims_refused integer (uint64) yes Claims refused for a conflict.
threads integer (uint64) yes Cooperation threads alive now.
refused_in map of integer (uint64) yes Hellos and syncs this runtime refused, by code.
refused_out map of integer (uint64) yes Refusals this runtime received or applied to a welcome, by code.

Used by CooperationStatus

CooperationState

object
The whole folded state, every collection in key order, and its digest: two runtimes that agree print the same digest.

The digest is the point. Convergence is otherwise a claim nobody can check from one machine; with it, two operators compare one short string and know whether their runtimes are looking at the same world. opaque_events is the other half of that honesty: events of kinds this executable does not interpret are counted and relayed rather than dropped, so a newer peer's traffic is visible instead of silently missing.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal, SessionInfo, StreamLiveness};
use majordomus_cli::mesh::state::{fold, CooperationState};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionOpened { info: SessionInfo::named("s1", "cli") }).unwrap();

let events = j.events();
let state: CooperationState = fold(&events, &|_| StreamLiveness::Own);
assert_eq!(state.digest.len(), 32);

// the same events in another order, and twice over, are the same state
let mut shuffled = events.clone();
shuffled.reverse();
shuffled.extend(events);
assert_eq!(fold(&shuffled, &|_| StreamLiveness::Own).digest, state.digest);
propertytyperequireddescription
sessions array of SessionView → SessionView yes Every session.
claims array of ClaimView → ClaimView yes Every claim.
overlaps array of ClaimOverlap → ClaimOverlap yes Advisory overlaps between live claims.
handovers array of HandoverView → HandoverView yes Every handover.
reviews array of ReviewView → ReviewView yes Every review request.
opaque_events integer (uint) yes Events of kinds this executable does not interpret.
digest string yes 32 hex of the SHA-256 of the canonical JSON of everything above.

Used by MeshStateAnswer

CooperationStatus

object
The whole cooperation runtime, as every surface reports it: the command line, the HTTP surface and MCP all render this one value, so that a person and an agent asking the same question of the same runtime are never told different things.

It answers in one reading the three questions a mesh raises — is this runtime cooperating at all, what does it speak, and who is it linked to — and it stays answerable when the answer is no: an inactive runtime still reports its protocol range and the reason, rather than an absence the caller has to interpret.

#### use std::sync::Arc;
#### use majordomus_cli::mesh::config::{CooperationConfig, TrustConfig};
#### use majordomus_cli::mesh::cooperation::{CheckoutFacts, Cooperation, CooperationSetup};
#### use majordomus_cli::mesh::identity::NodeIdentity;
#### use majordomus_cli::mesh::link::HttpTransport;
#### use majordomus_cli::mesh::registry::MeshRegistry;
#### use majordomus_cli::mesh::repository::of_root_commits;
#### let cooperation = Cooperation::new(CooperationSetup {
####     identity: Arc::new(NodeIdentity::ephemeral().unwrap()), runtime: "0000000000000001".into(),
####     repository: of_root_commits(&["root".into()]), endpoints: vec!["127.0.0.1:9".into()],
####     version: "doc".into(), config: CooperationConfig::default(), trust: TrustConfig::default(),
####     journal_path: None, registry: Arc::new(MeshRegistry::new()),
####     transport: Arc::new(HttpTransport), board: None, checkout: CheckoutFacts::default(),
#### }).unwrap();
use majordomus_cli::mesh::cooperation::CooperationStatus;

let running: CooperationStatus = cooperation.status();
assert_eq!(running.runtime.as_deref(), Some(cooperation.runtime_key()));
assert!(running.peers.is_empty(), "nothing is dialed until something dials");

// and where there is no runtime at all, the same shape says why
let none = CooperationStatus::inactive("the mesh declaration is disabled");
assert_eq!(none.protocol, running.protocol, "still answerable about what it speaks");
propertytyperequireddescription
active boolean yes Whether cooperation runs in this process.
reason string | null no Why not, when it does not.
runtime string | null no This runtime, <node>-<runtime>.
stream StreamId | null → StreamId no This runtime's current stream.
repository MeshRepositoryIdentity | null → MeshRepositoryIdentity no The repository identity links are matched on.
protocol array of any yes The link protocol range spoken.
features array of string yes The link features carried.
heartbeat_seconds integer (uint64) yes Seconds between heartbeats.
expiry_seconds integer (uint64) yes Seconds of silence before expiry.
endpoints array of string yes Where this runtime answers.
seeds array of string yes The declared seeds.
peers array of PeerView → PeerView yes Every linked peer, by runtime key.
refused array of RefusedView → RefusedView yes Candidates refused, by endpoint.
counters CooperationCounters → CooperationCounters yes Counters.
journal JournalTallies → JournalTallies yes The journal's tallies.
started_at string | null no When cooperation started, RFC 3339.

Used by mesh.cooperation

DoctorCheck

object
One check's verdict.
propertytyperequireddescription
check string yes What was checked.
ok boolean yes Whether it holds.
detail string yes The evidence: a path, an address, an error.
impact string | null no What a failure or a limitation means for the mesh.
remediation string | null no What to do about it.

Used by MeshDoctorReport

Durability

"synced" | "memory" | "not_durable" | "unknown"
Whether an event this runtime wrote reached this machine's disk: what an append acknowledges, and the only thing a caller may say about it.

use majordomus_cli::mesh::journal::Durability;
assert_eq!(serde_json::to_value(Durability::NotDurable).unwrap(), "not_durable");
assert_eq!(Durability::default(), Durability::Unknown, "an answer that did not say");
assert!(Durability::Synced.is_on_disk() && !Durability::NotDurable.is_on_disk());
  • syncedAppended to the journal file and synced (fsync of its data) before the append returned.
  • memoryThe journal has no file: it lives in memory and its peers, by configuration.
  • not_durableThe write to the journal file failed. The event is held in memory and replicated to peers, and is lost to this machine when the runtime restarts unless a peer still holds it. It is not acknowledged as durable.
  • unknownNot reported: an answer from an executable that predates this field.

Used by Written

Envelope

object
The wire envelope: the advertisement plus its signature.
propertytyperequireddescription
v integer (uint32) yes Protocol version.
pk string yes The Ed25519 public key, hex. The node id is derived from it, never carried: a field that could disagree with the key is a field somebody will forge.
inst InstanceId → InstanceId yes This run of the node.
name string no A human name for the node. Presentation only.
seq integer (uint64) yes Monotonic per instance: a replayed datagram carries a counter the reader has already seen and is dropped.
ts integer (uint64) yes Seconds since the Unix epoch at signing time.
ep array of string no Where this node answers HTTP, as host:port authorities, at most MAX_ENDPOINTS. Advisory: an endpoint is where to try, identity is the key.
caps array of string no The transports the node serves: http, mcp, ws. Sanitized capability awareness, never an authorization.
repos array of string no The repositories this node serves, as mesh repository ids (32 hex, see super::repository): derived from the repository's root commits or its declared identity, never from a path, so that one repository cloned on two machines is recognised as one. A digest discloses nothing; a reader that serves the same repository recognises it, anyone else learns only "some repo".
ver string no The executable version, informational.
rt string no The runtime slot (16 hex): which server of this node — one per checkout. Empty in version 1. Omitted from the signed bytes when empty, so a version-1 signature verifies unchanged.
sig string yes Ed25519 over Advertisement::signing_bytes, hex.

Used by RegisterAnswer

EventList

object
The answer of mesh.events.
propertytyperequireddescription
active boolean yes Whether cooperation runs.
count integer (uint) yes How many events this answer carries.
lamport integer (uint64) yes The after of the next page: the stamp of the last event this page carries, or the after that was asked when it carries none. A page never ends inside a run of equal stamps, so resuming after it misses nothing (I2140).
events array of MeshEvent → MeshEvent yes The events, in Lamport order.

Used by mesh.events

FirewallObservation

object
The firewall's own word, judged against the plan: the state, the evidence behind it, and the labels of the rules not admitted when the state is missing.

use majordomus_cli::mesh::firewall::{judge_ufw, plan, FirewallObservation, FirewallObservationState};

let o: FirewallObservation = judge_ufw("Status: inactive\n", &plan(None, &[], None));
assert_eq!(o.state, FirewallObservationState::Inactive);
assert!(o.missing.is_empty() && o.detail.contains("inactive"));
propertytyperequireddescription
state FirewallObservationState → FirewallObservationState yes The verdict.
detail string yes The evidence: the status line, the rules found, or the refusal.
missing array of string no The rules not admitted, by label, when state is missing.

Used by FirewallReport

FirewallObservationState

"no_backend" | "inactive" | "present" | "missing" | "unobservable"
What the host's firewall says about the plan, as one word. Only missing is a failure on its own; unobservable is an absence that the kernel log may still turn into one.

use majordomus_cli::mesh::firewall::FirewallObservationState;

assert_eq!(serde_json::to_value(FirewallObservationState::Unobservable).unwrap(), "unobservable");
  • no_backendNo backend to ask.
  • inactiveThe firewall is off: nothing filters, nothing is needed.
  • presentEvery rule of the plan is admitted.
  • missingAt least one rule of the plan is not admitted.
  • unobservableThe firewall could not be asked from this process — it needs root.

Used by FirewallObservation

FirewallPlan

object
What the declaration implies this host must admit: the rules, the networks they admit from, the hub ports this machine is listed for, and the addresses the plan saw. The answer of plan; an empty rules is "nothing is needed", which is the answer for no declaration and for a disabled one.

use majordomus_cli::mesh::firewall::{plan, FirewallPlan};

let nothing: FirewallPlan = plan(None, &[], None);
assert!(nothing.rules.is_empty() && nothing.hub_ports.is_empty() && nothing.networks.is_empty());
propertytyperequireddescription
local_addresses array of string yes This machine's addresses, as the plan saw them.
networks array of string yes The private networks peers come from: the enclosing ranges of every address the declaration names, or of this machine's own when it names none.
hub_ports array of integer (uint16) yes The ports of the declared hubs this machine is.
rules array of FirewallRule → FirewallRule yes The admissions, in a fixed order.

Used by FirewallReport

FirewallReport

object
The whole report: the plan, the backend, the commands, the firewall's word and the kernel's, with one verdict sentence and one ok. What mesh.firewall answers and what the doctor's firewall check reads.

use majordomus_cli::mesh::firewall::{report, FirewallReport, BLOCK_WINDOW_SECONDS};

let r: FirewallReport = report(None, &[], None);
assert!(r.ok && r.commands.is_empty());
assert_eq!(r.window_seconds, BLOCK_WINDOW_SECONDS);
assert_eq!(r.platform, std::env::consts::OS);
propertytyperequireddescription
ok boolean yes Whether the host admits the mesh as far as it can be observed: nothing missing, and nothing logged as dropped.
platform string yes The platform (linux, macos, …).
backend Backend → Backend yes The firewall front found.
executable string | null no The executable the server runs as, which is what the macOS firewall admits.
plan FirewallPlan → FirewallPlan yes What the declaration implies this host must admit.
commands array of AdmitCommand → AdmitCommand yes The commands that admit it on this backend; what apply runs.
observation FirewallObservation → FirewallObservation yes The firewall's own word about the plan.
window_seconds integer (uint64) yes The window blocked_recently covers, in seconds.
blocked_recently object | null no Drops the kernel logged toward the plan's ports within the window, per port; absent when the log could not be read.
verdict string yes The sentence a person reads.

Used by mesh.firewall

FirewallRule

object
One inbound admission the mesh needs on this host: a protocol and a port, the group it is addressed to when it is multicast, the networks it may come from, and the sentence that says why. A rule is derived, never written by hand; FirewallRule::label is how a verdict names it.

use majordomus_cli::mesh::firewall::{FirewallRule, Protocol, Role};

let rule = FirewallRule {
    role: Role::Hub,
    protocol: Protocol::Tcp,
    port: 8791,
    destination: None,
    sources: vec!["192.168.7.0/24".into()],
    reason: "this machine is a declared rendezvous hub".into(),
};
assert_eq!(rule.label(), "tcp/8791 from 192.168.7.0/24");
propertytyperequireddescription
role Role → Role yes Why.
protocol Protocol → Protocol yes The transport protocol.
port integer (uint16) yes The destination port.
destination string | null no The destination address when it is a multicast group; None for this host's own.
sources array of string yes The networks the traffic may come from, as CIDR; empty admits any source, which is only ever the case for the multicast group, whose TTL keeps it on the segment.
reason string yes The sentence a person reads beside the rule.

Used by FirewallPlan

HandoverBody

object
A handover as the mesh carries it: the record's facts and its body, bounded.

What travels is the writing, plus the few facts that let a receiver file it — the task, the issue, the branch, the commit. No path of the author's disk is among them, because the checkout that reads this is somewhere else and a path from another machine is at best noise. Identity is the digest of the body, so a handover published twice by a retry or by two runtimes is one handover everywhere.

use majordomus_cli::mesh::journal::HandoverBody;

let body = "# Objective\nship the mesh\n".to_string();
let handover = HandoverBody {
    id: HandoverBody::digest_of(&body),
    task: Some("t-1".into()),
    issue: Some("#184".into()),
    milestone: None,
    branch: Some("feature/mesh-cooperation".into()),
    head: None,
    created_at: None,
    name: None,
    body,
};
assert_eq!(handover.id, HandoverBody::digest_of(&handover.body));

let wire = serde_json::to_value(&handover).unwrap();
assert!(wire.get("milestone").is_none(), "what it does not say is omitted");
propertytyperequireddescription
id string yes The content digest (32 hex): the same handover published twice is one handover.
task string | null no The task the handover closes or continues.
issue string | null no The issue it belongs to.
milestone string | null no The milestone it belongs to.
branch string | null no The branch it was written on.
head string | null no The commit it was written at.
created_at string | null no When it was written, RFC 3339, as its author's clock said.
name string | null no The record's file name at its origin, informational.
body string yes The Markdown body (# Objective, # Current State, # Next Action, ...).

Used by HandoverView

HandoverConsumeInput

object
The input of mesh.handover.consume.
propertytyperequireddescription
handover string yes The handover's id (its content digest).
session string | null no The consuming session; the calling MCP session's own when omitted.
materialize boolean | null no Whether to write it as a local handover record majordomus handover --resolve finds (default true).

Used by mesh.handover.consume

HandoverPublishInput

object
The input of mesh.handover.publish.
propertytyperequireddescription
path string | null no The record, relative to the repository root and inside .ai/local/state/handovers/; the newest record when omitted. Nothing outside that directory is ever read by this command.
issue string | null no The issue the handover belongs to.
milestone string | null no The milestone it belongs to.

Used by mesh.handover.publish

HandoverView

object
A handover anywhere in the mesh, with everyone who has taken it. The id is the digest of the body, so the same handover published twice — by a retry, or by two runtimes that both saw it — is one handover here; and consumed_by is what keeps two workers from silently picking up the same continuity, since the list is folded from events every runtime holds.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, HandoverBody, Journal, StreamLiveness};
use majordomus_cli::mesh::state::{fold, HandoverView};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
let body = "# Objective\nship\n".to_string();
let handover = HandoverBody { id: HandoverBody::digest_of(&body), task: None, issue: None,
    milestone: None, branch: Some("feature/x".into()), head: None, created_at: None,
    name: None, body };
let id = handover.id.clone();
j.append_own(EventBody::HandoverPublished { handover }).unwrap();
j.append_own(EventBody::HandoverConsumed { handover: id.clone(), session: "s1".into() })
    .unwrap();

let state = fold(&j.events(), &|_| StreamLiveness::Own);
let view: &HandoverView = &state.handovers[0];
assert_eq!(view.id, id, "identified by what it says, not by who said it");
assert_eq!(view.consumed_by.len(), 1, "somebody has picked this one up");
propertytyperequireddescription
id string yes The content digest.
stream StreamId → StreamId yes The stream that first published it.
runtime string yes <node>-<runtime> of the publisher.
published_lamport integer (uint64) yes The Lamport stamp of the first publication.
handover HandoverBody → HandoverBody yes The record's facts and body.
consumed_by array of string yes Every session that consumed it, by key, in Lamport order.

Used by ConsumeAnswer CooperationState

InstanceId

string
One run of a node: 16 hex characters of OS entropy, new at every process start. A restart keeps the node id and changes this, which is how a reader tells "the same node came back" from "the same process is still there".

Used by Envelope NodeRecord PublicIdentity

JournalTallies

object
The journal's counters since start: how much it holds, how much it has written, received, absorbed, refused, held back and compacted away. They are what makes a replication problem diagnosable from one machine — a runtime whose received never rises is not linked, one whose rejected rises is being sent something it will not take, and one whose pending stays high is missing an event ahead of what it has.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal, JournalTallies};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
let before: JournalTallies = j.tallies();
assert_eq!((before.written, before.events), (0, 0));

j.append_own(EventBody::SessionClosed { session: "s1".into() }).unwrap();
let after = j.tallies();
assert_eq!((after.written, after.events, after.streams), (1, 1, 1));
assert!(after.lamport > before.lamport);
propertytyperequireddescription
streams integer (uint) yes Streams tracked.
events integer (uint) yes Events held.
written integer (uint64) yes Events written by this runtime since start.
received integer (uint64) yes Events received and stored.
duplicates integer (uint64) yes Duplicate deliveries absorbed.
rejected integer (uint64) yes Events refused.
pending integer (uint) yes Events held for a gap right now.
compacted integer (uint64) yes Events dropped by compaction since start.
opaque integer (uint) yes Events of a kind this executable does not interpret, held and relayed.
lamport integer (uint64) yes The highest Lamport stamp seen.
write_failures integer (uint64) no Appends to the journal file that failed since start: the events were held in memory and replicated, and are not on this machine's disk. Nonzero fails mesh doctor.
unreadable integer (uint64) no Lines of the journal file the last reload could not read: torn, corrupt, or written by something that is not this journal. Counted, never silently dropped.
unread_bytes integer (uint64) no Bytes of the journal file the last reload left unread because they lie beyond MAX_RELOAD_BYTES: on disk and kept by compaction, and not in memory. Nonzero fails mesh doctor.

Used by CooperationStatus

LinkRefusal

object
A refusal: the code, the reason in words, and the protocol range of the refuser so a version mismatch is diagnosable from either end.

Carrying the refuser's protocol range in every refusal, not only in the one about protocols, is what makes a version problem visible from the side that has the log: the dialer sees what the answerer speaks without anyone having to read the other machine.

use majordomus_cli::mesh::link::{LinkRefusal, RefusalCode, LINK_PROTOCOL_MAX};

let refusal = LinkRefusal::new(RefusalCode::Untrusted, "the key is not in the allowlist");
assert_eq!(refusal.code, RefusalCode::Untrusted);
assert_eq!(refusal.proto_max, LINK_PROTOCOL_MAX);
assert_eq!(refusal.to_string(), "untrusted: the key is not in the allowlist");
propertytyperequireddescription
code RefusalCode → RefusalCode yes The rule that refused.
detail string yes Why, in words.
proto_min integer (uint32) yes The refuser's oldest protocol.
proto_max integer (uint32) yes The refuser's newest protocol.

Used by LinkReply RefusedView

LinkReply

object
What a hello or a sync answers over the wire: a signed message or a refusal. Both are successful replies — the transport carries them the same way and neither is an error — because a refusal is a decision this protocol wants recorded and shown, and an answer that arrived as an HTTP failure would be indistinguishable from a peer that is down.

use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::link::{sign, Domain, LinkReply, RefusalCode};

let node = NodeIdentity::ephemeral().unwrap();
let accepted = LinkReply::accepted(sign(&node, Domain::Welcome, serde_json::json!({})));
assert!(accepted.signed.is_some() && accepted.refusal.is_none());

let refused = LinkReply::refused(RefusalCode::SelfLink, "this runtime dialled itself");
assert!(refused.signed.is_none());
assert_eq!(refused.refusal.unwrap().code, RefusalCode::SelfLink);
propertytyperequireddescription
signed Signed | null → Signed no The signed welcome or sync answer, when accepted.
refusal LinkRefusal | null → LinkRefusal no The refusal, when not.

Used by mesh.link.hello mesh.link.sync

LinkState

"connecting" | "connected" | "degraded" | "unreachable" | "expired"
A link's standing, from this runtime's side. It is a statement about a connection and never about the peer: a runtime reachable only through a third party is Expired here while its streams beat on everywhere, which is why claims follow streams and not this.

The variants are ordered by how far the link has fallen, so the worst link in a table is the maximum and a reader never has to rank them by hand.

use majordomus_cli::mesh::cooperation::LinkState;

let table = [LinkState::Connected, LinkState::Unreachable, LinkState::Degraded];
assert_eq!(table.iter().max(), Some(&LinkState::Unreachable), "the worst link shows");
assert_eq!(serde_json::to_value(LinkState::Connected).unwrap(), "connected");
  • connectingHandshaking, or never yet exchanged.
  • connectedAn exchange within two heartbeats.
  • degradedRounds are failing; the last exchange is under half the expiry old.
  • unreachableThe last exchange is over half the expiry old; the link is about to expire.
  • expiredNo exchange within the expiry: the link is dropped and will be re-dialed.

Used by PeerVerdict PeerView

MachineNode

object
One machine in the peer tree.
propertytyperequireddescription
node string yes The node id.
name string yes Its display name, when a card or this node said.
local boolean yes Whether it is this machine.
runtimes array of RuntimeNode → RuntimeNode yes Its runtimes.

Used by PeerTree

MeshClaimBrief

object
One live claim, told briefly: whose it is, what it is for and how many paths it holds, with its intent cut to SUMMARY_TEXT characters — enough to know whom to ask.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{ClaimMode, EventBody, Journal, SessionInfo, StreamLiveness};
use majordomus_cli::mesh::state::fold;

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionOpened { info: SessionInfo::named("s1", "claude-code") })
    .unwrap();
for (claim, path) in [("c1", "apps/a"), ("c2", "apps/b")] {
    j.append_own(EventBody::ClaimAcquired { claim: claim.into(), session: "s1".into(),
        scope: vec![path.into()], intent: Some("work".into()), mode: ClaimMode::Exclusive,
        issue: Some("#1".into()) }).unwrap();
}
j.append_own(EventBody::ClaimReleased { claim: "c1".into() }).unwrap();
let state = fold(&j.events(), &|_| StreamLiveness::Own);
use majordomus_cli::mesh::page::{summarize, ClaimBrief};

let summary = summarize(&state, &[]);
let newest: &ClaimBrief = summary.runtimes[0].newest_claim.as_ref().unwrap();
assert_eq!((newest.issue.as_deref(), newest.paths), (Some("#1"), 1));
propertytyperequireddescription
key string yes <stream>/<claim>.
session string yes The holding session's key.
issue string | null no The issue it is for.
intent string | null no What it is for, cut to SUMMARY_TEXT characters.
paths integer (uint) yes How many paths it claims.
mode ClaimMode → ClaimMode yes Exclusive or advisory.
acquired_lamport integer (uint64) yes The Lamport stamp of the acquisition.

Used by MeshRuntimeSummary RuntimeNode

MeshClaimCounts

object
Claims by standing: held, conflicted, released and expired. Held and conflicted are live; the other two are history, counted rather than listed.

use majordomus_cli::mesh::page::ClaimCounts;

let counts = ClaimCounts { held: 2, released: 5, ..ClaimCounts::default() };
assert_eq!(serde_json::to_value(&counts).unwrap()["released"], 5);
propertytyperequireddescription
held integer (uint) yes Live and, for an exclusive claim, the winner of its scope.
conflicted integer (uint) yes Live, exclusive, and beaten to its scope.
released integer (uint) yes Released by the holder.
expired integer (uint) yes Ended without a release.

Used by MeshRuntimeSummary MeshSummary

MeshClaimFilter

"all" | "live"
Which claims a full answer keeps: all of them, the ended ones included, or only those still live — the question an executor asks before it takes a lease.

use majordomus_cli::mesh::page::ClaimFilter;

assert_eq!(ClaimFilter::default(), ClaimFilter::All);
assert_eq!(serde_json::from_str::<ClaimFilter>(r#""live""#).unwrap(), ClaimFilter::Live);
  • allEvery claim, ended ones included.
  • liveOnly claims that are still live: held, or conflicted and still held.

Used by mesh.peers mesh.state

MeshDoctorReport

object
The whole self-check.
propertytyperequireddescription
ok boolean yes Whether every check holds.
checks array of DoctorCheck → DoctorCheck yes The checks, in the order they ran.

Used by mesh.doctor

MeshEvent

object
One event as the wire and the store carry it. The body is kept as JSON so that an event of an unknown kind survives storage and relay byte-for-byte; MeshEvent::body interprets it.

Every field above the signature is covered by it, which is what makes a relay harmless: a runtime that forwards an event cannot alter its stream, its sequence, its repository or its body without the next reader noticing, and so vouches for nothing. The wall clock is the one field nothing is decided by — freshness is measured with beats and monotonic clocks, never by comparing two machines' idea of the time.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal, MeshEvent};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionClosed { session: "s1".into() }).unwrap();

let event: &MeshEvent = &j.events()[0];
assert_eq!(event.seq, 1, "dense from 1 within its stream");
assert_eq!(event.kind(), "session_closed");
assert_eq!(event.id(), format!("{}/1", event.stream));

// a relayed byte changed anywhere under the signature is a different event
let mut tampered = event.clone();
tampered.repo = "another-repository".into();
assert_ne!(tampered.signing_bytes(), event.signing_bytes());
propertytyperequireddescription
v integer (uint32) yes The event format version.
stream StreamId → StreamId yes The stream that wrote it.
seq integer (uint64) yes Dense from 1 within the stream.
lamport integer (uint64) yes The Lamport stamp: above every stamp its writer had seen.
at integer (uint64) yes The writer's wall clock, seconds since the Unix epoch. Informational only: nothing is decided by comparing it with another machine's clock.
repo string yes The mesh repository id the event belongs to.
pk string yes The writer's Ed25519 public key, hex; its digest is the stream's node.
body any yes The body, kind tagged.
sig string yes Ed25519 over the canonical bytes of everything above, hex.

Used by EventList

MeshHandoverBrief

object
A handover nobody has taken, told briefly: the id mesh.handover.consume takes, who published it and what it continues. Its body stays in the full view.

use majordomus_cli::mesh::page::HandoverBrief;

let brief = HandoverBrief { id: "ab12".into(), runtime: "n-r".into(),
    branch: Some("feature/x".into()), issue: None, published_lamport: 7 };
assert_eq!(serde_json::to_value(&brief).unwrap()["branch"], "feature/x");
propertytyperequireddescription
id string yes The content digest: what mesh.handover.consume takes.
runtime string yes <node>-<runtime> of the publisher.
branch string | null no The branch it continues.
issue string | null no The issue it continues.
published_lamport integer (uint64) yes The Lamport stamp of the first publication.

Used by MeshSummary

MeshIdentityReport

object
The answer of mesh.identity: this machine's node identity, public half only.
propertytyperequireddescription
path string | null no Where the identity file lives (or would).
present boolean yes Whether the file exists.
identity PublicIdentity | null → PublicIdentity no The public identity, when the file exists and loads. The signing key is not here, not in any projection, and not in any log.
error string | null no Why the identity did not load, when it did not.

Used by mesh.identity

MeshKind

"sessions" | "claims" | "overlaps" | "handovers" | "reviews"
One collection of the folded state, as kind names it: a reader that wants only the reviews is not sent the claims.

use majordomus_cli::mesh::page::Kind;

assert_eq!(serde_json::from_str::<Kind>(r#""reviews""#).unwrap(), Kind::Reviews);
assert!(serde_json::from_str::<Kind>(r#""everything""#).is_err());
  • sessionsThe sessions.
  • claimsThe claims.
  • overlapsThe advisory overlaps.
  • handoversThe handovers.
  • reviewsThe review requests.

Used by mesh.peers mesh.state

MeshProviderState

"running" | "failed" | "stopped"
The provider's health, as every surface reports it.
  • runningThreads up, socket bound (where the provider has one).
  • failedThe provider could not start or died; ProviderStatus::detail says why. One provider failing never stops another — failure isolation is the contract.
  • stoppedStopped by the manager.

Used by ProviderStatus

MeshRepositoryBasis

"declared" | "root_commits"
What the repository identity was derived from, which a reader needs in order to judge it: an identity taken from the root commits is a fact about the history two clones share, while a declared one is a decision somebody made, and only the second can be changed by editing a file.

use majordomus_cli::mesh::repository::{declared, of_root_commits, MeshRepositoryBasis};

assert_eq!(declared("majordomus").unwrap().basis, MeshRepositoryBasis::Declared);
assert_eq!(
    of_root_commits(&["a".repeat(40)]).basis,
    MeshRepositoryBasis::RootCommits,
);
  • declaredcooperation.repository in the mesh declaration.
  • root_commitsThe repository's root commits.

Used by MeshRepositoryIdentity

MeshRepositoryIdentity

object
A repository's mesh identity, carried by every advertisement, link and event, together with the account of where it came from. The detail exists so that a person told two checkouts are not the same repository can see what each one was derived from instead of comparing two opaque digests.

use majordomus_cli::mesh::repository::MeshRepositoryIdentity;
use majordomus_cli::mesh::repository::of_root_commits;

let identity: MeshRepositoryIdentity = of_root_commits(&["a".repeat(40)]);
assert_eq!(identity.id.len(), 32, "32 hex characters, on every machine");
assert_eq!(identity.detail, "aaaaaaaaaaaa", "the evidence, short enough to read");
propertytyperequireddescription
id string yes 32 hex: what advertisements, links and events carry.
basis MeshRepositoryBasis → MeshRepositoryBasis yes What it was derived from.
detail string yes The evidence, human-readable: the declared value, or the root commits.

Used by CooperationStatus

MeshReviewBrief

object
An open review request, told briefly: its key, what is to be reviewed and whom it asks. Only requests nobody has answered are listed in the summary.

use majordomus_cli::mesh::page::ReviewBrief;

let brief = ReviewBrief { key: "s/r1".into(), subject: "feature/x".into(), issue: None,
    reviewer: None };
assert!(serde_json::to_value(&brief).unwrap().get("issue").is_none(), "absent, not null");
propertytyperequireddescription
key string yes <stream>/<review>.
subject string yes What is to be reviewed, cut to SUMMARY_TEXT characters.
issue string | null no The issue.
reviewer string | null no The runtime asked, when one was.

Used by MeshSummary

MeshRuntimeSummary

object
One runtime, counted: its liveness and beat, its sessions and claims by standing, and its newest live claim. The summary lists these, the living runtimes first.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{ClaimMode, EventBody, Journal, SessionInfo, StreamLiveness};
use majordomus_cli::mesh::state::fold;

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionOpened { info: SessionInfo::named("s1", "claude-code") })
    .unwrap();
for (claim, path) in [("c1", "apps/a"), ("c2", "apps/b")] {
    j.append_own(EventBody::ClaimAcquired { claim: claim.into(), session: "s1".into(),
        scope: vec![path.into()], intent: Some("work".into()), mode: ClaimMode::Exclusive,
        issue: Some("#1".into()) }).unwrap();
}
j.append_own(EventBody::ClaimReleased { claim: "c1".into() }).unwrap();
let state = fold(&j.events(), &|_| StreamLiveness::Own);
use majordomus_cli::mesh::page::{summarize, RuntimeSummary};

let summary = summarize(&state, &[]);
let runtime: &RuntimeSummary = &summary.runtimes[0];
assert_eq!((runtime.sessions.active, runtime.claims.held, runtime.claims.released), (1, 1, 1));
propertytyperequireddescription
runtime string yes <node>-<runtime>.
liveness StreamLiveness → StreamLiveness yes Own, live or expired.
last_beat_ms integer | null no Milliseconds since its beat last rose, on this runtime's clock.
sessions MeshSessionCounts → MeshSessionCounts yes Its sessions by standing.
claims MeshClaimCounts → MeshClaimCounts yes Its claims by standing.
newest_claim MeshClaimBrief | null → MeshClaimBrief no Its newest live claim.

Used by MeshSummary

MeshSessionCounts

object
Sessions by standing: how many are active, closed and expired. A count stays the same size however many sessions the mesh has seen, which is why the summary carries counts.

use majordomus_cli::mesh::page::SessionCounts;

let none = SessionCounts::default();
assert_eq!(serde_json::to_value(&none).unwrap(),
    serde_json::json!({ "active": 0, "closed": 0, "expired": 0 }));
propertytyperequireddescription
active integer (uint) yes Open, on a runtime that beats.
closed integer (uint) yes Closed by their own runtime.
expired integer (uint) yes Never closed, on a runtime that stopped beating.

Used by MeshRuntimeSummary MeshSummary

MeshSource

"udp_multicast" | "udp_broadcast" | "rendezvous" | "synthetic"
Where an observation came from. A record accumulates these; provenance is the answer to "why do I see this node".
  • udp_multicastA multicast datagram.
  • udp_broadcastA broadcast datagram.
  • rendezvousA rendezvous answer or registration.
  • syntheticA synthetic provider (tests, self-check).

Used by Sighting

MeshStateAnswer

object
The answer of mesh.state.
propertytyperequireddescription
active boolean yes Whether cooperation runs.
view MeshView → MeshView yes What the answer carries: summary or full.
reason string | null no Why not.
summary MeshSummary | null → MeshSummary no The summary, in the summary view: counts, each runtime's newest live claim, the open reviews and the untaken handovers.
state CooperationState | null → CooperationState no The folded state narrowed by the filters, in the full view; its digest is the whole fold's.
streams array of StreamView → StreamView yes Every stream's liveness.

Used by mesh.state

MeshStatus

object
The whole mesh, as every surface reports it.
propertytyperequireddescription
active boolean yes Whether the mesh is running in this process.
reason string | null no Why not, when it is not: no declaration, disabled, no identity, stopped.
identity PublicIdentity | null → PublicIdentity no This node, when the mesh has an identity.
trust_policy string | null no The trust policy in force.
providers array of ProviderStatus → ProviderStatus yes Every provider, canonical order (by id).
tallies Tallies → Tallies yes The registry's tallies.
refusals Refusals → Refusals yes Refused datagrams, by reason.
started_at string | null no When the mesh started, RFC 3339.

Used by mesh.status

MeshSummary

object
The mesh in an answer a worker reads whole: counts, each runtime, the open reviews and the untaken handovers, every list bounded by SUMMARY_LIST with the rest counted.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{ClaimMode, EventBody, Journal, SessionInfo, StreamLiveness};
use majordomus_cli::mesh::state::fold;

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionOpened { info: SessionInfo::named("s1", "claude-code") })
    .unwrap();
for (claim, path) in [("c1", "apps/a"), ("c2", "apps/b")] {
    j.append_own(EventBody::ClaimAcquired { claim: claim.into(), session: "s1".into(),
        scope: vec![path.into()], intent: Some("work".into()), mode: ClaimMode::Exclusive,
        issue: Some("#1".into()) }).unwrap();
}
j.append_own(EventBody::ClaimReleased { claim: "c1".into() }).unwrap();
let state = fold(&j.events(), &|_| StreamLiveness::Own);
use majordomus_cli::mesh::page::{summarize, Summary};

let summary: Summary = summarize(&state, &[]);
assert_eq!((summary.claims.held, summary.claims.released), (1, 1));
assert_eq!(summary.digest, state.digest);
propertytyperequireddescription
digest string yes The whole fold's digest: equal digests, equal state.
sessions MeshSessionCounts → MeshSessionCounts yes Every session by standing.
claims MeshClaimCounts → MeshClaimCounts yes Every claim by standing.
overlaps integer (uint) yes Advisory overlaps between live claims.
runtimes array of MeshRuntimeSummary → MeshRuntimeSummary yes Each runtime counted, the living first; at most SUMMARY_LIST.
runtimes_not_listed integer (uint) yes Runtimes past the list, counted.
open_reviews array of MeshReviewBrief → MeshReviewBrief yes Review requests nobody has answered; at most SUMMARY_LIST.
open_reviews_not_listed integer (uint) yes Open reviews past the list, counted.
untaken_handovers array of MeshHandoverBrief → MeshHandoverBrief yes Handovers nobody has consumed; at most SUMMARY_LIST.
untaken_handovers_not_listed integer (uint) yes Untaken handovers past the list, counted.

Used by MeshStateAnswer

MeshVerifyCheck

object
One verification check: what was checked, whether it holds, the evidence, and — when something is wrong or limited — the impact and what to do about it.

The impact and the remedy are carried with the verdict rather than left to whoever renders it, so that a failing check is actionable wherever it is read — in a terminal, in a JSON answer, or by an agent that has never seen the mesh before. A check that holds carries neither, because there is nothing to do.

#### use std::sync::Arc;
#### use majordomus_cli::mesh::config::{CooperationConfig, TrustConfig};
#### use majordomus_cli::mesh::cooperation::{CheckoutFacts, Cooperation, CooperationSetup};
#### use majordomus_cli::mesh::identity::NodeIdentity;
#### use majordomus_cli::mesh::link::HttpTransport;
#### use majordomus_cli::mesh::registry::MeshRegistry;
#### use majordomus_cli::mesh::repository::of_root_commits;
#### let cooperation = Cooperation::new(CooperationSetup {
####     identity: Arc::new(NodeIdentity::ephemeral().unwrap()), runtime: "0000000000000001".into(),
####     repository: of_root_commits(&["root".into()]), endpoints: vec!["127.0.0.1:9".into()],
####     version: "doc".into(), config: CooperationConfig::default(), trust: TrustConfig::default(),
####     journal_path: None, registry: Arc::new(MeshRegistry::new()),
####     transport: Arc::new(HttpTransport), board: None, checkout: CheckoutFacts::default(),
#### }).unwrap();
use majordomus_cli::mesh::cooperation::MeshVerifyCheck;

let report = cooperation.verify();
let first: &MeshVerifyCheck = &report.checks[0];
assert_eq!(first.check, "cooperation");
assert!(first.detail.contains(cooperation.runtime_key()), "the evidence, not a verdict");
assert!(first.remediation.is_none(), "a check that holds asks for nothing");
propertytyperequireddescription
check string yes What was checked.
ok boolean yes Whether it holds.
detail string yes The evidence.
impact string | null no What the failure or limitation means for cooperation.
remediation string | null no What to do.

Used by MeshVerifyReport

MeshVerifyReport

object
What mesh verify found: the local checks, every peer's round, and the state digest afterwards. The digest is what makes the report worth running on two machines — two runtimes that print the same digest have the same sessions, claims, handovers and reviews, and two that do not have something to reconcile, whatever their links say.

ok is the conjunction of everything below it, so a caller may act on the one field and a person may read the rest to find out which part of it was false.

#### use std::sync::Arc;
#### use majordomus_cli::mesh::config::{CooperationConfig, TrustConfig};
#### use majordomus_cli::mesh::cooperation::{CheckoutFacts, Cooperation, CooperationSetup};
#### use majordomus_cli::mesh::identity::NodeIdentity;
#### use majordomus_cli::mesh::link::HttpTransport;
#### use majordomus_cli::mesh::registry::MeshRegistry;
#### use majordomus_cli::mesh::repository::of_root_commits;
#### let cooperation = Cooperation::new(CooperationSetup {
####     identity: Arc::new(NodeIdentity::ephemeral().unwrap()), runtime: "0000000000000001".into(),
####     repository: of_root_commits(&["root".into()]), endpoints: vec!["127.0.0.1:9".into()],
####     version: "doc".into(), config: CooperationConfig::default(), trust: TrustConfig::default(),
####     journal_path: None, registry: Arc::new(MeshRegistry::new()),
####     transport: Arc::new(HttpTransport), board: None, checkout: CheckoutFacts::default(),
#### }).unwrap();
use majordomus_cli::mesh::cooperation::MeshVerifyReport;

let report: MeshVerifyReport = cooperation.verify();
assert_eq!(report.runtime.as_deref(), Some(cooperation.runtime_key()));
assert_eq!(report.digest, Some(cooperation.state().digest), "what a peer is compared to");
assert_eq!(report.ok, report.checks.iter().all(|c| c.ok), "one field for every check");
propertytyperequireddescription
ok boolean yes Whether every check holds and every round succeeded.
runtime string | null no This runtime.
checks array of MeshVerifyCheck → MeshVerifyCheck yes The checks, in the order they ran.
peers array of PeerVerdict → PeerVerdict yes Every linked peer's verdict.
digest string | null no The state digest after the rounds.

Used by mesh.verify

MeshView

"summary" | "full"
How much an answer about the mesh carries. The summary is the default because it is the answer a worker can read whole whatever the mesh's history; the full fold is one word away when a reader needs every record.

use majordomus_cli::mesh::page::View;

assert_eq!(View::default(), View::Summary);
assert_eq!(serde_json::to_value(View::Full).unwrap(), "full");
  • summaryCounts, each runtime's newest live claim, the open reviews and the untaken handovers: bounded, whatever the history.
  • fullEverything, narrowed only by the filters.

Used by mesh.peers mesh.state MeshStateAnswer PeerTree

NodeId

string
The stable identity of a node: 32 hex characters of the SHA-256 of its Ed25519 public key. Derived, never chosen: two nodes with one id hold one key.

Used by NodeRecord PublicIdentity

NodeList

object
The answer of mesh.nodes: every node this process has observed.
propertytyperequireddescription
count integer (uint) yes How many nodes the registry holds.
nodes array of NodeRecord → NodeRecord yes The nodes, in node-id order — the one canonical order every surface shows.

Used by mesh.nodes

NodeRecord

object
One discovered node: the canonical record every surface projects.
propertytyperequireddescription
node_id NodeId → NodeId yes The node id: the digest of the key below.
runtime string no The runtime slot: which server of the node (one per checkout), 16 hex; empty for a version-1 advertisement. A record is one runtime: two worktrees of one machine are two records under one node id and one key.
public_key string yes The Ed25519 public key that proved this record's advertisements.
display_name string yes The node's own display name. Presentation only.
instance_id InstanceId → InstanceId yes The node's current run.
trust TrustState → TrustState yes What the policy decided.
presence Presence → Presence yes Whether the node is answering now.
endpoints array of string yes Advertised host:port authorities.
capabilities array of string yes Advertised transports (http, mcp, ws).
repositories array of string yes Advertised repository digests.
protocol_version integer (uint32) yes The advertised protocol version.
version string yes The advertised executable version.
sources array of Sighting → Sighting yes Every source this node was seen through, most recent sighting per source.
first_seen string yes First seen, RFC 3339.
last_seen string yes Last seen, RFC 3339.
restarts integer (uint64) yes How many instances this node id has shown (a restart increments it).

Used by NodeList

PeerDetail

object
The answer of mesh.peer.
propertytyperequireddescription
found boolean yes Whether the runtime is known here.
reason string | null no Why not, when it is not.
machine string | null no The machine it runs on.
runtime RuntimeNode | null → RuntimeNode no The runtime, its link and its sessions.
refused array of RefusedView → RefusedView yes Refusals recorded against it.

Used by mesh.peer

PeerTree

object
The answer of mesh.peers: machine → runtime → session → claim, every level from the one journal and the one link table.
propertytyperequireddescription
active boolean yes Whether cooperation runs.
view MeshView → MeshView yes What the answer carries: summary or full.
reason string | null no Why not.
runtime string | null no This runtime.
repository string | null no The mesh repository id.
machines array of MachineNode → MachineNode yes The machines, local first.
refused array of RefusedView → RefusedView yes Candidates refused, with the rule.
digest string | null no The digest of the cooperation state: equal digests, equal state.

Used by mesh.peers

PeerVerdict

object
What one sync round, run now rather than remembered, said about one peer. Verification does not report the link's standing and stop there: it exchanges with the peer and says whether the exchange worked and whether both sides ended holding the same marks, which is the difference between a peer that answers and a peer that agrees.

round_trip and converged are optional because a peer this runtime does not dial is not asked — an absent answer is not a failed one, and reporting false there would make an inbound-only link look broken every time.

#### use std::collections::BTreeMap;
#### use std::sync::{Arc, Mutex, Weak};
#### use majordomus_cli::mesh::config::{CooperationConfig, TrustConfig};
#### use majordomus_cli::mesh::cooperation::*;
#### use majordomus_cli::mesh::identity::NodeIdentity;
#### use majordomus_cli::mesh::link::{LinkReply, LinkTransport, Signed, HELLO_PATH};
#### use majordomus_cli::mesh::registry::MeshRegistry;
#### use majordomus_cli::mesh::repository::{of_root_commits, runtime_id};
#### use majordomus_cli::mesh::trust::TrustPolicy;
#### #[derive(Default)] struct Net(Mutex<BTreeMap<String, Weak<Cooperation>>>);
#### impl LinkTransport for Net {
####   fn post(&self, e: &str, p: &str, m: &Signed) -> Result<LinkReply, String> {
####     let peer = (self.0.lock().unwrap().get(e).and_then(Weak::upgrade))
####         .ok_or_else(|| format!("{e}: nobody there"))?;
####     Ok(if p == HELLO_PATH { peer.accept_hello(m) } else { peer.accept_sync(m) }) } }
#### let net = Arc::new(Net::default());
#### let runtime = |endpoint: &str| { let c = Cooperation::new(CooperationSetup {
####     identity: Arc::new(NodeIdentity::ephemeral().unwrap()), runtime: runtime_id(endpoint),
####     repository: of_root_commits(&["root".into()]), endpoints: vec![endpoint.into()],
####     version: "doc".into(), config: CooperationConfig::default(),
####     trust: TrustConfig { policy: TrustPolicy::Tofu, allow: vec![] }, journal_path: None,
####     registry: Arc::new(MeshRegistry::new()),
####     transport: Arc::clone(&net) as Arc<dyn LinkTransport>, board: None,
####     checkout: CheckoutFacts::default() }).unwrap();
####   net.0.lock().unwrap().insert(endpoint.into(), Arc::downgrade(&c)); c };
#### let (a, b) = (runtime("a:1"), runtime("b:1"));
a.dial(&["b:1".into()]).unwrap();

let dialer: &PeerVerdict = &a.verify().peers[0];
assert_eq!(dialer.round_trip, Some(true), "asked, and answered");

let answerer = &b.verify().peers[0];
assert!(!answerer.dialed, "B does not dial A, so B asks nothing of it");
assert_eq!(answerer.round_trip, None, "and reports no verdict it did not earn");
propertytyperequireddescription
runtime string yes The peer's runtime key.
state LinkState → LinkState yes The link's standing after the round.
dialed boolean yes Whether this runtime dials the peer (and so ran a round with it now).
round_trip boolean | null no Whether the round succeeded; None when the peer dials this runtime instead.
rtt_ms integer | null no The round trip, milliseconds.
converged boolean | null no Whether both sides hold the same high-water mark of every stream after the round.
detail string yes What happened.

Used by MeshVerifyReport

PeerView

object
One peer as every surface shows it: who it is, how this runtime reaches it, and what the link has been through. The counters are carried per peer rather than only in the totals because the question a person asks of a flapping mesh is which peer is flapping, and restarts answers a different one from reconnects — a peer whose process keeps dying, against a link that keeps dropping under a process that never did.

#### use std::collections::BTreeMap;
#### use std::sync::{Arc, Mutex, Weak};
#### use majordomus_cli::mesh::config::{CooperationConfig, TrustConfig};
#### use majordomus_cli::mesh::cooperation::*;
#### use majordomus_cli::mesh::identity::NodeIdentity;
#### use majordomus_cli::mesh::link::{LinkReply, LinkTransport, Signed, HELLO_PATH};
#### use majordomus_cli::mesh::registry::MeshRegistry;
#### use majordomus_cli::mesh::repository::{of_root_commits, runtime_id};
#### use majordomus_cli::mesh::trust::TrustPolicy;
#### #[derive(Default)] struct Net(Mutex<BTreeMap<String, Weak<Cooperation>>>);
#### impl LinkTransport for Net {
####   fn post(&self, e: &str, p: &str, m: &Signed) -> Result<LinkReply, String> {
####     let peer = (self.0.lock().unwrap().get(e).and_then(Weak::upgrade))
####         .ok_or_else(|| format!("{e}: nobody there"))?;
####     Ok(if p == HELLO_PATH { peer.accept_hello(m) } else { peer.accept_sync(m) }) } }
#### let net = Arc::new(Net::default());
#### let runtime = |endpoint: &str| { let c = Cooperation::new(CooperationSetup {
####     identity: Arc::new(NodeIdentity::ephemeral().unwrap()), runtime: runtime_id(endpoint),
####     repository: of_root_commits(&["root".into()]), endpoints: vec![endpoint.into()],
####     version: "doc".into(), config: CooperationConfig::default(),
####     trust: TrustConfig { policy: TrustPolicy::Tofu, allow: vec![] }, journal_path: None,
####     registry: Arc::new(MeshRegistry::new()),
####     transport: Arc::clone(&net) as Arc<dyn LinkTransport>, board: None,
####     checkout: CheckoutFacts::default() }).unwrap();
####   net.0.lock().unwrap().insert(endpoint.into(), Arc::downgrade(&c)); c };
#### let (a, b) = (runtime("a:1"), runtime("b:1"));
a.dial(&["b:1".into()]).unwrap();

let peer: &PeerView = &a.peers()[0];
assert_eq!(peer.runtime, b.runtime_key(), "the durable identity, not the endpoint");
assert!(peer.outbound, "this runtime dials it");
assert_eq!(peer.handshakes, 1);
assert_eq!(peer.restarts, 0, "the peer's process has not died under the link");
propertytyperequireddescription
runtime string yes <node>-<runtime>: the peer's durable identity.
node string yes The node (machine key digest).
instance string yes The current instance.
name string yes The node's display name.
version string yes The executable version.
local boolean yes Whether the peer runs on this machine (the same node key).
trust TrustState → TrustState yes The trust verdict the link was admitted under.
protocol integer (uint32) yes The negotiated link protocol.
features array of string yes The link features the peer carries.
endpoints array of string yes Where the peer answers.
state LinkState → LinkState yes The link's standing.
outbound boolean yes Whether this runtime dials the peer.
inbound boolean yes Whether the peer dials this runtime.
endpoint string | null no The endpoint this runtime dials, when it does.
last_exchange_ms integer | null no Milliseconds since the last successful exchange in either direction.
rtt_ms integer | null no The last round trip, milliseconds.
failures integer (uint32) yes Consecutive failed rounds.
last_error string | null no The last failure, in words.
handshakes integer (uint64) yes Handshakes completed with this peer.
reconnects integer (uint64) yes Handshakes after an expiry or a dropped link.
restarts integer (uint64) yes New instances of the peer seen (its process restarted).
linked_at string yes When the current link was established, RFC 3339.

Used by CooperationStatus RuntimeNode

Presence

"present" | "absent"
Whether the node is answering now, by its own announcements.
  • presentAn advertisement arrived within PRESENCE_TTL.
  • absentNothing within the TTL; the node may be gone or unreachable. It stays listed until RETENTION passes, then expires out entirely.

Used by NodeRecord

Protocol

"udp" | "tcp"
The transport protocol of a rule: discovery is datagrams, registration and the link protocol are connections. Serialised as the word a firewall front takes.

use majordomus_cli::mesh::firewall::Protocol;

assert_eq!(serde_json::to_value(Protocol::Udp).unwrap(), "udp");
assert_eq!(serde_json::to_value(Protocol::Tcp).unwrap(), "tcp");
  • udpDatagrams: discovery.
  • tcpConnections: the hub's registrations and the link protocol.

Used by FirewallRule

ProviderStatus

object
One provider's status: state, why, and its counters.
propertytyperequireddescription
id string yes The provider id: udp_multicast, udp_broadcast, rendezvous, synthetic.
state MeshProviderState → MeshProviderState yes The state.
detail string | null no Why, when Failed; what it watches, when Running.
sent integer (uint64) yes Advertisements transmitted.
received integer (uint64) yes Datagrams heard and handed to the manager.

Used by MeshStatus

PublicIdentity

object
What everyone may know about this node: the id, the public key, a display name and this run's instance. Everything an advertisement carries; nothing that signs.
propertytyperequireddescription
node_id NodeId → NodeId yes The node id: a digest of the public key.
public_key string yes The Ed25519 public key, 64 hex characters. Whoever holds the matching secret is this node; there is no other proof.
display_name string yes A human name for the node, from the identity file; defaults to the first eight characters of the node id. Presentation only — never used for trust.
instance_id InstanceId → InstanceId yes This run of the node.

Used by MeshIdentityReport MeshStatus

RefusalCode

"malformed" | "oversized" | "protocol_unsupported" | "signature" | "stale" | "replay" | "repository_mismatch" | "untrusted" | "self_link" | "not_active" | "unknown_link" | "capacity" | "feature_unsupported" | "misaddressed"
Why a link message was turned down. Every refusal is typed, counted and shown: a mesh that refuses a peer says which rule did, instead of dropping a socket.

The codes are the diagnosis a person needs when two runtimes will not link, and they are deliberately distinct where the remedies differ: Untrusted is a key to add, RepositoryMismatch is two different repositories, SelfLink is a configuration that dials its own endpoint, and UnknownLink is not a failure at all but an instruction to say hello again.

use majordomus_cli::mesh::link::RefusalCode;

// the wire word is the code's identity across surfaces, and it is stable
assert_eq!(RefusalCode::RepositoryMismatch.as_str(), "repository_mismatch");
assert_eq!(
    serde_json::to_value(RefusalCode::Untrusted).unwrap(),
    serde_json::json!("untrusted"),
);
assert_ne!(RefusalCode::Untrusted, RefusalCode::RepositoryMismatch);
  • malformedNot the shape of the message.
  • oversizedOver MAX_LINK_MESSAGE.
  • protocol_unsupportedThe protocol ranges do not meet.
  • signatureThe signature does not verify.
  • staleThe timestamp is outside the skew window.
  • replayA nonce or a counter already seen.
  • repository_mismatchThe peer serves another repository: the isolation rule.
  • untrustedThe peer's key is not trusted by this runtime's policy: discovery is not trust.
  • self_linkThe peer is this very runtime.
  • not_activeThis runtime's cooperation is not active.
  • unknown_linkThe link id is unknown here: the answerer restarted or expired the link; say hello.
  • capacityA bound of this runtime is full.
  • feature_unsupportedThe peer lacks a feature the operation needs.
  • misaddressedA hello addressed to another runtime: captured on its way there and replayed here.

Used by LinkRefusal

Refusals

object
Refused datagrams, by reason. The diagnosis of a hostile or misconfigured network: a mesh that hears garbage says so, with numbers, instead of being quietly empty.
propertytyperequireddescription
oversized integer (uint64) yes Longer than the datagram bound.
malformed integer (uint64) yes Not an envelope.
version integer (uint64) yes A protocol version this executable does not read.
bounds integer (uint64) yes Out-of-bounds fields.
stale integer (uint64) yes Outside the clock-skew window.
signature integer (uint64) yes A signature that does not verify.
self_heard integer (uint64) yes This node's own datagrams, heard back and skipped.
overflow integer (uint64) no Observations dropped because the queue to the registry was full: a flood, counted rather than held (I2133).

Used by MeshStatus

RefusedView

object
A candidate that was refused, with the rule that refused it. A refusal is kept and shown rather than logged and dropped, because the commonest mesh failure is a link that never forms: a person who declared a seed and sees no peer needs the decision that was made about it — the wrong repository, a key nobody allowed, a clock too far out — and the direction says which side made it.

#### use std::collections::BTreeMap;
#### use std::sync::{Arc, Mutex, Weak};
#### use majordomus_cli::mesh::config::{CooperationConfig, TrustConfig};
#### use majordomus_cli::mesh::cooperation::*;
#### use majordomus_cli::mesh::identity::NodeIdentity;
#### use majordomus_cli::mesh::link::{LinkReply, LinkTransport, RefusalCode, Signed, HELLO_PATH};
#### use majordomus_cli::mesh::registry::MeshRegistry;
#### use majordomus_cli::mesh::repository::{of_root_commits, runtime_id};
#### use majordomus_cli::mesh::trust::TrustPolicy;
#### #[derive(Default)] struct Net(Mutex<BTreeMap<String, Weak<Cooperation>>>);
#### impl LinkTransport for Net {
####   fn post(&self, e: &str, p: &str, m: &Signed) -> Result<LinkReply, String> {
####     let peer = (self.0.lock().unwrap().get(e).and_then(Weak::upgrade))
####         .ok_or_else(|| format!("{e}: nobody there"))?;
####     Ok(if p == HELLO_PATH { peer.accept_hello(m) } else { peer.accept_sync(m) }) } }
#### let net = Arc::new(Net::default());
#### let runtime = |endpoint: &str, policy: TrustPolicy| {
####   let c = Cooperation::new(CooperationSetup {
####     identity: Arc::new(NodeIdentity::ephemeral().unwrap()), runtime: runtime_id(endpoint),
####     repository: of_root_commits(&["root".into()]), endpoints: vec![endpoint.into()],
####     version: "doc".into(), config: CooperationConfig::default(),
####     trust: TrustConfig { policy, allow: vec![] }, journal_path: None,
####     registry: Arc::new(MeshRegistry::new()),
####     transport: Arc::clone(&net) as Arc<dyn LinkTransport>, board: None,
####     checkout: CheckoutFacts::default() }).unwrap();
####   net.0.lock().unwrap().insert(endpoint.into(), Arc::downgrade(&c)); c };
#### let a = runtime("a:1", TrustPolicy::Tofu);
#### let strict = runtime("s:1", TrustPolicy::DenyUnknown);
a.dial(&["s:1".into()]).expect_err("reachable, and not trusted");

let refused: &RefusedView = &strict.status().refused[0];
assert_eq!(refused.refusal.code, RefusalCode::Untrusted);
assert_eq!(refused.direction, "inbound", "this runtime refused the hello");
assert_eq!(refused.runtime.as_deref(), Some(a.runtime_key()));
propertytyperequireddescription
endpoint string yes The endpoint dialed, or inbound:<runtime> for a refused hello.
direction string yes outbound (this runtime refused the peer's welcome, or the peer refused this hello) or inbound (this runtime refused the peer's hello).
runtime string | null no The peer's runtime key, when its card said.
refusal LinkRefusal → LinkRefusal yes The refusal.
at string yes When, RFC 3339.

Used by CooperationStatus PeerDetail PeerTree

RegisterAnswer

object
What a registration answers: whether the envelope was accepted, and the candidates the rendezvous holds. Defined here beside the client; the capability re-exports it.
propertytyperequireddescription
accepted boolean yes Whether the presented envelope was accepted into the local registry.
refusal string | null no Why not, when it was not.
candidates array of Envelope → Envelope yes The signed envelopes of the nodes this rendezvous currently holds, the answering node's own included. Each verifies on its own signature; the rendezvous vouches for nothing.

Used by mesh.register

RegisterInput

object
The input of mesh.register: one signed envelope, exactly as the wire carries it.
propertytyperequireddescription
envelope any yes The envelope. Verified here exactly as a datagram would be — bounds, staleness, signature — before anything is recorded.

Used by mesh.register

ReleaseInput

object
The input of mesh.release.
propertytyperequireddescription
claim string yes The claim's key, <stream>/<claim>, as mesh.claim answered it.

Used by mesh.release

ReviewAnswer

object
One answer to a review request: who answered, what they decided, and why. The verdict is one of the three declared words, so a requester can act on it without reading the note; the note is where the reasoning goes and is optional, because "approved" often needs none.

use majordomus_cli::mesh::state::ReviewAnswer;

let answer = ReviewAnswer {
    session: "a1b2c3d4-r1-0f/s2".into(),
    verdict: "changes_requested".into(),
    note: Some("the fold is not deterministic".into()),
};
assert_eq!(answer.verdict, "changes_requested");

// the note is omitted rather than carried empty when there is nothing to say
let bare = ReviewAnswer { note: None, ..answer };
let value = serde_json::to_value(&bare).unwrap();
assert!(value.get("note").is_none());
propertytyperequireddescription
session string yes The answering session's key.
verdict string yes The verdict.
note string | null no The note.

Used by ReviewView

ReviewAnswerInput

object
The input of mesh.review.answer.
propertytyperequireddescription
request string yes The request's key, <stream>/<review>.
session string | null no The answering session; the calling MCP session's own when omitted.
verdict string yes approved, changes_requested or commented.
note string | null no The note.

Used by mesh.review.answer

ReviewRequestInput

object
The input of mesh.review.request.
propertytyperequireddescription
session string | null no The requesting session; the calling MCP session's own when omitted.
subject string yes What to review: a branch, a commit, a pull request.
scope array of string no The paths it covers.
issue string | null no The issue.
reviewer string | null no The runtime asked (<node>-<runtime>); anyone when omitted.
body string | null no What the reviewer needs beyond the subject: evidence, commands, the question. Up to 8 KiB, several lines.

Used by mesh.review.request

ReviewState

"open" | "answered" | "withdrawn" | "expired"
A review request's standing: whether anybody has answered it yet. There is deliberately no Approved or Rejected here — a request may be answered by several runtimes and the verdicts are the answers' own, so collapsing them into one state would throw away the disagreement that makes a second reviewer worth asking.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal, StreamLiveness};
use majordomus_cli::mesh::state::{fold, ReviewState};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::ReviewRequested { review: "r1".into(), session: "s1".into(),
    subject: "feature/x".into(), scope: vec![], issue: None, reviewer: None, body: None }).unwrap();
let asked = fold(&j.events(), &|_| StreamLiveness::Own);
assert_eq!(asked.reviews[0].state, ReviewState::Open);

let request = asked.reviews[0].key.clone();
j.append_own(EventBody::ReviewAnswered { request, session: "s2".into(),
    verdict: "approved".into(), note: None }).unwrap();
assert_eq!(fold(&j.events(), &|_| StreamLiveness::Own).reviews[0].state, ReviewState::Answered);
  • openNobody has answered.
  • answeredAt least one answer.
  • withdrawnThe runtime that asked ended the request: done, void or superseded. Whatever was answered before stays with it.
  • expiredNobody answered and the runtime that asked stopped beating. A reading of liveness, like a claim's expiry, decided from the journal and the streams' beats so that no runtime needs another's clock — a runtime that beats again reads open again.

Used by ReviewView

ReviewView

object
One review request anywhere in the mesh, with every answer it has drawn. A request with no reviewer is an offer to the whole mesh rather than to nobody, which is why the answers are a list: any linked runtime carrying the reviews feature may add one, and they are kept in Lamport order so that every runtime reads them in the same sequence.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal, StreamLiveness};
use majordomus_cli::mesh::state::{fold, ReviewView};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::ReviewRequested { review: "r1".into(), session: "s1".into(),
    subject: "feature/mesh-cooperation".into(), scope: vec!["src/mesh".into()],
    issue: Some("#184".into()), reviewer: None, body: None }).unwrap();
let request = fold(&j.events(), &|_| StreamLiveness::Own).reviews[0].key.clone();
j.append_own(EventBody::ReviewAnswered { request, session: "s2".into(),
    verdict: "approved".into(), note: Some("reads well".into()) }).unwrap();

let state = fold(&j.events(), &|_| StreamLiveness::Own);
let review: &ReviewView = &state.reviews[0];
assert_eq!(review.subject, "feature/mesh-cooperation");
assert_eq!(review.reviewer, None, "offered to the mesh, not addressed");
assert_eq!(review.answers[0].verdict, "approved");
propertytyperequireddescription
key string yes <stream>/<review>.
session string yes The requesting session's key.
subject string yes What is to be reviewed.
scope array of string yes Paths it covers.
issue string | null no The issue.
reviewer string | null no The runtime asked, when one was.
body string | null no What the reviewer needs beyond the subject, when the requester wrote it.
state ReviewState → ReviewState yes Its standing.
answers array of ReviewAnswer → ReviewAnswer yes The answers, in Lamport order.
result string | null no The requester's own result or reason, written when it withdrew the request. Never an answer: a review is another session's verdict.

Used by CooperationState

ReviewWithdrawInput

object
The input of mesh.review.withdraw.
propertytyperequireddescription
request string yes The request's key, <stream>/<review>.
session string | null no The withdrawing session of the asking runtime; the calling MCP session's own when omitted.
note string | null no The requester's result or reason, up to 8 KiB: what came of it, or why it is void.

Used by mesh.review.withdraw

Role

"multicast" | "broadcast" | "hub" | "link"
Why a rule exists: which part of the declaration implied it. The role is what a person reads beside the rule and what a verdict names, and it is serialised in snake case.

use majordomus_cli::mesh::firewall::Role;

assert_eq!(serde_json::to_value(Role::Hub).unwrap(), "hub");
let back: Role = serde_json::from_value(serde_json::json!("multicast")).unwrap();
assert_eq!(back, Role::Multicast);
  • multicastThe declared multicast group: every runtime of the segment advertises to it.
  • broadcastThe declared broadcast port, when broadcast is a permitted fallback.
  • hubThis machine is a declared rendezvous hub: the fleet registers here.
  • linkThis machine's server is dialed by peers on the link protocol.

Used by FirewallRule

RuntimeNode

object
One runtime in the peer tree.
propertytyperequireddescription
runtime string yes <node>-<runtime>.
this_runtime boolean yes Whether this is the runtime answering.
liveness StreamLiveness → StreamLiveness yes Whether its stream beats: own, live, or expired.
last_beat_ms integer | null no Milliseconds since its beat last rose, on this runtime's clock.
link PeerView | null → PeerView no This runtime's link to it, when there is one.
newest_claim MeshClaimBrief | null → MeshClaimBrief no Its newest live claim, told briefly.
sessions array of SessionNode → SessionNode yes Its sessions; in the summary view only the active ones.

Used by MachineNode PeerDetail

SessionCloseInput

object
The input of mesh.session.close.
propertytyperequireddescription
session string | null no The session; the calling MCP session's own when omitted.

Used by mesh.session.close

SessionInfo

object
What a session tells the mesh about itself. Every field is a public fact about work in progress; none is a path on the author's disk, a secret or a credential.

Almost everything here is optional, and that is the design: a session says what it knows, and a field it leaves out is omitted rather than carried empty, so a reader can tell "no task" from "a task called nothing". Opening the same session id again replaces this record, which is how a session that learns its branch later tells the mesh.

use majordomus_cli::mesh::journal::SessionInfo;

let mut info = SessionInfo::named("s1", "claude-code");
assert_eq!(info.task, None);
assert!(serde_json::to_value(&info).unwrap().get("task").is_none(), "unsaid, not empty");

info.branch = Some("feature/mesh-cooperation".into());
assert_eq!(serde_json::to_value(&info).unwrap()["branch"], "feature/mesh-cooperation");
propertytyperequireddescription
session string yes The session's id within its stream.
client string yes The client: claude-code, codex, cli, ...
worker string | null no The worker's own name for itself, when it gave one.
intent string | null no What the session says it is doing.
task string | null no The task id (t-...) the session works under.
issue string | null no The issue (I0001, #184) the work belongs to.
milestone string | null no The milestone the work belongs to.
checkout string | null no The checkout id (a digest, never a path).
branch string | null no The branch checked out.
head string | null no The commit checked out.
dirty boolean | null no Whether the working tree had uncommitted changes when this was said.
context string | null no The context revision the session works from (a digest the context compiler names).

Used by SessionNode SessionView

SessionInput

object
The input of mesh.session.open: what a session says about itself.
propertytyperequireddescription
session string | null no The session id within this runtime; the calling MCP session's own when omitted.
client string | null no The client (claude-code, codex, cli); cli when omitted.
worker string | null no The worker's name for itself.
intent string | null no What it is doing.
task string | null no The task id.
issue string | null no The issue.
milestone string | null no The milestone.
branch string | null no The branch.
head string | null no The head.
dirty boolean | null no Whether the working tree is dirty.
context string | null no The context revision it works from.

Used by mesh.session.open

SessionNode

object
One session in the peer tree.
propertytyperequireddescription
key string yes <stream>/<session>.
info SessionInfo → SessionInfo yes What the session last said about itself.
state SessionState → SessionState yes Its standing.
claims array of ClaimView → ClaimView yes The claims it holds or held; empty in the summary view, which counts them instead.
claims_held integer (uint) yes How many of its claims are live.
reviews array of string yes The review requests it opened, by key.

Used by RuntimeNode

SessionState

"active" | "closed" | "expired"
A session's standing, as every runtime of the mesh reads it. The distinction that matters is between Closed and Expired: the first is a session that said it was finished, the second one whose runtime stopped beating and can no longer say anything. Both end the session's claims, but only the second is a machine nobody should expect an answer from.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal, SessionInfo, StreamLiveness};
use majordomus_cli::mesh::state::{fold, SessionState};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionOpened { info: SessionInfo::named("s1", "cli") }).unwrap();
assert_eq!(fold(&j.events(), &|_| StreamLiveness::Own).sessions[0].state, SessionState::Active);

// the same events, read on a runtime that no longer hears the writer
let gone = fold(&j.events(), &|_| StreamLiveness::Expired);
assert_eq!(gone.sessions[0].state, SessionState::Expired);
  • activeOpen, and its runtime is alive.
  • closedClosed by its own runtime.
  • expiredNever closed, but its runtime stopped beating: gone, as far as anyone can tell.

Used by SessionNode SessionView

SessionView

object
One session anywhere in the mesh, as the fold sees it: what the session said about itself, where it is writing from, and whether it still counts. The key is <stream>/<session> rather than the session's own id, because two runtimes may both call a session s1 and the mesh has to hold them apart.

use std::sync::Arc;
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{EventBody, Journal, SessionInfo, StreamLiveness};
use majordomus_cli::mesh::state::{fold, SessionView};

let j = Journal::open(Arc::new(NodeIdentity::ephemeral().unwrap()), "0000000000000001",
    "repo".into(), None).unwrap();
j.append_own(EventBody::SessionOpened { info: SessionInfo::named("s1", "claude-code") })
    .unwrap();

let state = fold(&j.events(), &|_| StreamLiveness::Own);
let view: &SessionView = &state.sessions[0];
assert!(view.key.ends_with("/s1"), "qualified by the stream that wrote it: {}", view.key);
assert_eq!(view.info.client, "claude-code");
assert_eq!(view.runtime, view.stream.runtime_key());
propertytyperequireddescription
key string yes <stream>/<session>: unique across the mesh.
stream StreamId → StreamId yes The writing stream.
runtime string yes <node>-<runtime>.
node string yes The node (machine).
info SessionInfo → SessionInfo yes What the session last said about itself.
state SessionState → SessionState yes Its standing.
updated_lamport integer (uint64) yes The Lamport stamp of its latest word.

Used by CooperationState

Sighting

object
One provenance entry: a source, and where the observation physically came from.
propertytyperequireddescription
source MeshSource → MeshSource yes The provider kind that saw it.
path string yes The network path, as text: a sender address, an interface, a rendezvous URL.
at string yes When, RFC 3339.

Used by NodeRecord

Signed

object
A signed message: a JSON body and the Ed25519 signature over its canonical bytes under the message's domain. The body stays JSON so that fields a newer peer adds survive verification here unchanged.

Keeping the body as JSON rather than as the bytes that were signed is what makes the protocol extensible: a field a newer peer added is carried through verification untouched, because the signature is checked over the canonical form of whatever is there rather than over a shape this version knows.

use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::link::{sign, verify_signed, Domain, Signed};

let node = NodeIdentity::ephemeral().unwrap();
let signed: Signed = sign(&node, Domain::SyncRequest, serde_json::json!({ "b": 2, "a": 1 }));
assert_eq!(signed.sig.len(), 128, "hex Ed25519");

// key order is not content, so a round trip through JSON still verifies
let wire = serde_json::to_string(&signed).unwrap();
let back: Signed = serde_json::from_str(&wire).unwrap();
assert!(verify_signed(&node.public.public_key, Domain::SyncRequest, &back));
propertytyperequireddescription
body any yes The message.
sig string yes Hex Ed25519 over the domain and the canonical JSON of body.

Used by mesh.link.hello mesh.link.sync LinkReply

StreamId

string
One stream: a run of one runtime of one node. <node 32 hex>-<runtime 16 hex>-<instance 16 hex>. The node is the machine's key, the runtime is one checkout's server on that machine, the instance is one process run of that server — so two worktrees on one machine are two streams, and a restart is a new stream of the same runtime.

Numbering events within a stream rather than globally is what makes replication cheap and unambiguous: a gap in one stream is visible without coordinating with anyone, and (stream, seq) identifies an event on every machine that holds it.

use majordomus_cli::mesh::journal::StreamId;

let id = StreamId::new(&"a".repeat(32), &"b".repeat(16), &"c".repeat(16)).unwrap();
assert_eq!(id.node(), "a".repeat(32));
assert_eq!(id.runtime(), "b".repeat(16));

// a restart is a new stream, and the same runtime
let after = StreamId::new(&"a".repeat(32), &"b".repeat(16), &"d".repeat(16)).unwrap();
assert_ne!(after, id);
assert_eq!(after.runtime_key(), id.runtime_key());

Used by ClaimView CooperationStatus HandoverView MeshEvent SessionView StreamView

StreamLiveness

"own" | "live" | "expired"
Whether a stream is speaking now, on this runtime's clock. It is a verdict about a stream and not about a link: a runtime reachable only through a relay is alive, and a runtime that crashed stops beating for everyone at once. Own is kept apart from Live because a runtime's own stream needs no evidence — it is the thing doing the beating.

use majordomus_cli::mesh::journal::StreamLiveness;

assert!(StreamLiveness::Own.is_alive() && StreamLiveness::Live.is_alive());
assert!(!StreamLiveness::Expired.is_alive());
assert_eq!(
    serde_json::to_value(StreamLiveness::Expired).unwrap(),
    serde_json::json!("expired"),
);
  • ownThis runtime's own current stream.
  • liveIts beat rose within the expiry.
  • expiredIts beat has not risen within the expiry, or was never heard.

Used by MeshRuntimeSummary RuntimeNode StreamView

StreamView

object
One stream's liveness.
propertytyperequireddescription
stream StreamId → StreamId yes The stream.
liveness StreamLiveness → StreamLiveness yes Own, live or expired.
last_beat_ms integer | null no Milliseconds since its beat rose.

Used by MeshStateAnswer

Tallies

object
The registry's tallies, for status and health.
propertytyperequireddescription
nodes integer (uint) yes Records held.
trusted integer (uint) yes Of which trusted.
present integer (uint) yes Of which present.
accepted integer (uint64) yes Observations accepted since start.
replayed integer (uint64) yes Replayed datagrams dropped since start.
expired integer (uint64) yes Records expired out since start.
evicted integer (uint64) yes Records evicted for space since start.

Used by MeshStatus

TrustState

one of 3 variants
What the policy decided about one node.
variant Valid, recorded, trusted for nothing. The resting state of an unknown node.
state"observed"yes
variant The policy trusts this key; detail says which rule did (allowlist, tofu).
state"trusted"yes
detailstringyes
variant The policy rejected this node; detail says why (for instance a TOFU key change). A rejected node stays listed — an operator debugging a mesh needs to see what was refused, not wonder about silence.
state"rejected"yes
detailstringyes

Used by NodeRecord PeerView

Written

object
What an operation wrote, as its answer to the caller. Every mesh operation is an event appended to this runtime's journal, and the caller is handed the three facts it cannot work out for itself: the mesh-wide key of the thing it created, the event that says so, and the Lamport stamp that places it against everyone else's events.

The key is what a later operation is addressed by — a claim is released by the key its acquisition returned, never by the local name it was given — and the stamp is what a caller waiting for a peer to catch up compares against.

#### use std::sync::Arc;
#### use majordomus_cli::mesh::config::{CooperationConfig, TrustConfig};
#### use majordomus_cli::mesh::cooperation::{CheckoutFacts, Cooperation, CooperationSetup};
#### use majordomus_cli::mesh::identity::NodeIdentity;
#### use majordomus_cli::mesh::link::HttpTransport;
#### use majordomus_cli::mesh::registry::MeshRegistry;
#### use majordomus_cli::mesh::repository::of_root_commits;
#### let cooperation = Cooperation::new(CooperationSetup {
####     identity: Arc::new(NodeIdentity::ephemeral().unwrap()), runtime: "0000000000000001".into(),
####     repository: of_root_commits(&["root".into()]), endpoints: vec!["127.0.0.1:9".into()],
####     version: "doc".into(), config: CooperationConfig::default(), trust: TrustConfig::default(),
####     journal_path: None, registry: Arc::new(MeshRegistry::new()),
####     transport: Arc::new(HttpTransport), board: None, checkout: CheckoutFacts::default(),
#### }).unwrap();
use majordomus_cli::mesh::cooperation::Written;
use majordomus_cli::mesh::journal::{ClaimMode, Durability, SessionInfo};

let session = SessionInfo::named("s1", "cli");
let written: Written = cooperation
    .claim(&session, vec!["docs".into()], None, ClaimMode::Advisory, None)
    .unwrap();
assert!(written.lamport > 0, "placed against every other runtime's events");
assert_eq!(written.durability, Durability::Memory, "this journal has no file");

// and the key it returned is how the claim is addressed from here on
cooperation.release(&written.key).unwrap();
propertytyperequireddescription
key string yes <stream>/<local id> of what was created or changed.
event string yes <stream>/<seq> of the event that says so.
lamport integer (uint64) yes The event's Lamport stamp.
durability Durability → Durability no Whether the event reached this machine's disk: synced, memory (a journal with no file, by configuration), or not_durable — held and replicated to peers, not on disk here, and not acknowledged as durable. unknown from an executable that predates it.

Used by mesh.claim mesh.release mesh.handover.publish mesh.review.request mesh.review.answer mesh.review.withdraw mesh.session.open mesh.session.close ConsumeAnswer

Searches the titles and descriptions of every page this site publishes.