cmn-substrate 0.3.0

CMN protocol core — Ed25519 signatures, BLAKE3 tree hashing, JSON schema validation, URI parsing, and JCS canonicalization. Zero I/O, WASM-compatible.
Documentation
{
  "$id": "https://cmn.dev/schemas/v1/cmn.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "CMN Domain Entry Point",
  "type": "object",
  "required": ["$schema", "capsules", "capsule_signature"],
  "properties": {
    "$schema": {
      "const": "https://cmn.dev/schemas/v1/cmn.json"
    },
    "capsules": {
      "type": "array",
      "minItems": 1,
      "items": { "$ref": "#/$defs/capsule_entry" },
      "description": "First entry is this host domain's own capsule; additional entries MAY describe mirrored origin domains."
    },
    "capsule_signature": {
      "$ref": "#/$defs/signature",
      "description": "Signature over the JCS-canonical capsules array, verified with capsules[0].key"
    }
  },
  "additionalProperties": false,
  "$defs": {
    "capsule_entry": {
      "type": "object",
      "required": ["uri", "serial", "key", "history", "endpoints"],
      "properties": {
        "uri": {
          "type": "string",
          "pattern": "^cmn://(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$",
          "description": "Domain URI of the capsule origin: cmn://{origin_domain}"
        },
        "serial": {
          "type": "integer",
          "minimum": 1,
          "description": "Monotonically increasing domain-state serial for anti-rollback pinning."
        },
        "key": {
          "type": "string",
          "pattern": "^ed25519\\.[1-9A-HJ-NP-Za-km-z]+$",
          "description": "Current Ed25519 public key of the entry's origin domain in {algorithm}.{base58} format"
        },
        "history": {
          "type": "array",
          "description": "Historical keys for rotation/revocation. Retired entries require an old-key signature authorizing replaced_by.",
          "items": { "$ref": "#/$defs/key_history_entry" }
        },
        "endpoints": {
          "$ref": "#/$defs/endpoints",
          "description": "Typed array of endpoint definitions. Taste-only domains provide only a taste endpoint."
        }
      },
      "additionalProperties": false
    },
    "domain": {
      "type": "string",
      "pattern": "^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$",
      "description": "Lowercase RFC1123 fully-qualified domain name"
    },
    "key_history_entry": {
      "type": "object",
      "required": ["key", "status", "retired_at_epoch_ms"],
      "properties": {
        "key": {
          "type": "string",
          "pattern": "^ed25519\\.[1-9A-HJ-NP-Za-km-z]+$",
          "description": "Historical Ed25519 public key in {algorithm}.{base58} format"
        },
        "status": {
          "type": "string",
          "enum": ["retired", "revoked"],
          "description": "retired = normal rotation with proof; revoked = compromised key"
        },
        "retired_at_epoch_ms": {
          "type": "integer",
          "minimum": 0,
          "description": "When this key left active use (milliseconds since Unix epoch)"
        },
        "replaced_by": {
          "type": "string",
          "pattern": "^ed25519\\.[1-9A-HJ-NP-Za-km-z]+$",
          "description": "Successor key authorized by this historical key"
        },
        "effective_serial": {
          "type": "integer",
          "minimum": 1,
          "description": "Domain-state serial at which this key rotation became effective. If omitted, verifiers use the containing capsule serial."
        },
        "rotation_signature": {
          "$ref": "#/$defs/signature",
          "description": "Signature by key over the cmn-key-rotation-v1 statement"
        },
        "revoked_at_epoch_ms": {
          "type": "integer",
          "minimum": 0,
          "description": "Required when status=revoked; signatures produced at or after this time must be rejected"
        }
      },
      "allOf": [
        {
          "if": {
            "properties": { "status": { "const": "retired" } },
            "required": ["status"]
          },
          "then": { "required": ["replaced_by", "rotation_signature"] }
        },
        {
          "if": {
            "properties": { "status": { "const": "revoked" } },
            "required": ["status"]
          },
          "then": { "required": ["revoked_at_epoch_ms"] }
        }
      ],
      "additionalProperties": false
    },
    "content_hash": {
      "type": "string",
      "pattern": "^[a-z0-9]+\\.[1-9A-HJ-NP-Za-km-z]+$",
      "description": "Content hash with algorithm prefix (e.g., b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2)"
    },
    "signature": {
      "type": "string",
      "pattern": "^[a-z0-9]+\\.[1-9A-HJ-NP-Za-km-z]+$",
      "description": "Signature format: {algorithm}.{base58}, e.g., ed25519.3yMR7vZQ9hL2xKJd..."
    },
    "archive_format": {
      "type": "string",
      "minLength": 1,
      "pattern": "^[a-z0-9][a-z0-9+._-]*$",
      "description": "Archive transport format identifier. Clients MUST select decoders by this field, not by filename suffix."
    },
    "endpoint_type": {
      "type": "string",
      "minLength": 1,
      "pattern": "^[a-z0-9][a-z0-9._-]*$",
      "description": "Endpoint type identifier (e.g., mycelium, spore, archive, taste)"
    },
    "endpoint_mycelium": {
      "type": "object",
      "required": ["type", "url", "hash"],
      "properties": {
        "type": { "const": "mycelium" },
        "url": {
          "type": "string",
          "pattern": "\\{hash\\}",
          "description": "Template URL for fetching mycelium, e.g. https://example.com/cmn/mycelium/{hash}.json"
        },
        "hash": {
          "$ref": "#/$defs/content_hash",
          "description": "Content hash of the primary mycelium manifest. Authoritative source for domain metadata (name, bio, nutrients) and featured spores. Used for change detection and display."
        },
        "hashes": {
          "type": "array",
          "items": { "$ref": "#/$defs/content_hash" },
          "description": "Optional additional mycelium shard hashes for large domains. Each points to an overflow shard whose spore list is merged for search and indexing. Metadata from these shards is ignored — only the primary hash is authoritative."
        }
      },
      "additionalProperties": false
    },
    "endpoint_spore": {
      "type": "object",
      "required": ["type", "url"],
      "properties": {
        "type": { "const": "spore" },
        "url": {
          "type": "string",
          "pattern": "\\{hash\\}",
          "description": "Template URL for fetching spores, e.g. https://example.com/cmn/spore/{hash}.json"
        }
      },
      "additionalProperties": false
    },
    "endpoint_archive": {
      "type": "object",
      "required": ["type", "url", "format"],
      "properties": {
        "type": { "const": "archive" },
        "url": {
          "type": "string",
          "pattern": "\\{hash\\}",
          "description": "Template URL for archives. MUST include {hash}, e.g. https://example.com/cmn/archive/{hash}.tar.zst"
        },
        "format": {
          "$ref": "#/$defs/archive_format",
          "description": "Archive format identifier, e.g. tar+zstd or tar+gzip"
        },
        "delta_url": {
          "type": "string",
          "pattern": "(?=.*\\{hash\\})(?=.*\\{old_hash\\})",
          "description": "Optional template URL for archive deltas. MUST include {hash} (target) and {old_hash} (cached base). Direction is old_hash -> hash."
        }
      },
      "additionalProperties": false
    },
    "endpoint_taste": {
      "type": "object",
      "required": ["type", "url"],
      "properties": {
        "type": { "const": "taste" },
        "url": {
          "type": "string",
          "pattern": "\\{hash\\}",
          "description": "Template URL for fetching taste reports, e.g. https://example.com/cmn/taste/{hash}.json"
        }
      },
      "additionalProperties": false
    },
    "endpoint_extension": {
      "type": "object",
      "required": ["type", "url"],
      "properties": {
        "type": {
          "allOf": [
            { "$ref": "#/$defs/endpoint_type" },
            { "not": { "enum": ["mycelium", "spore", "archive", "taste"] } }
          ],
          "description": "Extension endpoint type identifier"
        },
        "url": {
          "type": "string",
          "minLength": 1,
          "description": "Template URL for this endpoint type"
        }
      },
      "additionalProperties": true
    },
    "endpoint_entry": {
      "allOf": [
        { "not": { "required": ["protocol_version"] } },
        {
          "oneOf": [
            { "$ref": "#/$defs/endpoint_mycelium" },
            { "$ref": "#/$defs/endpoint_spore" },
            { "$ref": "#/$defs/endpoint_archive" },
            { "$ref": "#/$defs/endpoint_taste" },
            { "$ref": "#/$defs/endpoint_extension" }
          ]
        }
      ]
    },
    "endpoints": {
      "type": "array",
      "items": { "$ref": "#/$defs/endpoint_entry" },
      "description": "Typed array of endpoint definitions. Each entry has a 'type' field. Multiple archive entries with different formats are allowed. Clients SHOULD try entries in listed order."
    }
  }
}