Hypha CLI Reference
Philosophy
Hypha: “A bio-digital extension for Agents to release and absorb Spores”
Hypha is not a “tool” — it is the biological extension through which Agents interact with the network. All commands represent life activities, not technical operations.
Lifecycle Perspective
┌─────────────────────────────────────────────────────────────────┐
│ Consuming Flow │
├─────────────────────────────────────────────────────────────────┤
│ sense ──→ taste ──→ spawn ──→ bond ──→ grow ──→ absorb │
│ ↓ ↓ ↓ ↓ ↓ ↓ │
│ View Evaluate Create Fetch Update Merge │
│ metadata safety working refs from other │
│ copy source branches │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Releasing Flow │
├─────────────────────────────────────────────────────────────────┤
│ hatch ──→ replicate ──→ release ──→ mycelium pulse │
│ ↓ ↓ ↓ ↓ │
│ Prepare Replicate Sign & Notify │
│ metadata ext. refs publish indexer │
└─────────────────────────────────────────────────────────────────┘Command Concepts
| Command | Biological Concept | Technical Implementation |
|---|---|---|
sense | Perceive spores in environment | Resolve URI, fetch metadata |
taste | Test before consuming | Fetch to cache for agent review, record safety verdict |
spawn | Spore germinates, takes root | Create working copy (taste gate) |
bond | Form mycorrhizal connections | Fetch all bonds to .cmn/bonds/ (taste gate) |
hatch bond | Manage mycorrhizal connections | Add/remove/clear bonds in spore.core.json |
hatch tree | Configure growth pattern | Set tree algorithm, exclude names, follow rules |
grow | Hyphae extend toward source | Sync from spawned_from source (taste gate) |
absorb | Absorb nutrients from other hyphae | AI-assisted merge (taste gate) |
hatch | Spore forms within | Create spore.core.json |
replicate | DNA replication — exact copy | Copy spore to your domain (same hash, re-sign capsule) |
release | Spore released to environment | Sign & publish to mycelium |
lineage | Trace life origins & observe evolutionary tree | Query bond graph (–direction in|out) |
search | Sense spores by affinity | Semantic search via Synapse |
synapse | Manage sensory connections | Configure Synapse node connections |
Response Structure
Hypha emits a stdout JSONL event stream. The final result/error event uses this top-level JSON structure (Agent-First Data protocol):
{
"code": "ok" | "error",
"trace": { ... },
"result": { ... },
"error": "message"
}| Field | Presence | Description |
|---|---|---|
code | Always | "ok" or "error" |
trace | Optional | Internal resolution state — shows how far the operation got (aids debugging) |
result | Success only | Command-specific result payload |
error | Error only | Human-readable error message (string). Error codes appear in trace.error_code. |
The trace field is progressively filled as the operation proceeds. For visitor pipeline commands (sense/taste/spawn/grow/absorb), it contains:
{
"uri": "cmn://domain/hash",
"cmn": { "resolved": true, "public_key": "..." },
"verified": true
}
When an error occurs, trace shows which stages completed successfully before the failure. For release, it contains {"status": "released"|"skipped", "site": "..."}. Commands without a resolution pipeline omit the trace field.
Design Rules
- Agent First: Default output is JSON for machine-readability
- Structured Output: Default output is valid JSONL (one JSON object per line)
- Error Handling: Errors return
"code": "error"with a human-readableerrorstring andtrace.error_codefor LLM self-correction - No Panics: All errors are handled gracefully; never crashes unexpectedly
Output Modes
| Mode | Flag | Output stream |
|---|---|---|
| JSON (default) | -o json | Compact JSONL via agent_first_data::output_json |
| Plain | -o plain | logfmt-style output via agent_first_data::output_plain |
| YAML | -o yaml | YAML via agent_first_data::output_yaml |
All runtime protocol events are emitted on stdout (not stderr), including startup, progress, warnings, success, and errors. Output is event-stream style: startup (code=log event=startup) can appear before the final line (code=ok or code=error).
Global Options
hypha [OPTIONS] <COMMAND>
Options:
-o, --output <FORMAT> Output format: json | plain | yaml (default: json)
-h, --help Print help
-V, --version Print version
All commands that place code into your working directory (spawn, grow, absorb, bond) require the target spore to have been tasted. Bond management (hatch bond set/remove/clear) and tree configuration (hatch tree set/show) modify spore.core.json and do not require tasting. toxic verdicts block, rotten warns then proceeds, all others (safe, fresh, sweet) proceed normally. (taste itself downloads to the global cache for review — the cache is not gated.)
1. Spore Lifecycle Commands
sense - View Spore Metadata
hypha sense <URI>
Action: Resolves CMN URI and displays spore metadata without downloading
Verification (reported in trace field):
- Fetch cmn.json for domain’s public key
- Signature verification against public key
- Cache status (which lookups were served from cache)
Output:
{
"code": "ok",
"trace": {
"uri": "cmn://cmn.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"cmn": {
"resolved": true, "cached": false, "cached_at": null,
"public_key": "ed25519.5XmkQ9vZP8nL3xJdFtR7wNcA6sY2bKgU1eH9pXb4"
},
"verified": { "core_signature": true, "capsule_signature": true }
},
"result": {
"spore": {
"$schema": "https://cmn.dev/schemas/v1/spore.json",
"capsule": {
"uri": "cmn://cmn.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"core": {
"name": "CMN Protocol Specification",
"domain": "cmn.dev",
"synopsis": "Code Mycelial Network - A sovereign-first protocol for code distribution",
"intent": ["spec simplification and strict domain validation"],
"license": "CC0-1.0",
"mutations": [
"§05 URI: remove regex and pseudocode, use prose parsing steps",
"§05 URI §6: domain must be lowercase RFC 1123, reject uppercase instead of normalizing"
],
"bonds": [
{
"uri": "cmn://cmn.dev/b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5",
"relation": "spawned_from"
}
],
"tree": { "algorithm": "blob_tree_blake3_nfc", "exclude_names": [".git"], "follow_rules": [".gitignore"] }
},
"core_signature": "ed25519.5XmkQ9vZP8nL3xJdFtR7wNcA6sY2bKgU1eH9pXb4...",
"dist": [
{
"type": "archive",
"filename": "cmn-spec.tar.zst"
}
]
},
"capsule_signature": "ed25519.5XmkQ9vZP8nL3xJdFtR7wNcA6sY2bKgU1eH9pXb4..."
}
}
}taste - Evaluate Before Use
# Step 1: Fetch to cache and output paths for agent review
hypha taste <URI>
# Step 2: Record verdict after agent analysis
hypha taste <URI> --verdict <sweet|fresh|safe|rotten|toxic> [--notes "..."]
# Optional: Pull others' taste reports from Synapse
hypha taste <URI> --synapse <URL>
# Optional: Share your report (requires domain with signing key)
hypha taste <URI> --verdict safe --domain <DOMAIN> --synapse <URL>
Arguments:
URI: CMN URI of the spore to evaluate
Options:
| Option | Description |
|---|---|
--verdict <VERDICT> | Record verdict: sweet, fresh, safe, rotten, or toxic |
--notes <TEXT> | Optional notes explaining the verdict |
--synapse <URL> | Pull others’ taste reports for reference |
--domain <DOMAIN> | Sign and share your report (requires domain key) |
Verdicts (5-level taste scale):
| Verdict | Meaning | Effect |
|---|---|---|
sweet | Used it, it’s great — personal endorsement | Proceed normally |
fresh | Reviewed thoroughly, no issues found | Proceed normally |
safe | Quick scan, nothing obviously wrong | Proceed normally |
rotten | Unusable — broken, won’t compile, fundamentally flawed | Proceed with warning |
toxic | Confirmed dangerous (malware, data theft, backdoor) | Blocked |
| (none) | Not yet tasted | Blocked (NOT_TASTED) |
Step 1 — Fetch and prepare for review (no --verdict flag):
Fetches the spore (and its parent, if a spawned_from reference exists) to the local cache, then outputs the paths for agent review.
{
"code": "ok",
"result": {
"uri": "cmn://fork.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"cache_path": "/home/user/.cmn/hypha/cache/fork.dev/spore/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2/content",
"parent": {
"uri": "cmn://origin.dev/b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5",
"cache_path": "/home/user/.cmn/hypha/cache/origin.dev/spore/b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5/content"
},
"taste": null,
"others_tastes": []
}
}
The agent reads the code at cache_path (and optionally compares with parent.cache_path), then proceeds to record a verdict.
Step 2 — Record verdict (with --verdict flag):
{
"code": "ok",
"result": {
"uri": "cmn://fork.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"taste": "safe",
"notes": "Reviewed changes: minor bug fix, no security concerns",
"shared": false
}
}
When --domain and --synapse are provided, the report is signed and submitted via POST /synapse/pulse, and shared is true.
Pulling others’ reports (with --synapse, no --verdict):
{
"code": "ok",
"result": {
"uri": "cmn://fork.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"cache_path": "/home/user/.cmn/hypha/cache/fork.dev/spore/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2/content",
"parent": { ... },
"taste": null,
"others_tastes": [
{ "domain": "alice.dev", "taste": "safe", "notes": "Clean fork, tests pass" },
{ "domain": "bob.dev", "taste": "rotten", "notes": "Tests fail on ARM" }
]
}
}
Others’ reports are reference data — the agent’s own judgment is authoritative.
spawn - Create Working Copy
hypha spawn <URI> [DIRECTORY] [--vcs <TYPE>] [--dist <SOURCE>] [--bond]
Arguments:
URI: CMN URI of the sporeDIRECTORY: Target directory (default:./<spore-id>)
Options:
| Option | Description |
|---|---|
--vcs <TYPE> | Initialize version control (e.g., --vcs git) |
--dist <SOURCE> | Preferred distribution: archive (default) or git |
--bond | After spawn, run bond to fetch all tasted bonds to .cmn/bonds/ |
Action: Creates a working copy. Default: archive source, no VCS.
Archive source workflow (default):
- Verify spore signature
- Download and extract archive
- If
--vcs git: initialize git repo with initial commit - Add
spawned_fromreference tospore.core.json
Git source workflow (--dist git):
- Verify spore signature
- Clone to cache bare repo (or use existing)
- Clone from cache to target directory
- If
--vcs git: keep .git, set up remotes; otherwise remove .git - Add
spawned_fromreference tospore.core.json
Examples:
hypha spawn cmn://cmn.dev/b3.... # archive, no VCS
hypha spawn cmn://cmn.dev/b3.... --vcs git # archive + git init
hypha spawn cmn://cmn.dev/b3.... --dist git # git source, no VCS
hypha spawn cmn://cmn.dev/b3.... --dist git --vcs git # git source + keep .git
Output:
{
"code": "ok",
"result": {
"uri": "cmn://cmn.dev/b3....",
"name": "CMN Protocol Specification",
"path": "/home/user/projects/cmn-spec",
"source_type": "archive",
"vcs": null
}
}grow - Update from Source
hypha grow [--synapse <DOMAIN|URL>] [--synapse-token-secret <TOKEN>] [--dist <SOURCE>] [--bond] [--discard-local-changes]
Options:
| Option | Description |
|---|---|
--synapse <DOMAIN|URL> | Synapse to query for updates (domain or URL; default: configured default) |
--synapse-token-secret <TOKEN> | Auth token for synapse (overrides configured token) |
--dist <SOURCE> | Override distribution source: archive or git |
--bond | After grow, run bond to refresh .cmn/bonds/ (bonds may have changed) |
--discard-local-changes | Skip local modification check for archive grow (when no VCS is present) |
Action: Pulls latest changes using Synapse lineage discovery (run in spawned directory)
Requirements:
- Must be in a spore directory with
spore.core.jsoncontaining aspawned_fromreference - For git-backed directories: checks working directory is clean before updating
- For non-git directories (archive spawn): compares local files against the cached spawn origin. If modifications are detected, grow is blocked with
LOCAL_MODIFIEDerror. Use--discard-local-changesto bypass, orgit initto preserve changes.
Update discovery (8-step flow with Agent-First Data progress):
- RESOLVE — Read
./spore.core.json, extractspawned_fromURI (domain + hash) - SYNAPSE — Resolve synapse endpoint (falls back to mycelium chain-following if no synapse configured)
- LINEAGE — Query Synapse for newer versions on the same domain, follow chain to tip
- VERIFY — Fetch & verify new spore from source site (cmn.json → manifest → signatures)
- TASTE — Check taste verdict for new hash (blocks if toxic)
- RESOLVE_DIST — Determine update method (respects
--dist; else: git if.gitexists and manifest has git dist; else archive with delta) - APPLY — Download & apply update (git: fetch + checkout; archive: download + replace files)
- COMPLETE — Report result with old/new hash
Output:
{
"code": "ok",
"result": {
"uri": "cmn://cmn.dev/b3....",
"old_hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"new_hash": "b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5",
"method": "git",
"path": "/home/user/projects/cmn-spec"
}
}absorb - Prepare for AI Merge
hypha absorb <URI>... [--discover] [-s <DOMAIN|URL>] [--max-depth <N>]
Arguments:
URI...: One or more spore URIs to absorb
Options:
--discover: Auto-discover descendants fromspawned_frombonds--synapse <DOMAIN|URL>: Synapse server — domain or URL (default: configured default; required with –discover)--max-depth <N>: Maximum depth for discovery (default: 10)
Action: Prepares code from spores for AI-assisted merging
Files created:
.cmn/absorb/{hash}/spore.json- Spore manifest.cmn/absorb/{hash}/content/- Extracted source code.cmn/absorb/ABSORB.md- Structured AI prompt
Output:
{
"code": "ok",
"result": {
"sources": [
{ "uri": "cmn://...", "hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2", "path": ".cmn/absorb/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2/" }
],
"prompt_path": ".cmn/absorb/ABSORB.md"
}
}bond - Fetch Bonds
hypha bond [--clean] [--status]
Options:
| Option | Description |
|---|---|
--clean | Remove orphaned bonds not in spore.core.json |
--status | Show bond status without fetching |
Action: Reads spore.core.json bonds and fetches tasted bonded spores to .cmn/bonds/. Excludes spawned_from (handled by grow) and absorbed_from (historical — merge already done). toxic spores are blocked, rotten spores warn then proceed, untasted spores are blocked.
Requirements:
- Must be in a project directory with
spore.core.json - Each referenced spore must be tasted
When to use:
- After
spawn(if not using--bond) - After
grow(if bonds changed) - After
absorb(if newdepends_onbonds were added) - After manually editing
spore.core.jsonbonds
Directory structure created:
.cmn/
└── bonds/
├── bonds.json ← index of all bonds
├── b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2/ ← depends_on
│ ├── spore.json
│ └── content/
└── b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5/ ← follows
├── spore.json
└── content/
The bonds.json index maps hashes to URIs, relations, and names:
{
"bonds": [
{ "hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2", "uri": "cmn://pub.com/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2", "relation": "depends_on", "name": "Spore B" }
]
}
For depends_on bonds, the publisher configures build system paths using the hash directory:
- Rust:
Cargo.toml→spore_b = { path = ".cmn/bonds/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2/content" } - Node:
package.json→"spore-b": "file:.cmn/bonds/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2/content" - Go:
go.mod→replacedirective pointing to.cmn/bonds/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2/content
Output:
{
"code": "ok",
"result": {
"bonded": [
{ "uri": "cmn://pub.com/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2", "relation": "depends_on", "status": "bonded" }
]
}
}
Status output (--status):
{
"code": "ok",
"result": {
"bonds": [
{ "uri": "cmn://origin.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2", "relation": "spawned_from", "bonded": "excluded" },
{ "uri": "cmn://pub.com/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2", "relation": "depends_on", "taste": "safe", "bonded": true },
{ "uri": "cmn://cmn.dev/b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5", "relation": "implements", "taste": "safe", "bonded": true },
{ "uri": "cmn://xyz.com/b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5", "relation": "inspired_by", "taste": null, "bonded": false }
]
}
}hatch - Prepare Spore Metadata
hypha hatch [OPTIONS]
Options:
| Option | Description |
|---|---|
--id <ID> | URL-safe identifier (for paths) |
--name <NAME> | Display name |
--domain <DOMAIN> | Publisher domain |
--synopsis <TEXT> | Short description |
--intent <TEXT> | Why this release (repeatable, required for release) |
--mutations <TEXT> | What changed (repeatable) |
--license <SPDX> | License identifier (default: MIT) |
Action: Creates a new spore.core.json or updates existing one. Use hypha hatch bond set/remove/clear to manage bonds and hypha hatch tree set/show to manage tree configuration.
Output:
{
"code": "ok",
"result": {
"protocol": "cmn/1",
"spore": {
"id": "my-tool",
"name": "My Tool",
"synopsis": "A useful tool",
"intent": ["v1.0"],
"mutations": [],
"license": "MIT",
"tree": { "algorithm": "blob_tree_blake3_nfc", "exclude_names": [".git"], "follow_rules": [".gitignore"] }
}
}
}hatch bond - Manage Bonds
hypha hatch bond set --uri <URI> --relation <RELATION> [--id ID] [--reason REASON] [--with KEY=VALUE]...
hypha hatch bond remove [--uri URI] [--relation RELATION]
hypha hatch bond clear
Subcommands:
| Subcommand | Description |
|---|---|
set | Add or update a bond (upsert by URI). --uri required. --relation required for new bonds, optional for updates. Only explicitly passed fields are overwritten. --with KEY=VALUE is repeatable and merges into existing with object. VALUE is parsed as JSON, falls back to string. |
remove | Remove bonds matching --uri and/or --relation. At least one filter required. When both are given, only bonds matching both are removed. |
clear | Remove all bonds |
Examples:
# New bond (--relation required)
hypha hatch bond set --uri cmn://cmn.dev/b3.abc --relation follows --id my-lib --reason "Core library"
# Update only reason (relation/id/with preserved)
hypha hatch bond set --uri cmn://cmn.dev/b3.abc --reason "Updated reason"
# Add with parameters (merges into existing with)
hypha hatch bond set --uri cmn://cmn.dev/b3.abc --with 'mints=["https://mint.example.com"]' --with unit=sat
# Remove
hypha hatch bond remove --relation follows
hypha hatch bond remove --uri cmn://cmn.dev/b3.abc
hypha hatch bond remove --uri cmn://cmn.dev/b3.abc --relation follows
hypha hatch bond clearhatch tree - Manage Tree Configuration
hypha hatch tree set [--algorithm <ALG>] [--exclude-names <NAME>...] [--follow-rules <FILE>...]
hypha hatch tree show
Subcommands:
| Subcommand | Description |
|---|---|
set | Set tree configuration fields. Only provided fields are overwritten. |
show | Show current tree configuration. |
Examples:
# Set exclude names
hypha hatch tree set --exclude-names .git node_modules
# Set follow rules
hypha hatch tree set --follow-rules .gitignore .npmignore
# Set algorithm
hypha hatch tree set --algorithm blob_tree_blake3_nfc
# Show current tree config
hypha hatch tree showrelease - Sign and Publish
hypha release --domain <DOMAIN> [OPTIONS]
Required:
--domain <DOMAIN>: Target domain
Options:
| Option | Description |
|---|---|
--source <PATH> | Spore source directory (default: current) |
--site-path <PATH> | Custom site directory |
--dist-git <URL> | External git repository URL |
--dist-ref <REF> | Git ref: tag/branch/commit |
--archive <FORMAT> | Archive format for release generation (currently only zstd) |
--dry-run | Pre-compute URI without writing any files |
Action:
- Validates
spore.core.jsonstructure - Computes content hash (BLAKE3)
- Verifies private key permissions (must be 0600)
- Signs manifest with private key
- Copies files to site’s
public/directory - Updates
cmn.jsonandmycelium.json
Output:
{
"code": "ok",
"result": {
"protocol": "cmn/1",
"uri": "cmn://cmn.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"spore": { ... },
"signature": "ed25519...."
}
}replicate - Copy Spore to Your Domain
hypha replicate <URI> --domain <DOMAIN> [--bonds]
Arguments:
URI: CMN URI of the spore to replicate (ignored when using--bonds)
Required:
--domain <DOMAIN>: Your domain to host the replicate
Options:
| Option | Description |
|---|---|
--bonds | Replicate all non-self bonds from spore.core.json and update URIs |
Action: Creates an exact copy of a spore on your domain (same hash, same core, same core_signature). Only the dist and capsule_signature change — the capsule is re-signed with your domain’s key.
This is a publisher command — requires a mycelium site (hypha mycelium root).
Steps:
- Fetch original spore.json (preserve
core+core_signature) - Download content (archive)
- Copy archive to your site’s
public/directory - Create new
distentry pointing to your domain - Re-sign capsule with your key → new
capsule_signature - Add spore to your
mycelium.json
Single spore:
hypha replicate cmn://other.com/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2 --domain mydomain.com
All non-self bonds (--bonds):
# In a project directory with spore.core.json
hypha replicate --bonds --domain mydomain.com
Reads spore.core.json, finds all bonds pointing to other domains, replicates each one to mydomain.com, and updates bond URIs:
Before: depends_on → cmn://other.com/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2
After: depends_on → cmn://mydomain.com/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2 (same hash)
Output:
{
"code": "ok",
"result": {
"replicated": [
{
"original": "cmn://other.com/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"replicate": "cmn://mydomain.com/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"core_domain": "other.com"
}
]
}
}
Verification by visitors:
core_signature→ verifies againstother.com(original author)capsule_signature→ verifies againstmydomain.com(replicate host)core.domain ≠ URI domain→ this is a replicate
2. Discovery Commands
lineage - Trace Spore Lineage
hypha lineage <URI> [--direction in|out] [-s <DOMAIN|URL>] [--max-depth <N>]
Action: Traces spore lineage via Synapse. --direction in (default) finds descendants (forks/evolutions). --direction out traces ancestors (spawn chain).
The -s/--synapse flag accepts a configured domain or a URL. If omitted, uses the default from hypha synapse use.
Output:
{
"code": "ok",
"result": {
"uri": "cmn://cmn.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"synapse": "https://synapse.cmn.network",
"direction": "inbound",
"count": 5,
"bonds": [
{ "uri": "cmn://fork1.dev/b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5", "relation": "spawned_from" }
]
}
}search - Find Spores by Keyword
hypha search <QUERY> [-s <DOMAIN|URL>] [--domain <DOMAIN>] [--license <SPDX>] [--bonds <RELATION:URI>] [--limit <N>]
Arguments:
QUERY: Search query text
Options:
| Option | Description |
|---|---|
--synapse <NAME|URL> | Synapse server — domain or URL (default: configured default) |
--domain <DOMAIN> | Filter results by domain |
--license <SPDX> | Filter results by license (SPDX identifier) |
--bonds <RELATION:URI> | Filter by bond relationship. Format: relation:uri. Comma-separated for AND logic (e.g. spawned_from:cmn://a.dev/hash,follows:cmn://b.dev/hash). |
--limit <N> | Maximum results (default: 20) |
Action: Performs semantic search over indexed spore metadata via Synapse’s /synapse/search endpoint
Examples:
hypha search "HTTP client library" --synapse https://synapse.cmn.dev
hypha search "database" --synapse https://synapse.cmn.dev --domain cmn.dev --limit 5
hypha search "crypto" --synapse https://synapse.cmn.dev --license MIT
hypha search "http client" --bonds spawned_from:cmn://cmn.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2
Output:
{
"code": "ok",
"result": {
"query": "HTTP client library",
"synapse": "https://synapse.cmn.dev",
"domain": null,
"license": null,
"bonds": null,
"count": 3,
"results": [
{
"uri": "cmn://cmn.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
"domain": "cmn.dev",
"name": "reqwest-lite",
"synopsis": "Lightweight HTTP client for Rust",
"license": "MIT",
"intent": ["initial release"],
"score": 0.92
},
{
"uri": "cmn://tools.dev/b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5",
"domain": "tools.dev",
"name": "curl-wrapper",
"synopsis": "Ergonomic curl wrapper",
"license": "Apache-2.0",
"intent": ["v2 rewrite"],
"score": 0.85
}
]
}
}
Error when search is not configured:
{
"code": "error",
"error": "Search engine not configured on this synapse instance",
"trace": {
"error_code": "SYNAPSE_ERR"
}
}
3. Mycelium Infrastructure
mycelium root - Establish Site
hypha mycelium root <DOMAIN> [--site-path <PATH>] [--endpoints-base <URL>]
Action: Establishes a new site with Ed25519 keypair (or updates existing)
Creates:
keys/private.pem- Ed25519 private key, PKCS#8 PEM format (mode 0600)keys/public.pem- Ed25519 public key, SPKI PEM formatpublic/.well-known/cmn.json- Domain entry point (includes public key)
Output:
{
"code": "ok",
"result": {
"domain": "example.com",
"public_key": "ed25519.5XmkQ9vZP8nL3xJdFtR7wNcA6sY2bKgU1eH9pXb4",
"site_path": "/home/user/.cmn/mycelium/example.com"
}
}Key Management
By default, mycelium root creates a new keypair for each domain. This provides security isolation between domains.
However, a single keypair can be used for multiple domains. This is an operational choice, not a protocol requirement. To share keys:
# Initialize first domain (creates new keypair)
hypha mycelium root example.com
# Initialize second domain with shared keypair
hypha mycelium root other.com
cp ~/.cmn/mycelium/example.com/keys/private.pem ~/.cmn/mycelium/other.com/keys/
cp ~/.cmn/mycelium/example.com/keys/public.pem ~/.cmn/mycelium/other.com/keys/
# cmn.json for other.com will use the same public key
When to share keys:
- Same organization publishing from multiple domains
- Simpler key management for small publishers
- Migration scenarios
When to use separate keys:
- Different security contexts or trust levels
- Different teams managing different domains
- Key rotation requirements per domain
mycelium status - View Site Status
hypha mycelium status [DOMAIN] [--site-path <PATH>]
Action:
- Without
DOMAIN: Lists all configured sites - With
DOMAIN: Shows specific site details
Output:
{
"code": "ok",
"result": {
"domain": "example.com",
"public_key": "ed25519.5XmkQ9vZP8nL3xJdFtR7wNcA6sY2bKgU1eH9pXb4",
"site_path": "/home/user/.cmn/mycelium/example.com",
"spore_count": 5
}
}mycelium serve - Local Server
hypha mycelium serve [DOMAIN] [--site-path <PATH>] [--port <PORT>]
Action: Starts a local HTTP server to serve the site (for debugging)
Options:
--port <PORT>: Listen port (default: 8080)
mycelium pulse - Notify Synapse
hypha mycelium pulse [-s <DOMAIN|URL>] --file <PATH>
Action: Sends a signed pulse to a Synapse indexer (POST /synapse/pulse)
Security:
- Request is signed with domain’s private key
- Includes timestamp to prevent replay attacks (Synapse rejects timestamps beyond
pulse.max_clock_skew, default 300s) - Synapse verifies signature via cmn.json public key fetch
Output:
{
"code": "ok",
"result": {
"synapse": "https://synapse.cmn.network",
"response": {
"code": "ok",
"message": "Indexed successfully"
}
}
}
4. Cache Management
cache list
hypha cache list
Lists all cached spores.
cache clean
hypha cache clean [--all]
Options:
--all: Remove all cached items
cache path
hypha cache path <URI>
Shows local filesystem path for a cached spore.
5. Synapse Management
synapse add - Register a Node
hypha synapse add <URL>
Action: Registers a Synapse node. The domain is extracted from the URL and used as the identifier. Creates $CMN_HOME/hypha/synapse/<domain>/config.toml. If this is the first node, it becomes the default automatically.
Example: hypha synapse add https://synapse.cmn.dev → registers as synapse.cmn.dev
Output:
{
"code": "ok",
"result": {
"domain": "synapse.cmn.dev",
"url": "https://synapse.cmn.dev",
"default": true
}
}synapse list - Show Configured Nodes
hypha synapse list
Action: Lists all configured Synapse nodes with default marker and token status.
Output:
{
"code": "ok",
"result": {
"count": 2,
"default": "synapse.cmn.dev",
"nodes": [
{ "domain": "synapse.cmn.dev", "url": "https://synapse.cmn.dev", "has_token": false, "default": true },
{ "domain": "localhost", "url": "http://localhost:8080", "has_token": true, "default": false }
]
}
}synapse use - Set Default Node
hypha synapse use <DOMAIN>
Action: Sets the default Synapse node. All commands with -s/--synapse will use this node when no argument is given.
Example: hypha synapse use synapse.cmn.dev
synapse remove - Unregister a Node
hypha synapse remove <DOMAIN>
Action: Removes a Synapse node directory. Clears default if the removed node was the default.
synapse info - Query Instance Info
hypha synapse info [DOMAIN|URL]
Action: Calls GET /synapse/info on the Synapse instance and displays the self-description. Caches the response to $CMN_HOME/hypha/synapse/<domain>/info.json. Uses default node if no argument given.
Output:
{
"code": "ok",
"result": {
"synapse": "https://synapse.cmn.dev",
"info": {
"name": "CMN Synapse",
"indexed_spores": 12450,
"features": ["search", "lineage", "rhizome", "pulse"],
"payment": "free"
}
}
}synapse config - Configure a Synapse Node
hypha synapse config <DOMAIN> [--token-secret <TOKEN>]
Action: Configures a Synapse node. Currently supports setting the auth token. When set, Hypha automatically includes Authorization: Bearer <token> in all requests to that node. Stored in $CMN_HOME/hypha/synapse/<domain>/config.toml (0600 permissions).
--token-secret <TOKEN>: stores the token--token-secret "": clears any stored token
synapse discover - Find Other Instances
hypha synapse discover [-s <DOMAIN|URL>]
Action: Searches for other Synapse instances via GET /synapse/search?ref=follows:strain-synapse. Uses default node if no argument given.
6. Configuration Commands
config list - Show Current Configuration
hypha config list
Action: Shows the effective configuration (defaults merged with $CMN_HOME/hypha/config.toml). Reports the config file path and whether it exists.
{
"code": "ok",
"result": {
"path": "/home/user/.cmn/hypha/config.toml",
"exists": true,
"config": {
"defaults": { "synapse": "synapse.cmn.dev" },
"cache": { "cmn_ttl_s": 300 }
}
}
}config set - Set a Configuration Value
hypha config set <KEY> <VALUE>
Action: Sets a configuration value using dotted key notation. Creates config.toml if it doesn’t exist.
| Key | Type | Description |
|---|---|---|
defaults.synapse | string | Default synapse domain |
cache.path | string | Custom cache directory |
cache.cmn_ttl_s | integer | cmn.json cache TTL in seconds (default: 300) |
cache.key_trust_ttl_s | integer | Key trust cache TTL in seconds (default: 604800) |
cache.key_trust_refresh_mode | enum | Key trust refresh mode: expired, always, offline |
cache.key_trust_synapse_witness_mode | enum | Domain-offline fallback: allow (use Synapse witness) or require_domain (no Synapse witness) |
hypha config set defaults.synapse synapse.cmn.dev
hypha config set cache.cmn_ttl_s 600
hypha config set cache.key_trust_ttl_s 604800
hypha config set cache.key_trust_refresh_mode expired
hypha config set cache.key_trust_synapse_witness_mode require_domain
hypha config set cache.path /tmp/hypha-cache
7. System Commands
version
hypha -V
Shows version information.
help
hypha help [COMMAND]
Shows help information. Equivalent to --help.
8. Error Handling
Errors are emitted as structured JSON events in the stdout JSONL stream (Agent-First Data protocol):
{
"code": "error",
"error": "Site not found at /path/to/site",
"trace": {
"error_code": "NO_SITE"
}
}Pipeline Error Codes
These standardized codes correspond to stages in the visitor resolution pipeline (sense/taste/spawn/grow/absorb):
| Code | Stage | Description |
|---|---|---|
INVALID_URI | URI parse | Invalid or malformed CMN URI |
KEY_FETCH_FAILED | Key fetch | cmn.json fetch or public key extraction failed |
CMN_FAILED | cmn.json | cmn.json endpoint extraction failed |
MANIFEST_FAILED | Manifest | Manifest fetch (mycelium or spore) failed |
SIG_FAILED | Verification | Signature verification failed |
HASH_MISMATCH | Hash check | Content hash doesn’t match URI |
SPORE_NOT_FOUND | Catalog | Spore not found in mycelium catalog |
FETCH_FAILED | Download | Content download (HTTPS/git) failed |
Command-Specific Error Codes
| Code | Description |
|---|---|
INIT_ERR | Failed to initialize site |
NO_SITE | Site not found |
NO_SPORE | spore.core.json not found |
VALIDATION_ERR | Validation failed |
SIGN_ERR | Failed to sign (publisher) |
PARSE_ERR | Failed to parse JSON |
WRITE_ERR | Failed to write file |
DIR_EXISTS | Target directory exists |
DIR_ERR | Directory creation failed |
SPAWN_ERR | Spawn operation failed (git/archive) |
GROW_ERR | Grow operation failed (local git/archive) |
GIT_URL_CHANGED | Git repository URL changed since spawn |
REPO_IDENTITY_ERR | Repository root commit mismatch |
ABSORB_ERR | Absorb operation failed (local) |
BOND_ERR | Bond operation failed (fetch/extract bonds) |
NOT_TASTED | Spore has not been tasted — run hypha taste first |
ROTTEN_WARN | Spore tasted as rotten — proceeding with warning |
TOXIC | Spore tasted as toxic — blocked |
SYNAPSE_ERR | Synapse query failed |
GIT_ERR | Git operation failed (publisher) |
LOCAL_MODIFIED | Local files modified since spawn — archive grow blocked (use --discard-local-changes or git init) |
Exit Codes
0: Success1: General error2: Invalid arguments
9. Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
CMN_HOME | Base directory for all CMN data (keys, sites, config) | ~/.cmn |
SYNAPSE_TOKEN_SECRET | Auth token for Synapse requests. Overrides per-node config, overridden by --synapse-token-secret. | unset |
Configuration Files
| File | Description |
|---|---|
$CMN_HOME/hypha/config.toml | Hypha config (defaults, cache TTLs) |
$CMN_HOME/hypha/synapse/<domain>/config.toml | Per-node synapse config (url, token_secret). Mode 0600. |
$CMN_HOME/hypha/synapse/<domain>/info.json | Cached /synapse/info response |
hypha/config.toml format:
[defaults]
synapse = "synapse.cmn.dev"
[cache]
# path = "/custom/cache/path" # optional, default: $CMN_HOME/hypha/cache/
cmn_ttl_s = 300
synapse/<domain>/config.toml format:
url = "https://synapse.cmn.dev"
token_secret = "sk-abc123" # optional
10. Directory Structure
Hypha Configuration
$CMN_HOME/hypha/
├── config.toml # Defaults and cache TTLs
└── synapse/
├── synapse.cmn.dev/
│ ├── config.toml # url, token_secret (mode 0600)
│ └── info.json # Cached /v1/info response
└── localhost/
└── config.tomlMycelium Site
$CMN_HOME/mycelium/<domain>/
├── keys/
│ ├── private.pem # Ed25519 private key (mode 0600)
│ └── public.pem # Ed25519 public key
└── public/ # Deployable static files
├── .well-known/
│ └── cmn.json # Domain entry point (includes public key)
└── cmn/
├── mycelium/
├── spore/
└── archive/Project Working Directory
my-project/
├── src/
├── spore.core.json ← committed to git
├── .gitignore ← includes .cmn/
└── .cmn/ ← gitignored, fully regenerable
├── bonds/ ← bond: all bonded spores
│ ├── bonds.json ← index (hash, uri, relation, name)
│ ├── b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2/ ← organized by hash
│ │ ├── spore.json
│ │ └── content/
│ └── b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5/
│ ├── spore.json
│ └── content/
└── absorb/ ← absorb: temporary staging
├── ABSORB.md
└── b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2/
├── spore.json
└── content/
The .cmn/ directory is derived from spore.core.json and the global cache. Delete it anytime — hypha bond rebuilds bonds/, and hypha absorb rebuilds absorb/.
Local Cache
Default: $CMN_HOME/hypha/cache/. Override with [cache] path in config.toml.
$CMN_HOME/hypha/cache/
└── <domain>/
├── mycelium/
│ ├── cmn.json
│ └── mycelium.json
├── spore/
│ └── b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2/
│ ├── spore.json
│ └── content/
└── repos/
└── {root_commit}.git # Bare repo cache
11. Security
Key Format
Keys are stored in standard PEM format:
- Private key: PKCS#8 PEM
- Public key: SPKI PEM
Private Key Protection
- Mode 0600 required
- Verified before signing
- Fails with actionable error if wrong
Public Key Discovery
The domain’s public key is fetched from cmn.json over HTTPS:
https://cmn.dev/.well-known/cmn.json
→ { "capsule": { "public_key": "ed25519.5XmkQ9vZP8nL3xJdFtR7wNcA6sY2bKgU1eH9pXb4", ... }, ... }
TLS provides the authenticated channel that binds the public key to the domain. No DNS TXT records are required.