Quickstart

From zero to your first RCAN message in 5 minutes.

01Install the SDK

# Install
pip install rcan
# Optional extras
pip install rcan[http]    # registry client (httpx)
pip install rcan[crypto]  # Ed25519 message signing
pip install rcan[all]     # everything

02Build a Robot URI

Every robot has a globally unique, resolvable address. No more "which unit was it?"

address.py
from rcan import RobotURI

uri = RobotURI.build(
    manufacturer="acme",
    model="robotarm",
    version="v2",
    device_id="unit-001",
)
print(uri)
# rcan://registry.rcan.dev/acme/robotarm/v2/unit-001

# Parse from a string
uri = RobotURI.parse("rcan://registry.rcan.dev/acme/robotarm/v2/unit-001")
print(uri.namespace)      # acme/robotarm
print(uri.registry_url)   # https://registry.rcan.dev/registry/...

03Gate on AI Confidence

The gate doesn't trust the model — that's the point. The model is right 99.99% of the time. The gate catches the 0.01%.

gate.py
from rcan import RCANMessage, ConfidenceGate

gate = ConfidenceGate(threshold=0.8)
confidence = 0.91  # from your AI model

if gate.allows(confidence):
    msg = RCANMessage(
        cmd="move_forward",
        target=uri,
        params={"distance_m": 1.0},
        confidence=confidence,
        model_identity="Qwen2.5-7B-Q4",
    )
    print(msg.to_json(indent=2))
else:
    print(f"Blocked: confidence {confidence} < threshold {gate.threshold}")

For high-stakes actions, add a HiTLGate to require explicit human approval. See §16 AI Accountability.

04Create a Tamper-Evident Audit Record

Every safety-critical action can be sealed into an HMAC-chained record — forensic-grade proof of what the system did, when, at what confidence.

audit.py
from rcan import CommitmentRecord
from rcan.audit import AuditChain

chain = AuditChain(secret="your-hmac-secret")

record = chain.append(CommitmentRecord(
    action="move_forward",
    robot_uri=str(uri),
    confidence=0.91,
    model_identity="Qwen2.5-7B-Q4",
    params={"distance_m": 1.0},
    safety_approved=True,
))

print(f"Sealed: {record.content_hash[:12]}...")
print(f"Chain valid: {chain.verify_all()}")  # True

# Export JSONL for long-term storage or audit
with open("audit.jsonl", "w") as f:
    f.write(chain.to_jsonl())

05Register Your Robot

Get a globally unique RRN (Robot Registration Number). Free.

New RRNs use 12-digit sequences (e.g. RRN-000000000001). Legacy 8-digit RRNs (RRN-000000000001) remain valid — the format is backward compatible.

Terminal
# If using OpenCastor — built into the setup wizard
castor wizard
# → Step 13: Register with rcan.dev? [Y/n]  →  ✅ RRN-000000000042

# Or standalone
castor register --config myrobot.rcan.yaml

05bFederated Resolution

Any client can resolve an RRN across the federation — even if registered at a manufacturer's authoritative node:

from rcan import NodeClient
client = NodeClient()
# Resolve an RRN from any node in the federation
robot = client.resolve("RRN-BD-000000000001")
print(f"Found: {robot['record']['name']} (resolved by {robot['resolved_by']})")
# Discover which node is authoritative for a namespace
node = client.discover("RRN-BD-000000000001")
print(f"Authoritative node: {node['operator']} at {node['api_base']}")
# List all known registry nodes
nodes = client.list_nodes()
for n in nodes:
    print(f"  {n['operator']}: {n['namespace_prefix']}")

06Invoke a Skill (§19)

Once your robot is connected, you can invoke named skills using the INVOKE message type (§19.2). The caller sends an INVOKE frame; the robot replies with INVOKE_RESULT (§19.3).

Python — §19 INVOKE
import asyncio
from rcan import RCANClient

async def main():
    async with RCANClient("wss://myrobot.local:8765") as client:
        # Invoke the pick_and_place skill with a 5 s timeout
        result = await client.invoke(
            skill="pick_and_place",
            params={"target": "red_cube"},
            timeout_ms=5000,
        )
        print(result.status)   # "success" | "failure" | "timeout" | "cancelled"
        print(result.result)   # skill-specific output dict

asyncio.run(main())
TypeScript — §19 INVOKE
import { RCANClient } from 'rcan-ts';

const client = new RCANClient('wss://myrobot.local:8765');
await client.connect();

const result = await client.invoke('pick_and_place', {
  params: { target: 'red_cube' },
  timeoutMs: 5000,
});
console.log(result.status);  // "success" | "failure" | "timeout" | "cancelled"
console.log(result.result);  // skill output

// Cancel an in-flight invocation (§19.4)
const invocation = client.invoke('navigate', { params: { goal: 'kitchen' } });
await client.cancelInvoke(invocation.msgId);

Wire values: INVOKE = 11, INVOKE_RESULT = 12, INVOKE_CANCEL = 15. See §19 full spec for error codes, cancellation semantics, and the Capability Advertisement integration.

07Emit Telemetry (§20)

RCAN robots report state via STATUS messages that carry a structured Robot State object (§20.3). Joint sensor readings go in the nested joints array (§20.2). All field names, units, and types are defined in the Telemetry Field Registry so consumers can interpret readings without out-of-band documentation.

Python — §20 STATUS telemetry
from rcan import RCANClient

async with RCANClient("wss://myrobot.local:8765") as client:
    await client.publish_status({
        "ruri": "rcan://myrobot.local",
        "battery_percent": 82.5,
        "battery_voltage": 12.4,
        "uptime_seconds": 3612,
        "mode": "autonomous",
        "joints": [
            {
                "joint_name": "shoulder_pan",
                "position_rad": 0.523,
                "velocity_rad_s": 0.0,
                "effort_nm": 1.2,
                "temperature_c": 38.1,
            }
        ],
    })

Extensible: The registry follows an open-world model — you can add custom fields alongside standard ones. Standard fields use the canonical names defined in §20 so dashboards and monitoring tools can process them without configuration.

08Validate Your Config

Terminal
# Check L1/L2/L3 conformance
rcan-validate config myrobot.rcan.yaml

# Expected output:
# ✅ L1 — Addressing + message format: passed
# ✅ L2 — Auth + confidence gates: passed
# ⚠️  L3 — hitl_gates not configured (§16)
# Result: L2 (1 warning)

# Or with OpenCastor
castor compliance --config myrobot.rcan.yaml

Once you have an RCAN robot, register it at robotregistryfoundation.org. Registration is free and gives your robot a globally unique RRN (Robot Registration Number), enabling federated identity resolution across the RCAN network.

What's next

Edit this page on GitHub Last updated: 5/3/2026