§8.6 — Key Lifecycle and Rotation v1.5
Earlier RCAN protocol drafts used a single, non-expiring signing key per operator. Subsequent revisions add JWKS-based key management with expiry, rotation, and a grace period so production fleets can rotate keys without dropping active sessions.
JWKS Endpoint
Robots and operators SHOULD maintain a JSON Web Key Set (JWKS) at:
/.well-known/rcan-keys.json The registry proxies each robot's JWKS at:
GET /api/v1/robots/{rrn}/.well-known/rcan-keys.json JWKS Format
{
"keys": [
{
"kty": "OKP",
"crv": "Ed25519",
"kid": "rcan-key-2026-03",
"use": "sig",
"key_ops": ["verify"],
"x": "base64url-encoded-public-key",
"exp": 1782000000, // REQUIRED: max 365 days from creation
"iat": 1750000000, // Key creation time
"revoked_at": null // Set when key is compromised
},
{
"kty": "OKP",
"crv": "Ed25519",
"kid": "rcan-key-2025-03",
"use": "sig",
"key_ops": ["verify"],
"x": "base64url-encoded-old-public-key",
"exp": 1750000000, // Past expiry
"iat": 1718000000,
"revoked_at": null
}
]
} key_id on Signed Messages
All signed RCAN messages MUST include a key_id field in the envelope identifying which key was used to sign:
{
"id": "uuid-v4",
"type": 1,
"key_id": "rcan-key-2026-03", // kid of the signing key
"signature": "ed25519:...",
...
} Receivers MUST look up the signing key by key_id in the sender's JWKS before verifying the signature. If the key_id is not found in the JWKS, reject with KEY_NOT_FOUND.
Key Rotation Procedure
- Generate a new Ed25519 key pair
- Add the new public key to JWKS with a new
kid - Start signing new messages with the new key (update
key_id) - Wait for the grace period (
overlap_s, default 3600s = 1 hour) - Set
expon the old key tonow()(not a future time) - Broadcast KEY_ROTATION message (type 27) to peers
- Retain old key in JWKS for at least
replay_window_s * 2after expiry for in-flight validation
overlap_s has elapsed.
KEY_ROTATION Message (Type 27)
{
"id": "uuid-v4",
"type": 27,
"source": "rcan://rcan.dev/org/robot/v1/unit-001",
"rcan_version": "1.5",
"qos": 1,
"payload": {
"new_kid": "rcan-key-2026-03",
"old_kid": "rcan-key-2025-03",
"overlap_s": 3600, // Grace period in seconds
"jwks_url": "/.well-known/rcan-keys.json"
}
} Peers receiving KEY_ROTATION MUST refresh their cached JWKS for the sender within 60 seconds.
Key Validity Rules
| Key State | Verification |
|---|---|
Active: iat ≤ now < exp, revoked_at: null | ✅ Accept |
Grace period: now ≤ exp + (replay_window_s * 2), revoked_at: null | ✅ Accept for in-flight messages only |
Expired: now > exp + (replay_window_s * 2) | ❌ Reject with KEY_EXPIRED |
Revoked: revoked_at != null | ❌ Reject immediately with KEY_REVOKED |
| Not in JWKS | ❌ Reject with KEY_NOT_FOUND |
Max key validity: 365 days. Compliance frameworks (SOC 2, ISO 27001) typically require ≤90-day rotation; set exp accordingly.
Registry Key History
The registry MUST store key history (not just the current key) for each registered robot. This enables:
- Validation of messages signed with recently-rotated keys
- Audit trail verification against historical signatures
- Key compromise investigation
Registry endpoint: GET /api/v1/robots/{rrn}/public-key returns the current active key. The JWKS endpoint returns the full history.
Configuration
security:
key_rotation:
overlap_s: 3600 # Grace period: both keys valid (default 1h)
max_validity_days: 365 # Hard maximum key lifetime
auto_rotate: false # Enable automatic rotation (requires key management setup) See Also
- Robot Identity Revocation — key revocation vs. robot identity revocation
- Replay Attack Prevention — replay_window_s used in key expiry calculation
- Offline Operation — key cache for offline authentication
- KEY_ROTATION (type 27)