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/spore.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "CMN Spore",
  "type": "object",
  "required": ["$schema", "capsule", "capsule_signature"],
  "properties": {
    "$schema": {
      "const": "https://cmn.dev/schemas/v1/spore.json"
    },
    "capsule": {
      "type": "object",
      "required": ["uri", "core", "core_signature", "dist"],
      "properties": {
        "uri": { "$ref": "#/$defs/cmn_uri" },
        "core": { "$ref": "#/$defs/spore_core_released" },
        "core_signature": { "$ref": "#/$defs/signature" },
        "dist": { "$ref": "#/$defs/dist_array" }
      },
      "additionalProperties": true
    },
    "capsule_signature": { "$ref": "#/$defs/signature" }
  },
  "additionalProperties": true,
  "$defs": {
    "cmn_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])?/[a-z0-9]+\\.[1-9A-HJ-NP-Za-km-z]+$",
      "description": "CMN URI format: cmn://{domain}/{algorithm}.{base58}"
    },
    "hash": {
      "type": "string",
      "pattern": "^[a-z0-9]+\\.[1-9A-HJ-NP-Za-km-z]+$",
      "description": "Hash format: {algorithm}.{base58}, 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..."
    },
    "spdx_expression_simple": {
      "type": "string",
      "minLength": 1,
      "pattern": "^[A-Za-z0-9-.+():]+(?:\\s+(?:AND|OR|WITH)\\s+[A-Za-z0-9-.+():]+)*$",
      "description": "SPDX license expression (basic pattern validation). Full SPDX parser validation is recommended."
    },
    "spore_core": {
      "type": "object",
      "required": ["name", "synopsis", "intent", "license", "tree"],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque publisher-defined identifier. Consumers MUST treat raw id as opaque and MUST NOT use it directly as a filesystem path, shell token, or URL path segment. If a local-safe derivation is unsafe or collides, implementations MUST fall back to the spore hash."
        },
        "version": {
          "type": "string",
          "description": "Human-readable version string (e.g., 1.0.0). Optional."
        },
        "name": { "type": "string", "minLength": 1, "description": "Human-readable display name" },
        "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])?$"
        },
        "key": {
          "type": "string",
          "pattern": "^ed25519\\.[1-9A-HJ-NP-Za-km-z]+$",
          "description": "Optional pre-filled author public key. If absent, the publishing implementation MUST populate it from the current signing domain identity during release. If present, it MUST match that identity."
        },
        "synopsis": { "type": "string", "description": "One-line summary — agent reads this alone and understands what the spore does" },
        "intent": { "type": "array", "items": { "type": "string" }, "description": "Multi-paragraph description of this spore's functionality and purpose — what it does, how it works, why it exists. Each array item is a paragraph. Permanent, not cleared on release." },
        "license": { "$ref": "#/$defs/spdx_expression_simple" },
        "mutations": { "type": "array", "items": { "type": "string" }, "default": [], "description": "What changed relative to the spawned_from parent — describes the mutations applied to derive this spore from its ancestor." },
        "size_bytes": false,
        "updated_at_epoch_ms": false,
        "bonds": {
          "type": "array",
          "items": { "$ref": "#/$defs/bond" }
        },
        "tree": { "$ref": "#/$defs/tree" }
      },
      "additionalProperties": true
    },
    "spore_core_released": {
      "description": "Published spore core — the full spore_core as it appears in a released spore.json. Adds domain, key, size_bytes, updated_at_epoch_ms as required fields with proper type definitions (these are forbidden in the draft spore.core.json).",
      "type": "object",
      "required": ["name", "domain", "key", "synopsis", "intent", "license", "size_bytes", "updated_at_epoch_ms", "tree"],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque publisher-defined identifier. Consumers MUST treat raw id as opaque and MUST NOT use it directly as a filesystem path, shell token, or URL path segment. If a local-safe derivation is unsafe or collides, implementations MUST fall back to the spore hash."
        },
        "version": {
          "type": "string",
          "description": "Human-readable version string (e.g., 1.0.0). Optional."
        },
        "name": { "type": "string", "minLength": 1, "description": "Human-readable display name" },
        "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])?$"
        },
        "key": {
          "type": "string",
          "pattern": "^ed25519\\.[1-9A-HJ-NP-Za-km-z]+$",
          "description": "Author's Ed25519 public key. Publishing implementations populate this from the current signing domain identity during release."
        },
        "synopsis": { "type": "string", "description": "One-line summary — agent reads this alone and understands what the spore does" },
        "intent": { "type": "array", "items": { "type": "string" }, "description": "Multi-paragraph description of this spore's functionality and purpose — what it does, how it works, why it exists. Each array item is a paragraph. Permanent, not cleared on release." },
        "license": { "$ref": "#/$defs/spdx_expression_simple" },
        "mutations": { "type": "array", "items": { "type": "string" }, "default": [], "description": "What changed relative to the spawned_from parent — describes the mutations applied to derive this spore from its ancestor." },
        "size_bytes": { "type": "integer", "minimum": 0, "description": "Total uncompressed source size in bytes — sum of all blob content sizes from tree hash computation. Populated by release tooling, not present in spore.core.json draft." },
        "updated_at_epoch_ms": { "type": "integer", "minimum": 0, "description": "Content update timestamp (milliseconds since Unix epoch). Recommended source: latest Git commit time for the source tree, with max file mtime as fallback." },
        "bonds": {
          "type": "array",
          "items": { "$ref": "#/$defs/bond" }
        },
        "tree": { "$ref": "#/$defs/tree" }
      },
      "additionalProperties": true
    },
    "bond": {
      "type": "object",
      "required": ["uri", "relation"],
      "properties": {
        "uri": { "$ref": "#/$defs/cmn_uri" },
        "relation": {
          "type": "string",
          "minLength": 1,
          "description": "Relationship type. Predefined: spawned_from, absorbed_from, depends_on, follows, extends. Custom types allowed."
        },
        "id": {
          "type": "string",
          "minLength": 1,
          "description": "Optional opaque alias for this bond. Consumers MUST NOT use raw id directly as a filesystem path, shell token, or URL path segment. If a local-safe derivation is unsafe or collides, implementations MUST fall back to the bond target hash."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "description": "Optional explanation of why this bond exists and what role it plays. Typically omitted for spawned_from (use mutations field instead)."
        },
        "with": {
          "type": "object",
          "description": "Bond-specific parameters whose schema is defined by the bonded spore's convention. Opaque to the CMN protocol."
        }
      },
      "additionalProperties": true
    },
    "tree": {
      "type": "object",
      "required": ["algorithm"],
      "properties": {
        "algorithm": { "type": "string", "description": "Tree hash algorithm (e.g., blob_tree_blake3_nfc)" },
        "exclude_names": { "type": "array", "items": { "type": "string" }, "description": "Directories or files to skip during tree hashing. SHOULD include .git and .cmn at minimum." },
        "follow_rules": { "type": "array", "items": { "type": "string" }, "description": "Paths to ignore-style rule files (e.g., .gitignore) honored during tree hashing." }
      },
      "additionalProperties": true
    },
    "dist_array": {
      "type": "array",
      "minItems": 1,
      "items": { "$ref": "#/$defs/dist_entry" }
    },
    "dist_type": {
      "type": "string",
      "minLength": 1,
      "pattern": "^[a-z0-9][a-z0-9._-]*$",
      "description": "Distribution protocol identifier"
    },
    "dist_archive": {
      "type": "object",
      "required": ["type"],
      "properties": {
        "type": { "const": "archive" },
        "filename": { "type": "string", "minLength": 1, "description": "Deprecated. Archive URL is now resolved via endpoints archive url template + spore hash. Ignored by clients." }
      },
      "additionalProperties": true
    },
    "dist_git": {
      "type": "object",
      "required": ["type", "url"],
      "properties": {
        "type": { "const": "git" },
        "url": { "type": "string", "minLength": 1, "description": "Git repository URL" },
        "ref": { "type": "string", "minLength": 1, "description": "Optional git ref (tag/branch/commit)" }
      },
      "additionalProperties": true
    },
    "dist_ipfs": {
      "type": "object",
      "required": ["type", "cid"],
      "properties": {
        "type": { "const": "ipfs" },
        "cid": { "type": "string", "minLength": 1, "description": "IPFS CID or ipfs:// URI" }
      },
      "additionalProperties": true
    },
    "dist_extension": {
      "type": "object",
      "required": ["type"],
      "properties": {
        "type": {
          "allOf": [
            { "$ref": "#/$defs/dist_type" },
            { "not": { "enum": ["archive", "git", "ipfs"] } }
          ],
          "description": "Extension distribution protocol identifier (e.g., s3, bittorrent, rsync)"
        }
      },
      "additionalProperties": true
    },
    "dist_entry": {
      "oneOf": [
        { "$ref": "#/$defs/dist_archive" },
        { "$ref": "#/$defs/dist_git" },
        { "$ref": "#/$defs/dist_ipfs" },
        { "$ref": "#/$defs/dist_extension" }
      ]
    }
  }
}