Appearance
Node mesh HTTP API
This is the reference for the mesh surface under /v1/nodes/{id}/... and the two domain-wide mesh routes under /v1/keys/rotate and /v1/domains/{domainId}/mesh/topology. It maps each operation to its OpenAPI schema, the per-call ReBAC relation that gates it, the closed Problem.code taxonomy, and the deferred-wiring posture that produces 501 until the production composition root threads every collaborator. The wire-contract origin is api/openapi/plexsphere-v1.yaml; this doc is a map, not a duplicate contract — for the bounded-context narratives see ../../contexts/mesh/sse.md (signed event bus), ../../contexts/mesh/reconciliation-pull.md (canonical state snapshot), ../../contexts/mesh/reachability.md (heartbeat-driven projection), and ../../contexts/mesh/key-rotation.md (per-Peer mesh-key rotation lifecycle). For the cursor-paginated /v1/nodes listing surface (which carries the nodes tag, NOT mesh), see ../api/nodes.md.
Operations
| Method | Path | Operation ID | Auth | ReBAC gate | Notes |
|---|---|---|---|---|---|
| GET | /v1/nodes/{id}/events | GetNodeEvents | session / API token / OIDC | node#node-agent | SSE stream; Last-Event-ID resumes; 25-second keep-alive comment frame; X-Plexsphere-API-Version header pins the contract. |
| GET | /v1/nodes/{id}/state | GetNodeState | session / API token / OIDC | node#node-agent | Returns the canonical NodeStateSnapshot; reuses the SSE relation so any subscriber can also pull. |
| POST | /v1/nodes/{id}/heartbeat | PostNodeHeartbeat | NSK Bearer | the NSK is asserted to belong to the addressed Node | Advances the reachability state machine; returns accepted_at + reconcile + rotate_keys hints. |
| PUT | /v1/nodes/{id}/endpoint | PutNodeEndpoint | NSK Bearer | the NSK is asserted to belong to the addressed Node | Records a NAT-observed Peer endpoint; returns accepted_at + stale_after. 4 KiB body cap. |
| PUT | /v1/nodes/{id}/capabilities | PutNodeCapabilities | NSK Bearer | the NSK is asserted to belong to the addressed Node | Records the per-Node capability-manifest snapshot (binary_version, binary_checksum, optional ssh_host_key_fingerprint, optional declared hooks); the 200 carries accepted_at + fields_changed + host_key_changed. 32 KiB body cap. |
| POST | /v1/nodes/{id}/integrity-violations | PostNodeIntegrityViolations | NSK Bearer | the NSK is asserted to belong to the addressed Node | Ingests a batch of integrity-violation reports; returns 202 Accepted with accepted_at + violation_count. The recommended remediation action is reprovision. 32 KiB body cap. |
| GET | /v1/nodes/{id}/reachability | GetNodeReachability | session / API token / OIDC | node#node-agent | Reads the latest Reachability projection (healthy/stale/unreachable + last_heartbeat_at + changed_at). |
| POST | /v1/nodes/{id}/keys/rotate | PostNodeKeysRotate | session / API token / OIDC | node#node-operate | Operator-triggered: records a pending peer_key_rotation row and appends one rotate_keys outbox event. Idempotent — a second trigger against an already-pending rotation returns the existing handle. |
| GET | /v1/nodes/{id}/keys/rotate/preview | GetNodeKeysRotatePreview | session / API token / OIDC | node#node-operate | Non-mutating dry-run; returns the affected-edge listing and estimated_duration_seconds so a renderer can drive a confirmation dialog. NO event, NO peer_key_rotation row, NO audit beyond the read-side ReBAC decision. |
| POST | /v1/keys/rotate | PostKeysRotate | NSK Bearer | the NSK identifies the rotating Node; NO path {id} segment | Completion leg: plexd submits the freshly-generated Curve25519 public key; the handler updates nodes.public_key, retires the old PSK, wraps a fresh PSK, and flips the pending peer_key_rotation row to completed — all in one transaction. 4 KiB body cap. |
| GET | /v1/domains/{domainId}/mesh/topology | GetDomainMeshTopology | session / API token / OIDC | domain#domain-view | Read-side projection of the per-Domain peer graph — nodes and directed edges (mode = direct/relayed, optional relay_node_id). Mirrors the SSE peer-graph events one-for-one. |
All eleven operations may surface as 501 until the production composition root supplies their collaborators. The 501 carries a specific Problem.code per surface so log scrapers can alert on the deferred-wiring state without parsing the instance:
| Operation | 501 code | Missing collaborators |
|---|---|---|
| GetNodeEvents | signed_event_bus_not_provisioned | EventStream, NonceStore, SignatureVerifier, RelationChecker, NodeRepo |
| GetNodeState | signed_event_bus_not_provisioned | SnapshotProvider, RelationChecker, NodeRepo |
| PostNodeHeartbeat | heartbeat_not_provisioned | NSKValidator, ReachabilityRepo, Clock |
| PutNodeEndpoint | endpoint_not_provisioned | EndpointRecorder, NSKResolver, NodeRepo, PeerLookup |
| GetNodeReachability | reachability_not_provisioned | ReachabilityRepo, RelationChecker, NodeRepo |
| PostNodeKeysRotate | node_keys_rotate_not_provisioned | RotationRequester, RelationChecker |
| GetNodeKeysRotatePreview | node_keys_rotate_preview_not_provisioned | RotationPreview projection, RelationChecker |
| PostKeysRotate | keys_rotate_not_provisioned | RotationCompleter, NSKResolver, NodeRepo, PeerLookup |
| GetDomainMeshTopology | mesh_topology_not_provisioned | TopologyProjection, RelationChecker |
| PutNodeCapabilities | capabilities_not_provisioned | CapabilitiesRecorder, NSKResolver, NodeRepo |
| PostNodeIntegrityViolations | integrity_violations_not_provisioned | IntegrityViolationsRecorder, NSKResolver, NodeRepo |
The roadmap is tracked in ../../architecture/mesh-event-bus-roadmap.md; the kind dev wires every collaborator and never returns the 501.
Authentication artefacts
The eleven operations split across two distinct auth chains:
Read and operator-action surfaces (
events,state,reachability,keys/rotatetrigger + preview, meshtopology) — accept the same first-success-wins triple as the rest of/v1:psk_…API token Bearer, OIDC JWT Bearer, orplexsphere_sessioncookie. The relevant ReBAC relation gates access (node-agentfor the read surfaces,node-operatefor the rotation trigger and its preview,domain-viewfor the topology snapshot); missing relation →403, unknown id →404only after the authz gate passes (no id oracle).NSK-authenticated surfaces (
POST .../heartbeat,PUT .../endpoint,PUT .../capabilities,POST .../integrity-violations,POST /v1/keys/rotate) — use the per-Node Node Secret Key (NSK) plaintext as a Bearer credential. The heartbeat and endpoint handlers:- Authenticate the caller against the NSK.
- Assert that the NSK belongs to the Node addressed by the path
id. A leaked NSK from a sibling Node surfaces as403 node_id_mismatchso it cannot be replayed across Nodes. - Validate the body —
PostNodeHeartbeatrequiresclient_nowwithin 60s of server now,binary_checksumdecoding to a 32-byte SHA-256, andbinary_versionnon-empty;PutNodeEndpointrequiresreported_atwithin 60s of server now andendpointparsing as a non-loopbackhost:porttuple in the 1..65535 port range. - Persist the observation — the heartbeat advances the reachability state machine, the endpoint observation stamps the Peer aggregate.
PUT .../capabilitiesandPOST .../integrity-violationsfollow the identical contract: they authenticate the NSK, run the same defence-in-depth path-id double-check (a sibling Node's NSK surfaces as403 forbidden), validate the body, then persist. A missing or malformed NSK on either surface is401 unauthorized.POST /v1/keys/rotatecarries NO path{id}segment — the rotating Node is identified solely by the NSK envelope it presents, so a missing or unresolved NSK surfaces as401without leaking a Node id either side of the boundary.
The NSK was issued (exactly once) by POST /v1/register — see ../api/bootstrap-tokens.md.
SSE stream contract (GetNodeEvents)
Each event frame on /v1/nodes/{id}/events carries:
| Field | Value |
|---|---|
id: | JetStream stream sequence number for the envelope (numeric, monotonic). |
event: | Discriminator — currently node_state_updated; the README's 14-type taxonomy lands incrementally without breaking the wire. |
data: | The canonical signed envelope JSON produced by internal/signing/envelope.CanonicalBytes. The trailing signature: field is an ed25519 signature over the canonical bytes; consumers SHOULD verify it as a defence-in-depth measure even though the server has already verified it before emitting the frame. |
Resume protocol: re-connect with Last-Event-ID: <numeric> set to the last sequence the client durably processed; the server replays from > last. Absent or empty header → tail from now (no historical backfill). A non-numeric Last-Event-ID is 400 invalid_last_event_id and the stream is NOT opened.
The server emits a :keep-alive\n\n SSE comment-frame every 25 seconds so idle proxies do not collapse the connection. Cache-Control: no-cache opts the response out of any intermediary caching layer.
Reconciliation snapshot contract (GetNodeState)
NodeStateSnapshot always carries four wire blocks — peers, policy, bridge, state/reports — even when the latter three are empty. plexd's reconcile loop diffs by field presence rather than absence, so later stories populate the empty blocks without changing the wire shape.
The peer projection is a single SQL round-trip ordered by node_id ASC so two consecutive pulls against the same ledger snapshot are byte-equal — plexd reduces redundant rewrites by hashing the response. The addressed Node itself is excluded from peers so plexd never programs a self-peer.
Reachability state machine
PostNodeHeartbeat advances the per-Node state machine the projection at GET /v1/nodes/{id}/reachability reflects:
| State | Transition | Trigger |
|---|---|---|
healthy | → stale | 90s without a heartbeat |
stale | → unreachable | 300s without a heartbeat (cumulative) |
stale / unreachable | → healthy | accepted heartbeat |
The Reachability response carries state, last_heartbeat_at (null until the first heartbeat), and changed_at (always present, tracks the most recent transition).
Heartbeat hints
The 200 response on PostNodeHeartbeat carries two reconciliation flags both defaulting to false:
reconcile: true— the controller wants plexd to issue a freshGET /v1/nodes/{id}/statebecause something has drifted.rotate_keys: true— the NSK is nearing expiry or has been administratively flagged; plexd should re-register through the bootstrap-token surface.
Until later stories flip them, plexd treats both as no-ops.
Endpoint observation contract (PutNodeEndpoint)
PUT /v1/nodes/{id}/endpoint accepts a NAT-observed Peer endpoint from plexd and stamps it onto the Peer aggregate backing the addressed Node. The handler caps the request body at 4 KiB before the JSON decoder runs — an over-cap body surfaces as 413 endpoint_body_too_large. The EndpointRequest body carries:
endpoint— ahost:porttuple. The host must be a routable (non-loopback) IPv4/IPv6 address and the port must fall in the RFC 60561..65535range; otherwise400 endpoint_unparseable.nat_type— the NAT classification plexd observed.reported_at— the agent-side observation timestamp. It must be within 60s of server now and no older than the per-Domain endpoint TTL window; both refusals share400 endpoint_clock_skew(the audit-rowreasondisambiguates the drift arm from the stale arm).
The persistence write emits a peer_endpoint_changed outbox event when the (endpoint, port) tuple differs from the prior observation or transitions out of the stale window; an identical refresh only advances the freshness timestamp without emitting a wake-up. The 200 response carries accepted_at (server commit timestamp) and stale_after (accepted_at plus the per-Domain endpoint TTL); plexd schedules its next observation before stale_after so the sweeper never tombstones a live endpoint.
Mesh-key rotation (PostNodeKeysRotate, GetNodeKeysRotatePreview, PostKeysRotate)
A mesh-key rotation is a three-leg flow that lets an operator replace a Node's Curve25519 keypair without re-enrolling from scratch:
- Trigger —
POST /v1/nodes/{id}/keys/rotaterecords a pendingpeer_key_rotationrow and appends onerotate_keysoutbox event in a single transaction. Re-trigger against an already-pending rotation returns the existing handle withalready_pending: trueand appends no second event; the operation is idempotent. - Preview (optional) —
GET /v1/nodes/{id}/keys/rotate/previewis a non-mutating dry-run that returns the samepeer_id/already_pendingview the mutating trigger would, plus the list of affected peer edges and anestimated_duration_seconds. The dashboard's manual-rotation confirmation dialog andplexctl key rotate --previewconsume this payload. NO event, NOpeer_key_rotationrow, and NO audit row beyond the read-side ReBAC decision are emitted. - Completion — plexd, on receiving the
rotate_keyscommand via its SSE event stream, generates a fresh Curve25519 keypair and submits the new public key toPOST /v1/keys/rotate(NSK-Bearer authenticated, no path id). The handler updatesnodes.public_key, retires the old pairwise PSK, wraps and inserts a fresh PSK, appends apeer_key_rotatedoutbox event, and flips the pendingpeer_key_rotationrow tocompleted— all in one transaction.
The 200 body of PostKeysRotate carries the rotation id plus the (kid, wrap_key_version) reference of the re-issued PSK; it never carries PSK plaintext or ciphertext. An idempotent retry with the same key against an already-completed rotation returns the prior receipt and emits no second event.
For the per-Peer state machine and the SSE event roster the rotation emits, see ../../contexts/mesh/key-rotation.md.
Mesh-topology snapshot (GetDomainMeshTopology)
GET /v1/domains/{domainId}/mesh/topology returns the read-side projection of the per-Domain peer graph that backs the dashboard mesh-map view and the plexctl mesh topology CLI. nodes carries each anchored Node's mesh-IP and reachability; edges carries each pairwise relationship with its mode (direct or relayed), the handshake age, and the bridge Node currently serving as the relay fallback when one is assigned. The payload mirrors the SSE peer-graph events one-for-one so a renderer that consumes the live stream and a renderer that polls this pull converge on byte-identical state.
Capability manifest (PutNodeCapabilities)
PUT /v1/nodes/{id}/capabilities records the per-Node capability manifest plexd reports after it starts: the running binary_version, the binary_checksum (a 32-byte SHA-256), an optional ssh_host_key_fingerprint, and an optional list of declared_hooks. The handler caps the body at 32 KiB before the JSON decoder runs — an over-cap body surfaces as 413 capabilities_body_too_large. Each hook entry carries a name and a checksum; duplicate names (declared_hook_duplicate) and an over-long list (declared_hooks_too_many) are refused. The 200 response carries accepted_at, the fields_changed set, and a host_key_changed boolean so a renderer can highlight a rotated SSH host key without diffing the full manifest. The recorded manifest feeds the policy context's capability and integrity surfaces — see ../../contexts/policy/capabilities.md.
Integrity violations (PostNodeIntegrityViolations)
POST /v1/nodes/{id}/integrity-violations ingests a batch of integrity-violation reports plexd raises when an artifact's observed checksum or SSH host-key fingerprint diverges from the manifest the Node declared. The body carries a non-empty violations array (an empty array is 400 integrity_violations_empty; an over-long batch is 400 integrity_violations_too_many); each entry names a kind, the detected_by detector, the offending artifact_id, and a kind-specific checksum or host-key fingerprint. The handler caps the body at 32 KiB (413 integrity_violations_body_too_large). On success it returns 202 Accepted with accepted_at and violation_count — acceptance is asynchronous, and the recommended remediation action is always reprovision. The ingested violations drive the policy context's integrity-alert pipeline — see ../../contexts/policy/integrity.md.
Path & query parameters
| Operation | Parameter | Type | Required | Notes |
|---|---|---|---|---|
every /v1/nodes/{id}/… operation | id (path) | string (uuid) | yes | Node identifier (UUIDv7). Malformed → 400 invalid_node_id. |
| GetDomainMeshTopology | domainId (path) | string (uuid) | yes | Domain identifier (UUIDv7). |
| GetNodeEvents | Last-Event-ID (header) | string | no | SSE replay cursor; non-numeric → 400 invalid_last_event_id. |
| PostNodeHeartbeat / PutNodeEndpoint / PutNodeCapabilities / PostNodeIntegrityViolations / PostKeysRotate | Authorization (header) | string | yes | Bearer <NSK plaintext>. |
Schemas
The OpenAPI spec is the authoritative source for field shapes. The schemas this surface uses are:
- State pull:
NodeStateSnapshot(with sub-schemas for the four wire blocks). - Heartbeat:
HeartbeatRequest(client_now,binary_checksum,binary_version,nat_summary),HeartbeatResponse(accepted_at,reconcile,rotate_keys). - Endpoint:
EndpointRequest(endpoint,nat_type,reported_at),EndpointResponse(accepted_at,stale_after). - Reachability:
Reachability(state +last_heartbeat_at+changed_at). - Key rotation:
KeysRotateRequest(new_public_key),KeysRotateResponse(rotation_id,kid,wrap_key_version),RotationTriggerResponse(rotation_id,peer_id,already_pending),RotationImpactPreview(node_id,peer_id,affected_peer_count,affected_edges,already_pending,estimated_duration_seconds). - Mesh topology:
MeshTopology(domain_id,generated_at,nodes,edges) with sub-schemas forMeshTopologyNodeandMeshTopologyEdge. - Capability manifest:
CapabilityManifestRequest(binary_version,binary_checksum, optionalssh_host_key_fingerprint, optionaldeclared_hooks),CapabilityManifestResponse(accepted_at,fields_changed,host_key_changed). - Integrity violations:
IntegrityViolationsRequest(violationsarray — each carrieskind,detected_by,artifact_id, and a kind-specific checksum or host-key fingerprint),IntegrityViolationsResponse(accepted_at,violation_count). - SSE envelope: the wire body is
text/event-stream; thedata:payload is the canonical JSON produced byinternal/signing/envelope.CanonicalBytes. The OpenAPI document cannot describe the framed event-stream encoding directly.
Error taxonomy
All error responses (other than the SSE text/event-stream success body) use the shared Problem envelope (application/problem+json). The 403 path on PostNodeHeartbeat uses the PermissionDenied shape; on the read surfaces it is a plain Problem with code = insufficient_relation.
| Code | Status | Where | Meaning |
|---|---|---|---|
invalid_node_id | 400 | every operation | Malformed Node UUID. |
invalid_last_event_id | 400 | GetNodeEvents | Non-numeric or negative Last-Event-ID header. |
clock_skew | 400 | PostNodeHeartbeat | client_now drifts more than 60s from server now. |
binary_checksum_empty | 400 | PostNodeHeartbeat | binary_checksum missing or not a 32-byte SHA-256. |
binary_version_empty | 400 | PostNodeHeartbeat | binary_version missing or whitespace-only after trim. |
malformed_heartbeat_request | 400 | PostNodeHeartbeat | Body cannot be decoded as a HeartbeatRequest envelope. |
endpoint_clock_skew | 400 | PutNodeEndpoint | reported_at drifts more than 60s from server now, or is older than the per-Domain endpoint TTL window. |
endpoint_unparseable | 400 | PutNodeEndpoint | endpoint is not a valid host:port, the port is outside 1..65535, or the host is not a routable IPv4/IPv6 address. |
malformed_endpoint_request | 400 | PutNodeEndpoint | Body cannot be decoded as an EndpointRequest envelope (invalid JSON, unknown field, missing required field). |
nsk_revoked | 401 | PostNodeHeartbeat / PutNodeEndpoint | NSK in the Authorization: Bearer header is missing, malformed, or has been revoked. |
insufficient_relation | 403 | read surfaces | Caller lacks node#node-agent. |
node_id_mismatch | 403 | PostNodeHeartbeat / PutNodeEndpoint | NSK authenticates but belongs to a different Node — replay defence. |
node_not_found | 404 | read surfaces / PostNodeHeartbeat | Node id not resolved (post-authz). |
endpoint_peer_not_found | 404 | PutNodeEndpoint | No live Peer row resolves for the authenticated Node (drained or never bound to a Peer). |
endpoint_peer_gone | 410 | PutNodeEndpoint | The target Peer was deregistered between the admission gates and the recorder's UPDATE — a narrow race. |
endpoint_body_too_large | 413 | PutNodeEndpoint | Request body exceeded the 4 KiB endpoint envelope cap. |
malformed_keys_rotate_request | 400 | PostKeysRotate | Body cannot be decoded as a KeysRotateRequest envelope. |
keys_rotate_public_key_invalid | 422 | PostKeysRotate | new_public_key does not decode to exactly 32 bytes, or is the all-zero degenerate value. |
keys_rotate_peer_not_found | 404 | PostKeysRotate | No live Peer row resolves for the authenticated Node (drained or never bound to a Peer). |
keys_rotate_no_pending_rotation | 409 | PostKeysRotate | No pending peer_key_rotation row exists for the authenticated Node — the submission has no rotation to complete. |
keys_rotate_body_too_large | 413 | PostKeysRotate | Request body exceeded the 4 KiB rotation envelope cap. |
insufficient_relation | 403 | PostNodeKeysRotate / GetNodeKeysRotatePreview / GetDomainMeshTopology | Caller lacks node-operate (rotation trigger / preview) or domain-view (topology). Surfaced before the existence check. |
node_not_found | 404 | PostNodeKeysRotate / GetNodeKeysRotatePreview | Node id does not match any Node in this Domain — post-authz only. |
domain_not_found | 404 | GetDomainMeshTopology | Domain id does not match any Domain — post-authz only. |
signed_event_bus_not_provisioned | 501 | GetNodeEvents / GetNodeState | EventStream / NonceStore / SignatureVerifier / RelationChecker / NodeRepo / SnapshotProvider not wired in this build. |
heartbeat_not_provisioned | 501 | PostNodeHeartbeat | NSKValidator / ReachabilityRepo / Clock not wired. |
endpoint_not_provisioned | 501 | PutNodeEndpoint | EndpointRecorder / NSKResolver / NodeRepo / PeerLookup not wired. |
reachability_not_provisioned | 501 | GetNodeReachability | ReachabilityRepo / RelationChecker / NodeRepo not wired. |
keys_rotate_not_provisioned | 501 | PostKeysRotate | RotationCompleter / NSKResolver / NodeRepo / PeerLookup not wired. |
node_keys_rotate_not_provisioned | 501 | PostNodeKeysRotate | RotationRequester / RelationChecker not wired. |
node_keys_rotate_preview_not_provisioned | 501 | GetNodeKeysRotatePreview | RotationPreview projection / RelationChecker not wired. |
mesh_topology_not_provisioned | 501 | GetDomainMeshTopology | TopologyProjection / RelationChecker not wired. |
unauthorized | 401 | PutNodeCapabilities / PostNodeIntegrityViolations | NSK in the Authorization: Bearer header is missing, malformed, or revoked. |
forbidden | 403 | PutNodeCapabilities / PostNodeIntegrityViolations | NSK authenticates but belongs to a different Node — the path-id double-check failed. |
malformed_capabilities_request | 400 | PutNodeCapabilities | Body cannot be decoded as a CapabilityManifestRequest envelope. |
binary_checksum_invalid | 400 | PutNodeCapabilities | binary_checksum is missing or does not decode to a 32-byte SHA-256. |
ssh_host_key_fingerprint_invalid | 400 | PutNodeCapabilities | ssh_host_key_fingerprint is present but malformed. |
declared_hook_invalid | 400 | PutNodeCapabilities | A declared-hook entry is malformed (empty name or bad checksum). |
declared_hook_duplicate | 400 | PutNodeCapabilities | Two declared hooks share a name. |
declared_hooks_too_many | 400 | PutNodeCapabilities | The declared-hook list exceeds the per-manifest maximum. |
capabilities_node_not_found | 404 | PutNodeCapabilities | Node id not resolved (post-authn). |
capabilities_body_too_large | 413 | PutNodeCapabilities | Request body exceeded the 32 KiB capabilities envelope cap. |
capabilities_not_provisioned | 501 | PutNodeCapabilities | CapabilitiesRecorder / NSKResolver / NodeRepo not wired. |
malformed_integrity_violations_request | 400 | PostNodeIntegrityViolations | Body cannot be decoded as an IntegrityViolationsRequest envelope. |
integrity_violations_empty | 400 | PostNodeIntegrityViolations | The violations array is empty. |
integrity_violations_too_many | 400 | PostNodeIntegrityViolations | The violations array exceeds the per-batch maximum. |
integrity_violation_kind_invalid | 400 | PostNodeIntegrityViolations | A violation kind is not a recognised value. |
integrity_violation_kind_mismatch | 400 | PostNodeIntegrityViolations | A violation's payload does not match its declared kind. |
integrity_violation_detected_by_invalid | 400 | PostNodeIntegrityViolations | A violation detected_by is not a recognised detector. |
integrity_violation_artifact_id_empty | 400 | PostNodeIntegrityViolations | A violation is missing its artifact_id. |
integrity_violation_checksum_invalid | 400 | PostNodeIntegrityViolations | A violation checksum is malformed. |
integrity_violation_host_key_fingerprint_invalid | 400 | PostNodeIntegrityViolations | A violation host-key fingerprint is malformed. |
integrity_violations_node_not_found | 404 | PostNodeIntegrityViolations | Node id not resolved (post-authn). |
integrity_violations_body_too_large | 413 | PostNodeIntegrityViolations | Request body exceeded the 32 KiB integrity-violations envelope cap. |
integrity_violations_not_provisioned | 501 | PostNodeIntegrityViolations | IntegrityViolationsRecorder / NSKResolver / NodeRepo not wired. |
internal | 500 | every operation | Server-side failure path. |
Cross-references
../../contexts/mesh/sse.md— bounded-context reference for the signed event bus, the canonical envelope shape, and thesignatureed25519 contract.../../contexts/mesh/reconciliation-pull.md— the cold-start reconciliation pull thatGetNodeStateexposes.../../contexts/mesh/reachability.md— the heartbeat → reachability state machine narrative.../../architecture/mesh-event-bus-roadmap.md— the deferred-wiring tracking that drives the 501 posture on the eleven mesh surfaces.../api/nodes.md— the cursor-paginated/v1/nodeslisting surface (nodestag, notmesh).../api/bootstrap-tokens.md—POST /v1/registerissues the NSK thatPostNodeHeartbeatconsumes.../../how-to/mesh/operate-reachability.md— operator guide for the reachability projection.../../how-to/mesh/inspect-the-event-bus.md— operator guide for inspecting the signed event bus.../../contexts/mesh/key-rotation.md— per-Peer mesh-key rotation lifecycle and the SSE event roster the trigger and completion legs emit.../../contexts/policy/capabilities.md— the capability-manifest model thatPutNodeCapabilitiesrecords and the declared-hook vocabulary.../../contexts/policy/integrity.md— the integrity-alert pipeline thatPostNodeIntegrityViolationsfeeds.../../../api/openapi/plexsphere-v1.yaml— OpenAPI 3.1 spec; theGetNodeEvents/GetNodeState/PostNodeHeartbeat/PutNodeEndpoint/PutNodeCapabilities/PostNodeIntegrityViolations/GetNodeReachability/PostNodeKeysRotate/GetNodeKeysRotatePreview/PostKeysRotate/GetDomainMeshTopologyoperations and theirNodeStateSnapshot/HeartbeatRequest/HeartbeatResponse/EndpointRequest/EndpointResponse/CapabilityManifestRequest/CapabilityManifestResponse/IntegrityViolationsRequest/IntegrityViolationsResponse/Reachability/KeysRotateRequest/KeysRotateResponse/RotationTriggerResponse/RotationImpactPreview/MeshTopologyschemas.