02. Mycelium - Domain Manifest
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
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 | Schema URL: https://cmn.dev/schemas/v1/mycelium.json |
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 URL: https://cmn.dev/schemas/v1/mycelium.json
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.
Protection Against:
- Replay attacks (old versions rejected by timestamp)
- Version rollback (timestamp must strictly increase)
- Ambiguous edits (same timestamp, different hash = rejected)
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"
}