§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

  1. Generate a new Ed25519 key pair
  2. Add the new public key to JWKS with a new kid
  3. Start signing new messages with the new key (update key_id)
  4. Wait for the grace period (overlap_s, default 3600s = 1 hour)
  5. Set exp on the old key to now() (not a future time)
  6. Broadcast KEY_ROTATION message (type 27) to peers
  7. Retain old key in JWKS for at least replay_window_s * 2 after expiry for in-flight validation
Grace period is mandatory: During the overlap window, both the old and new keys are valid. This prevents disruption to active sessions that have cached the old key. Do not expire the old key before 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 StateVerification
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