The Mycelium is a Site Descriptor. It represents a developer or organization and their published spores.
1. Overview
The domain entry point (cmn.json) is documented in 01-substrate.md. This document covers the full mycelium manifest that contains the complete site metadata.
Location: Defined by the type: "mycelium" endpoint in cmn.json (e.g., https://cmn.dev/cmn/mycelium/{hash}.json)
Schema: https://cmn.dev/schemas/v1/mycelium.json (hostname non-authoritative — see 01-substrate §6)
Size: ~1-10 KB
{
"$schema": "https://cmn.dev/schemas/v1/mycelium.json",
"capsule": {
"uri": "cmn://cmn.dev/mycelium/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"core": {
"domain": "cmn.dev",
"key": "ed25519.5XmkQ9vZP8nL3xJdFtR7wNcA6sY2bKgU1eH9pXb4",
"name": "cmn.dev",
"synopsis": "",
"updated_at_epoch_ms": 1769777183174,
"spores": [
{
"id": "cmn-spec",
"hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"name": "CMN Protocol Specification",
"synopsis": "Code Mycelial Network - A sovereign-first protocol for code distribution"
},
{
"id": "cmn-tools",
"hash": "b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5",
"name": "CMN Tools",
"synopsis": "Command-line and service tooling for the CMN protocol"
}
]
},
"core_signature": "ed25519.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa23yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2"
},
"capsule_signature": "ed25519.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa23yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2"
}
Note: Content endpoints (spore, archive, taste) are defined in the cmn.json endpoint array, not in the mycelium manifest. See 01-substrate §1.2.1.
2. Field Definitions
2.1 Mycelium Fields
| Field | Type | Description |
|---|---|---|
$schema | String | https://cmn.dev/schemas/v1/mycelium.json (hostname non-authoritative — see 01-substrate §6). |
capsule | Object | The capsule container. |
capsule.uri | String | Mycelium URI: cmn://{domain}/mycelium/{hash} |
capsule.core | Object | The mycelium content. |
capsule.core.name | String | Developer or organization name. |
capsule.core.domain | String | The domain (e.g., cmn.dev). |
capsule.core.key | String | Ed25519 public key of the content author. Enables offline signature verification once the key-domain binding is already trusted, without fetching cmn.json. |
capsule.core.synopsis | String | Brief description of the developer/org. |
capsule.core.bio | String? | Multiline markdown with full details about this domain. |
capsule.core.nutrients | Array? | Nutrient methods, ordered by preference (first = preferred). Omit if none. See §2.3. |
capsule.core.updated_at_epoch_ms | Number | Unix timestamp in milliseconds. Used for version ordering. |
capsule.core.spores | Array? | List of published spores. Omit if the domain has not published any spores yet. |
capsule.core.tastes | Array? | Published taste reports ({hash, target_uri}) for mirror discovery and re-submission. Omit if none. |
capsule.core_signature | String | Ed25519 signature of the core object (ed25519.<base58>, JCS canonical). |
capsule_signature | String | Ed25519 signature of the capsule object (ed25519.<base58>, JCS canonical). |
2.2 Spore Entry
| Field | Type | Description |
|---|---|---|
id | String | Stable identifier for the spore (e.g., cmn-tools). Used for deduplication across releases. Required. |
hash | String | BLAKE3 hash with prefix (e.g., b3.3yMR7vZQ9hL2x...). Required. |
name | String | Human-readable name of the spore. Required. |
synopsis | String? | Optional short description. |
Note: Spore entries use hash instead of full uri to keep the file size small. Clients can construct the full URI as cmn://{domain}/{hash}. The id field matches the id in spore.core.json and ensures that re-releasing a spore replaces the previous entry rather than appending a duplicate.
2.3 Nutrients
Optional array of nutrient methods, ordered by preference (first entry = preferred). Each entry is a typed method object with type as the only required field. All entries MAY include label for UI display.
{
"nutrients": [
{"type": "lightning", "address": "user@example.com"},
{"type": "lightning", "offer": "lno1qgsq..."},
{"type": "onchain_btc", "address": "bc1q..."},
{"type": "evm", "chain_id": 8453, "token": "native", "recipient": "0x1a2b..."},
{"type": "webpage", "url": "https://github.com/sponsors/alice", "label": "GitHub Sponsors"}
]
}
Nutrient types align with strain-payment-method-* conventions. Each type below corresponds to a strain (e.g., lightning → strain-payment-method-lightning), and field names match the strain’s payment request fields where applicable.
The base mycelium.json schema only requires type for nutrient entries. Type-specific required fields below are convention-level requirements enforced by corresponding strain-payment-method-* conventions and tooling.
Direct payment types — static addresses for voluntary donations:
| Type | Required Fields | Optional Fields | Description |
|---|---|---|---|
lightning | address or offer | Lightning address (user@domain, compatible with Nostr zaps) or BOLT12 offer (lno1...). One entry per form. | |
onchain_btc | address | Bitcoin L1 address. | |
liquid | address | asset_id | Liquid sidechain address. |
evm | chain_id, recipient | token | Ethereum or L2 (Arbitrum, Base, Optimism, etc.). token: ERC-20 contract address or "native" (default). |
solana | recipient | token | Solana address. token: SPL mint address or "native" (default). |
monero | address | Monero address. |
Webpage type — any nutrient page, platform profile, or checkout:
| Type | Required Fields | Description |
|---|---|---|
webpage | url | Human-facing payment page (GitHub Sponsors, Open Collective, Polar.sh, etc.). |
The type field is an open string — any value is valid. New payment networks require no protocol update.
Design notes:
- Nutrient type names match
strain-payment-{type}— a tool can map a nutrient entry to the corresponding strain convention without a lookup table. - No amounts — nutrient entries declare where to pay, not how much. Donation amounts are the donor’s choice.
- No dependency splits — nutrients declares “how to pay this domain.” Dependency-aware distribution is a tool-layer concern: tooling MAY traverse the bond graph (
depends_on,spawned_from,absorbed_from) and collect nutrient methods from each domain’s mycelium. - Nutrients is inside
coreand protected bycore_signature— mirrors cannot tamper with the original author’s nutrient addresses.
3. Schema Validation
Schema: https://cmn.dev/schemas/v1/mycelium.json (see 01-substrate §6)
Validators SHOULD embed schemas for offline validation. No network fetch should be required.
4. Signing and Hashing
4.1 Core Signature
The core_signature signs the core object:
- Serialize
coreusing JCS (RFC 8785) - Sign the canonical bytes with Ed25519 private key
- Format as
ed25519.<base58>
Purpose:
- Protects mycelium metadata (name, synopsis, updated_at_epoch_ms, spore list) from tampering
- The
updated_at_epoch_mstimestamp is included to prevent replay attacks
4.2 Content Hash Calculation
The hash in capsule.uri is calculated from core + core_signature:
- Construct hash input:
{"core": <core>, "core_signature": "<signature>"} - Serialize hash input using JCS
- Hash with BLAKE3 →
b3.<base58> - The mycelium URI is
cmn://{domain}/mycelium/{hash}(the hash is also stored incmn.jsonas an element of thehashesarray on thetype: "mycelium"endpoint for change detection)
Key Properties:
- Changing any core field (name, synopsis, spores) changes the hash
- Hash includes the core_signature (tamper-proof)
- URI contains the hash for content addressing
4.3 Capsule Signature
The capsule_signature signs the entire capsule object (including uri):
- Build capsule:
{"uri": <uri>, "core": <core>, "core_signature": "<signature>"} - Serialize using JCS
- Sign with Ed25519 private key →
ed25519.<base58>
Purpose:
- Verifies entire capsule including URI
- Prevents tampering with any field
- URI is protected by signature
4.4 Canonical JSON (JCS)
All signatures and hashes use JCS (see 01-substrate §1.3).
5. Publishing Workflow
Publishing implementations MUST:
- Build the
coreobject - Sign core →
core_signature - Compute hash from core + core_signature
- Construct
uriwith hash:cmn://{domain}/mycelium/{hash} - Build
capsulewith uri, core, core_signature - Sign capsule →
capsule_signature - Save full mycelium to
/cmn/mycelium/{hash}.json - Generate cmn.json capsule entry with endpoints (including
type: "mycelium"withhashesarray) - Sign cmn.json capsules array →
capsule_signature - Save to
cmn.json
6. File Organization
site_root/
├── .well-known/
│ └── cmn.json # Domain entry point (~150 bytes)
└── cmn/
└── mycelium/
└── b3.3yMR7vZQ9hL2x...pTa2.json # Current version
Benefits:
- Old versions retained for history (optional)
- CDN can cache full mycelium files indefinitely (hash-based URLs)
- Capsule is always at the same path (easy to find)
7. Content Resolution
7.1 Hash Lookup and Sharding
The mycelium endpoint in cmn.json carries a hashes array. Each hash points to a mycelium shard. Most domains use a single shard.
Shard rules:
- Every shard is a valid
mycelium.json(same schema, independently signed). All shards MUST have identicalname,domain,key, andsynopsisvalues — a mismatch is an error. hashes[0]is authoritative fornutrientsandbio. Other shards setnutrients: []and omitbio.- Each spore appears in exactly one shard. Clients merge
core.sporesfrom all shards. - Clients compare cached
hashesagainst the live array — only changed hashes need re-fetching.
7.2 Mycelium Resolution
When fetching the full mycelium content:
Find the type: "mycelium" endpoint in capsules[0].endpoints:
- For each hash in the
hashesarray, replace{hash}in the endpoint’surltemplate - Example:
- Hash =
b3.3yMR7vZQ9hL2x...pTa2 - URL =
https://cmn.dev/cmn/mycelium/b3.3yMR7vZQ9hL2x...pTa2.json
- Hash =
- Merge
core.sporesfrom all fetched shards into a single list
If endpoints are missing, return an error - there are no default fallback URLs.
7.3 Spore Resolution
When a client needs to fetch a specific spore:
Find the type: "spore" endpoint in capsules[0].endpoints:
- Replace
{hash}in the endpoint’surltemplate with the spore hash
Resolution flow: cmn.json → find type: "spore" endpoint → fetch spore.
If endpoints are missing, return an error. Synapse is only a fallback when the domain is unreachable, not a default endpoint.
Static Deployment: This design allows static file hosting without any server-side logic.
8. Pulse (Publishing Updates)
When mycelium is updated, publishing tools MUST regenerate both the capsule and full mycelium files.
Optional notification:
Publishing implementations MAY send a Pulse notification to one or more Synapse nodes (see 05-strain §5.2).
Synapse Validation:
- Schema Validation: Validate against embedded schema
- Signature Verification: Verify
core_signatureandcapsule_signatureagainst the domain’s public key - Version Check: Compare
updated_at_epoch_mswith cached versionnew > cached→ Accept (newer version)new == cached, same hash → Ignore (duplicate)new == cached, different hash → Reject (conflict — publisher MUST increment timestamp to correct)new < cached→ Reject (older version)
Content Correction: If a publisher needs to fix a mistake (e.g., typo in synopsis, wrong spore entry), they MUST increment updated_at_epoch_ms before re-signing and re-publishing. This produces a new hash and a newer timestamp, which a Synapse node can accept as an ordinary update. The monotonic timestamp requirement ensures that corrections are unambiguous and prevents conflicting edits.
Replay and rollback analysis for these timestamp rules is covered in 08-security §4.
9. Example
Entry Point (cmn.json):
{
"$schema": "https://cmn.dev/schemas/v1/cmn.json",
"capsules": [
{
"uri": "cmn://cmn.dev",
"key": "ed25519.5XmkQ9vZP8nL3xJdFtR7wNcA6sY2bKgU1eH9pXb4",
"endpoints": [
{"type": "mycelium", "url": "https://cmn.dev/cmn/mycelium/{hash}.json", "hashes": ["b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2"]},
{"type": "spore", "url": "https://cmn.dev/cmn/spore/{hash}.json"},
{"type": "archive", "url": "https://cmn.dev/cmn/archive/{hash}.tar.zst", "format": "tar+zstd"},
{"type": "taste", "url": "https://cmn.dev/cmn/taste/{hash}.json"}
]
}
],
"capsule_signature": "ed25519.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa23yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2"
}
Client resolution: Find the type: "mycelium" endpoint, iterate its hashes array, replace {hash} in the url template for each → fetch each mycelium shard.
Full (/cmn/mycelium/b3.3yMR7vZQ9hL2x...pTa2.json):
{
"$schema": "https://cmn.dev/schemas/v1/mycelium.json",
"capsule": {
"uri": "cmn://cmn.dev/mycelium/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"core": {
"domain": "cmn.dev",
"key": "ed25519.5XmkQ9vZP8nL3xJdFtR7wNcA6sY2bKgU1eH9pXb4",
"name": "cmn.dev",
"synopsis": "",
"updated_at_epoch_ms": 1769777183174,
"spores": [
{
"id": "cmn-spec",
"hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"name": "CMN Protocol Specification",
"synopsis": "Code Mycelial Network - A sovereign-first protocol for code distribution"
},
{
"id": "cmn-tools",
"hash": "b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5",
"name": "CMN Tools",
"synopsis": "Command-line and service tooling for the CMN protocol"
}
]
},
"core_signature": "ed25519.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa23yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2"
},
"capsule_signature": "ed25519.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa23yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2"
}