Message Type Reference
The RCAN protocol defines a canonical set of message types covering discovery, control, safety, invocation, registry operations, consent, fleet, and EU AI Act compliance. All messages share a common envelope; the type field selects semantics and routing behaviour. See the live compatibility matrix for the canonical message-type table for each revision.
- Type 9 (DISCOVER): Response payload gains
supported_transportsfield (§19 constrained transport negotiation). See §19. - TRAINING_DATA (type 36 in the current table): JSON-only binary in
payloaddeprecated. Image/video/audio MUST usemedia_chunks[]. See §5.4. - FEDERATION_SYNC: Full payload spec defined —
{source_registry, target_registry, sync_type, payload, signature}. See §18.
§3 — Envelope
Every RCAN message uses the same JSON envelope regardless of type:
{
"id": "uuid-v4", // msg_id — unique per message
"type": 3, // MessageType integer (canonical v1.5)
"priority": 1, // 0=LOW 1=NORMAL 2=HIGH 3=SAFETY
"source": "rcan://...", // Sender RURI
"target": "rcan://...", // Recipient RURI (wildcards allowed)
"payload": { ... }, // Type-specific data
"timestamp": 1741000000.0, // Unix epoch (seconds, required)
"ttl": 0, // Seconds until expiry (0 = no expiry)
"reply_to": null, // ID of message being replied to
"scope": ["control"], // Required RBAC scopes
"rcan_version": "3.2", // MUST be present; validated first
"qos": 1, // 0=fire-and-forget 1=at-least-once 2=exactly-once
"sender_type": "human", // "human"|"robot"|"cloud_function"|"system"
"key_id": "kid-abc123" // Optional: signing key identifier (GAP-09)
} §3.5 — Protocol Version Compatibility v1.5
RCAN version strings follow semantic versioning (MAJOR.MINOR). The rcan_version field MUST be the first field validated on any incoming message, before any other processing.
Compatibility Rules
| Situation | Action |
|---|---|
| Receiver MAJOR == Sender MAJOR, Receiver MINOR ≥ Sender MINOR | ✅ Accept — standard case |
| Receiver MAJOR == Sender MAJOR, Receiver MINOR < Sender MINOR | ✅ Accept — ignore unknown fields; apply defaults for missing required fields |
| Receiver MAJOR ≠ Sender MAJOR | ❌ Reject with VERSION_INCOMPATIBLE error |
rcan_version field missing | ⚠️ Accept with default "1.0"; log warning at NOTICE level |
Field Defaults for Missing v1.5 Fields
When a v1.5 receiver processes a v1.4 message missing new fields:
delegation_chainmissing → treat as no delegation (flat command)qosmissing → default to0(fire-and-forget)sender_typemissing → default to"human"key_idmissing → validate against current (only) key in trust set
Version Negotiation (DISCOVER / STATUS)
DISCOVER and STATUS messages MUST include rcan_version in their payload. Peers record each other's protocol version on first contact and apply the lower version's rules for the session.
// DISCOVER payload (v1.5)
{
"rcan_version": "3.2",
"capabilities": ["control", "safety", "training"],
"p66_conformance_pct": 87.0
} Canonical MessageType table
These are the integer values rcan-py and rcan-ts use today (types 1 to 45). The normative list is in the spec; if this page and the spec disagree, the spec wins. Values are listed for interop reference: use the named enum constants in code, not the integers.
| Type | Name | Section | Direction | Description |
|---|---|---|---|---|
1 | COMMAND | §3 | operator → robot | Motor, action, or behavior command. Requires control scope. |
2 | RESPONSE | §3 | robot → operator | Generic response to a prior message. reply_to links to original. |
3 | STATUS | §3 | robot → operator | Telemetry / state snapshot — position, battery, mode, health. |
4 | HEARTBEAT | §3 | bidirectional | Periodic liveness ping. No payload required. |
5 | CONFIG | §8 | operator → robot | Configuration update. See §9.2 CONFIG_UPDATE for required payload schema and authorization rules. |
6 | SAFETY | §6 | bidirectional | STOP / ESTOP / RESUME — safety-critical. Bypasses all queues. Cannot be overridden by AI. ESTOP MUST use QoS 2. |
7 | AUTH | §5 | bidirectional | Authentication: session token and expiry. See Authentication. |
8 | ERROR | §3 | bidirectional | Error response. Payload: {"code": "...", "detail": "..."}. See Error Codes. |
9 | DISCOVER | §4 | broadcast | mDNS / peer discovery probe. Responders reply with RURI, capabilities, rcan_version, and (v1.6) supported_transports. See §19 Transport Negotiation. |
10 | PENDING_AUTH | §5 | robot → operator | An AI-proposed command waiting for human authorization (§16.4 HiTL gate). |
11 | INVOKE | §19.2 | operator → robot | Skill or behavior invocation request. |
12 | INVOKE_RESULT | §19.3 | robot → operator | Result of an INVOKE. |
13 | INVOKE_CANCEL | §19.4 | operator → robot | Best-effort cancellation of an in-flight INVOKE. |
14 | REGISTRY_REGISTER | §21.4 | robot → registry | Register a robot with a registry. |
15 | REGISTRY_RESOLVE | §21.5 | any → registry | Resolve an RRN to its registry record. |
16 | TRANSPARENCY | §16 | robot → audit log | EU AI Act Art. 13 disclosure — robot announces AI nature, capabilities, operator identity to humans. Required for EU deployment from August 2026. |
17 | COMMAND_ACK | §3 | robot → operator | Acknowledgement for QoS ≥ 1. Must be sent within 500ms. Links via reply_to. |
18 | COMMAND_NACK | §3 | robot → operator | Negative acknowledgement — command received but rejected (reason in payload). See QoS. |
19 | ROBOT_REVOCATION | §13 | broadcast | Registry broadcast: robot identity revoked. Peers MUST invalidate cached keys and consent. See Revocation. |
20 | CONSENT_REQUEST | §11.2 | robot → robot | Request cross-robot access from target robot's owner. See Consent. |
21 | CONSENT_GRANT | §11.2 | owner → requester | Owner grants requested cross-robot access. Signed by owner JWT. |
22 | CONSENT_DENY | §11.2 | owner → requester | Owner denies access request. Signed by owner JWT. |
23 | FLEET_COMMAND | §15 | operator → fleet | Broadcast command to a robot group. See §15. Fleet ESTOP MUST reach ≤100 robots in 500ms. |
24 | SUBSCRIBE | §20 | any → robot | Subscribe to a telemetry stream (observer scope). See Observer Mode. |
25 | UNSUBSCRIBE | §20 | any → robot | Cancel an active telemetry subscription. |
26 | FAULT_REPORT | §16 | robot → operator | Structured robot fault report. Safety-affecting faults update Protocol 66 manifest. See §16. |
27 | KEY_ROTATION | §8.6 | broadcast | Announce new signing key and JWKS update. See Key Rotation. |
28 | COMMAND_COMMIT | §5.3 | bidirectional | Second phase of QoS 2 exactly-once delivery. Receiver sends after processing. See QoS. |
29 | SENSOR_DATA | §20 | robot → subscriber | Sensor readings (proximity, IMU, temperature, etc.). |
30 | TRAINING_CONSENT_REQUEST | §17 | operator → owner | Request consent for training data collection from a subject. See Training Consent. |
31 | TRAINING_CONSENT_GRANT | §17 | owner → requester | Subject grants training data consent. Signed by subject JWT. |
32 | TRAINING_CONSENT_DENY | §17 | owner → requester | Subject denies training data consent. |
33 | CONTRIBUTE_REQUEST | §credits | coordinator → robot | Idle-compute work unit offered to a robot. |
34 | CONTRIBUTE_RESULT | §credits | robot → coordinator | Result of a contributed work unit. |
35 | CONTRIBUTE_CANCEL | §credits | robot → coordinator | Robot withdraws from a work unit. |
36 | TRAINING_DATA | §3 | robot → data pipeline | Training data submission. MUST carry consent_token for biometric/audio/visual data. v1.6: JSON-only binary in payload is deprecated — use media_chunks[] for image/video/audio. See Training Consent, §5.4 Multi-Modal Payloads. |
37 | COMPETITION_ENTER | §competitions | robot → fleet | Competition entry. |
38 | COMPETITION_SCORE | §competitions | robot → fleet | Competition score. |
39 | SEASON_STANDING | §competitions | cloud → robot | Season standings. |
40 | PERSONAL_RESEARCH_RESULT | §competitions | robot → local gateway | Personal research result. |
41 | AUTHORITY_ACCESS | §authority | authority → robot | Audit request from an authority (EU AI Act Art. 16(j)). |
42 | AUTHORITY_RESPONSE | §authority | robot → authority | Audit data returned to the authority. |
43 | FIRMWARE_ATTESTATION | §firmware | robot → registry | Signed firmware manifest. See Firmware Attestation. |
44 | SBOM_UPDATE | §sbom | robot → registry | Updated CycloneDX SBOM. See SBOM. |
45 | AUTHORIZE | §16.4 | authorising principal → robot | Approves or denies a PENDING_AUTH. Owner role or higher. |
Priority Levels
| Value | Name | Behaviour |
|---|---|---|
0 | LOW | Processed after NORMAL; background telemetry. |
1 | NORMAL | Standard queue ordering. Default. |
2 | HIGH | Jumps ahead of NORMAL in dispatch queue. |
3 | SAFETY | Bypasses all queues (§6 Invariant 2). Exclusively for SAFETY (type 6) messages. |
§9.2 — CONFIG_UPDATE Wire Protocol v1.5
Gap: GAP-07 — MessageType CONFIG (5) existed with no payload schema. Any token holder could send arbitrary config payloads.
Required Payload Schema
All CONFIG (type 5) messages MUST include these fields:
{
"config_hash": "sha256:abc123...", // SHA-256 of payload_b64 decoded
"config_version": "1.2.0", // Semver of config being applied
"diff_only": false, // true = payload is a diff patch only
"payload_b64": "eyJ...", // Base64-encoded config JSON
"requires_restart": false, // Robot must restart to apply
"safety_overrides": false // true = modifies safety envelope params
} Authorization Rules
| Change Type | Minimum JWT Role | Notes |
|---|---|---|
| Standard config fields | control | Normal update flow |
safety_overrides: false | owner | Owner-only config changes |
safety_overrides: true | creator | Modifies safety envelope; creator role required |
Fields: brain.provider, safety.* | creator | Always require creator; safety_overrides ignored |
confidence_gate.*) are compile-time constants in the robot runtime. They CANNOT be modified via CONFIG_UPDATE regardless of JWT role. Any CONFIG payload containing these fields MUST be rejected.
Validation Sequence
- Validate
rcan_version(before any other field) - Verify JWT role meets minimum for the config change type
- Decode
payload_b64 - Compute SHA-256 of decoded bytes; compare to
config_hash - If hash mismatch → reject with
CONFIG_HASH_MISMATCHerror; write audit event - Check for forbidden fields (confidence gates); reject if found
- Store current config as rollback snapshot (retain for
rollback_grace_s, default 300s) - Apply config; write audit event with
config_hashandconfig_version
CONFIG_ROLLBACK
Send a CONFIG message with payload_b64: "ROLLBACK" and no config_hash to restore the previous snapshot. Requires same JWT role as the update being rolled back. Rollback is unavailable after rollback_grace_s has elapsed.
§15 — Fleet Broadcast / Group Commands v1.5 SHOULD
Gap: GAP-13 — No RCAN message type for fleet broadcast; sequential HTTP calls take O(n) time for fleet ESTOP.
FLEET_COMMAND Payload
{
"group_id": "floor-3-bots", // Fleet group identifier
"rrn_list": ["RRN-001", "RRN-002"], // Explicit member list (optional override)
"command": "ESTOP", // Command name
"params": {}, // Command parameters
"require_ack_all": true, // Wait for ACK from all members
"fleet_token": "eyJ..." // JWT with fleet claim listing target RRNs
} Addressing
- Local network: UDP multicast on
239.255.66.0/24port6600 - Fallback: TCP unicast to each robot in
rrn_list - Cloud: POST to
/api/v1/fleets/{fleet_id}/commandwhich fans out
Fleet ESTOP SLA
Fleet ESTOP MUST reach all members of a ≤100 robot fleet within 500ms on a local network. Implementations that cannot meet this SLA MUST fall back to individual unicast ESTOP messages in parallel.
Fleet JWT Claims
{
"sub": "human-uuid",
"aud": "rcan://fleet/floor-3-bots",
"fleet": ["RRN-000001", "RRN-000002"], // Robots this JWT authorizes
"scope": ["control"],
"exp": 1741003600
} Observer Mode v1.5 SHOULD
Gap: GAP-15 — No read-only streaming; observers had to poll individual status endpoints.
Observer JWT Scope
The observer scope is read-only and MUST NOT be combined with any control scope (control, safety, training) in the same JWT. Servers MUST reject any write operation from an observer-scoped token, regardless of payload.
SUBSCRIBE / UNSUBSCRIBE
// SUBSCRIBE payload
{
"stream": "status", // "status" | "telemetry" | "audit"
"filter": {"rrn": "RRN-001"} // Optional filter
}
// Server responds with COMMAND_ACK containing subscription ID
// Then delivers matching events as server-sent events or WebSocket frames Streaming Endpoints
GET /api/stream/status— robot status updates (SSE)GET /api/stream/telemetry— sensor data stream (SSE / WebSocket)GET /api/stream/audit— audit events (SSE, requires audit scope)
Observer connections are terminated when the JWT expires. The server MUST close the SSE connection and return HTTP 401.
§16 — Structured Fault Reporting v1.5 SHOULD
Gap: GAP-20 — Only a generic ERROR type existed; no fault taxonomy or safety manifest integration.
FAULT_REPORT Payload
{
"fault_code": "SENSOR_PROXIMITY_FAILURE", // See taxonomy below
"severity": "critical", // "info"|"warning"|"error"|"critical"
"subsystem": "sensor", // subsystem prefix
"description": "Left IR sensor not responding",
"affects_safety": true, // Degrades P66 invariant?
"safe_to_continue": false // Robot should halt?
} Standard Fault Code Taxonomy
| Prefix | Example Codes |
|---|---|
SENSOR_ | SENSOR_PROXIMITY_FAILURE, SENSOR_IMU_DRIFT, SENSOR_CAMERA_OFFLINE |
MOTOR_ | MOTOR_OVERCURRENT, MOTOR_ENCODER_FAULT, MOTOR_THERMAL |
BATTERY_ | BATTERY_CRITICAL, BATTERY_CELL_FAULT, BATTERY_CHARGING_FAULT |
NETWORK_ | NETWORK_REGISTRY_UNREACHABLE, NETWORK_PEER_TIMEOUT |
SAFETY_ | SAFETY_WATCHDOG_EXPIRED, SAFETY_ESTOP_STUCK |
AUTH_ | AUTH_KEY_EXPIRED, AUTH_TOKEN_REVOKED |
CONFIG_ | CONFIG_HASH_MISMATCH, CONFIG_VERSION_DOWNGRADE |
Safety Manifest Integration
Faults with affects_safety: true MUST update the Protocol 66 manifest with an active_faults list. The manifest's safe_to_continue aggregate is false if any active fault has safe_to_continue: false. Operators receive a push notification for severity: "critical" faults.
Migration from v1.4
msg.type == 1 (DISCOVER in old spec) must be updated. Always use named enum constants — MessageType.DISCOVER — never hardcoded integers.
Old spec integers that changed:
- DISCOVER: was
1→ now9 - STATUS: was
2→ now3 - COMMAND: was
3→ now1 - TRANSPARENCY: was
18→ now16 - HANDOFF: was
19(reserved) → removed (deferred to v1.6)
AUTHORIZE, which approves or denies a PENDING_AUTH (§16.4), is type 45. Older schemas carried 9, which is DISCOVER.
Python Usage
from rcan.message import MessageType, RCANMessage, SPEC_VERSION
# Create a COMMAND message (type 1)
msg = RCANMessage.command(
source="rcan://rcan.dev/acme/arm/v1/unit-001",
target="rcan://rcan.dev/acme/arm/v1/unit-001",
payload={"cmd": "move_forward", "speed": 0.5},
)
# SPEC_VERSION is the single source of truth — do not hardcode version strings
print(SPEC_VERSION) # "1.10" See Also
- Safety Conformance — ESTOP, Protocol 66, time sync, audit export
- QoS & Delivery Guarantees — retry semantics, ESTOP exactly-once
- Consent Wire Protocol — CONSENT_REQUEST / GRANT / DENY flows
- Replay Attack Prevention — msg_id seen-set, replay window
- Robot Identity Revocation — ROBOT_REVOCATION broadcast
- Command Delegation Chain — R2R command provenance
- Key Rotation — JWKS, key_id, grace period
- Offline Operation — cached keys, restricted mode
- Cloud Relay Identity — sender_type, cloud_provider
- Training Data Consent — EU AI Act Article 10
- Error Codes — full error code reference