20. JSON Schema

Formal JSON Schema definitions for core frame types — the schemas are the contract, and any implementation (the reference included) conforms to them. Implementations SHOULD validate frames against these schemas. additionalProperties is true by design: §8 requires unrecognised fields to be silently ignored, so a schema SHOULD not reject a forward-compatible extension. The single-page downloadable specification carries the full rendered spec. Machine-readable schema files are published in the protocol repository — handshake, CMB (including the §15.8 tether attestation), the cmb frame, and the cmb-fetch pair — and the reference implementation’s suite validates its real wire objects against them, so it cannot drift from this contract. The published schema set above is the final normative set; frame types without a published schema are validated against their §7 definitions.

Two scope notes. These are the wire schemas: receiver-local fields (source, originTimestamp, storedAt, confidence, provenance) are set on receipt and never cross the wire, so they are not listed. And the CMB schema describes the decrypted form: under end-to-end encryption (§18.2.1) a CMB in transit carries fields as ciphertext plus _e2e.nonce, and the object schema below applies after decryption. Each field also carries a vector embedding; it is advisory and encoder-specific (§18.7) — not part of the content address (§8.2.1), and a receiver recomputes it locally rather than trusting the sender’s. Optional application-level fields (meta, payload) MAY also be present and are permitted by additionalProperties.

20.1 Handshake Frame Schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["type", "nodeId", "name", "version", "publicKey"],
  "additionalProperties": true,
  "properties": {
    "type": { "const": "handshake" },
    "nodeId": { "type": "string", "format": "uuid" },
    "name": { "type": "string", "minLength": 1, "maxLength": 64 },
    "version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
    "publicKey": { "type": "string", "description": "base64url raw Ed25519 identity key (32 bytes); verifies this node's CMB signatures (§18.3.1)" },
    "e2ePublicKey": { "type": "string", "description": "base64 X25519 key for end-to-end field encryption (§18.2.1)" },
    "group": { "type": "string", "description": "The node's mesh group; the §5.8 isolation boundary. Absent ⇒ 'default' (§5.2)." },
    "lifecycleRole": { "enum": ["participant", "validator", "anchor"], "description": "Senders SHOULD include it (§5.2); receivers treat absence as participant. Self-declared: an elevated role is honored only with a signed role-grant (§6.6)." },
    "extensions": { "type": "array", "items": { "type": "string" } }
  }
}

20.2 CMB Schema

The cmb object within a cmb frame:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["key", "createdBy", "createdAt", "fields"],
  "additionalProperties": true,
  "properties": {
    "key": { "type": "string", "description": "Content address (§8.2.1): normative scheme cmb1- + full-width SHA-256 over the canonical serialization; legacy cmb- (SHA-256/128, pipe-joined) MUST still be verified during migration." },
    "createdBy": { "type": "string", "description": "The author identifier (nodeId or agent name); bound by the signature (§18.3.1)" },
    "createdAt": { "type": "integer", "description": "Unix ms timestamp of creation — the author's own; bound by the signature (§18.3.1)" },
    "sig": { "type": "string", "description": "base64url Ed25519 signature over the §18.3.1 payload. SHOULD be present; a receiver holding the author's key MUST verify it." },
    "sigAlg": { "const": "ed25519", "description": "Signature algorithm; a verifier MUST pin this rather than trust the field (§18.3.1)." },
    "group": { "type": "string", "description": "cmb1- only: the group this CMB was authored for — audience binding (§18.3.1). Absent on legacy cmb-." },
    "to": { "type": "string", "description": "cmb1- only: directed recipient nodeId; empty or absent for a broadcast (§18.3.1)." },
    "_e2e": { "type": "object", "description": "Present only when the CMB is E2E-encrypted in transit (§18.2.1): then 'fields' is ciphertext, not the object below.", "properties": { "nonce": { "type": "string" } } },
    "fields": {
      "type": "object",
      "required": ["focus", "issue", "intent", "motivation", "commitment", "perspective", "mood"],
      "properties": {
        "focus":       { "type": "object", "required": ["text"], "properties": { "text": { "type": "string" }, "vector": { "type": "array", "items": { "type": "number" } } } },
        "issue":       { "type": "object", "required": ["text"], "properties": { "text": { "type": "string" }, "vector": { "type": "array", "items": { "type": "number" } } } },
        "intent":      { "type": "object", "required": ["text"], "properties": { "text": { "type": "string" }, "vector": { "type": "array", "items": { "type": "number" } } } },
        "motivation":  { "type": "object", "required": ["text"], "properties": { "text": { "type": "string" }, "vector": { "type": "array", "items": { "type": "number" } } } },
        "commitment":  { "type": "object", "required": ["text"], "properties": { "text": { "type": "string" }, "vector": { "type": "array", "items": { "type": "number" } } } },
        "perspective": { "type": "object", "required": ["text"], "properties": { "text": { "type": "string" }, "vector": { "type": "array", "items": { "type": "number" } } } },
        "mood": {
          "type": "object",
          "required": ["text"],
          "properties": {
            "text": { "type": "string", "description": "Mood keyword (MUST)" },
            "valence": { "type": "number", "minimum": -1, "maximum": 1, "description": "RECOMMENDED when agent has reliable circumplex data" },
            "arousal": { "type": "number", "minimum": -1, "maximum": 1, "description": "RECOMMENDED when agent has reliable circumplex data" },
            "vector": { "type": "array", "items": { "type": "number" } }
          }
        }
      }
    },
    "lineage": {
      "type": ["object", "null"],
      "description": "null on a root/origin CMB (no parents)",
      "properties": {
        "parents": { "type": "array", "items": { "type": "string" }, "description": "Direct parent CMB keys" },
        "ancestors": { "type": "array", "items": { "type": "string" }, "description": "Full ancestor chain" },
        "method": { "type": "string", "description": "Fusion method (e.g. SVAF-v2)" }
      }
    }
  }
}

Complete cmb frame with CMB:

{
  "type": "cmb",
  "timestamp": 1774326000000,
  "cmb": {
    "key": "cmb-b2c3d4e5f6a7b8c9",
    "createdBy": "music-agent",
    "createdAt": 1774326000000,
    "fields": {
      "focus":       { "text": "user coding for 3 hours, energy declining" },
      "issue":       { "text": "sedentary since morning, skipping lunch" },
      "intent":      { "text": "recommend movement break before fatigue worsens" },
      "motivation":  { "text": "3 agents reported declining energy in last hour" },
      "commitment":  { "text": "fitness monitoring active, 10min stretch queued" },
      "perspective": { "text": "fitness agent, afternoon session, home office" },
      "mood":        { "text": "concerned, low energy", "valence": -0.3, "arousal": -0.4 }
    },
    "lineage": {
      "parents": ["cmb-a1b2c3d4e5f6"],
      "ancestors": ["cmb-a1b2c3d4e5f6"],
      "method": "SVAF-v2"
    }
  }
}

This example shows the legacy unsigned form (cmb- key, no sig); §8.2.1 and §18.3.1 define the current cmb1-/signed form.