Synapse API Reference

Synapse is the CMN Federated Indexer and Resilience Layer. It crawls sovereign domains, aggregates metadata, and serves as a content fallback when origin servers are offline.

For the protocol specification — pulse ingestion, verification rules, error codes, and Nostr integration — see the Synapse Protocol Specification.

Response Structure

All responses follow a consistent three-field structure:

{
  "code": "ok",
  "trace": { ... },
  "result": {
    "query": { ... },
    "<content>": { ... }
  }
}
FieldDescription
code"ok" on success, or a specific error code (e.g. "not_found", "signature_invalid") — always present
traceServer-side operational state — always present (empty {} when no metadata)
resultResponse payload (success) or absent (error)
errorHuman-readable error message (error) or absent (success)

Within result:

The trace field carries operational metadata such as max_depth_reached, source (graph vs storage), or pulse info. The action field (indexed/duplicate/updated/replicated) is in result for pulse responses. The access log middleware merges this with request metadata (method, path, status_code, latency_ms) for structured logging.

Spore API

GET /synapse/spore/:hash

Retrieve a spore by content hash (content-addressable lookup).

Path Parameters:

Response (200):

{
  "code": "ok",
  "trace": {},
  "result": {
    "query": { "hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2" },
    "spore": { ... },
    "replicates": ["mirror.net"]
  }
}

Notes:

GET /synapse/spore/:hash/bonds

Traverse bonds of a spore in a given direction.

Query Parameters:

Response (200):

{
  "code": "ok",
  "trace": {
    "max_depth_reached": false
  },
  "result": {
    "query": { "hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2", "direction": "inbound", "max_depth": 1 },
    "bonds": [
      {
        "uri": "cmn://fork.org/b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5",
        "domain": "fork.org",
        "name": "my-fork",
        "relation": "spawned_from"
      }
    ]
  }
}

Error (400): Missing or invalid direction parameter.

Mycelium API

GET /synapse/myceliums

List all indexed myceliums.

Response (200):

{
  "code": "ok",
  "trace": {},
  "result": {
    "query": {},
    "myceliums": [
      {
        "domain": "cmn.dev",
        "hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
        "updated_at_epoch_ms": 1769787347698
      }
    ]
  }
}

GET /synapse/mycelium/:domain

Retrieve indexed mycelium for a domain. Returns the full signed manifest (passthrough), enriched with cmn (capsule entry from cmn.json, best-effort) and replicates (known mirror domains, origin excluded).

Response (200):

{
  "code": "ok",
  "trace": {},
  "result": {
    "query": { "domain": "cmn.dev" },
    "cmn": { "uri": "cmn://cmn.dev", "public_key": "ed25519.5Xmk...", "mycelium_hash": "b3.abc...", "endpoints": { ... } },
    "replicates": [],
    "mycelium": {
      "$schema": "https://cmn.dev/schemas/synapse/mycelium.json",
      "capsule": {
        "uri": "cmn://cmn.dev",
        "core": {
          "name": "cmn.dev",
          "domain": "cmn.dev",
          "synopsis": "",
          "updated_at_epoch_ms": 1769787347698,
          "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 tools for CMN protocol (hypha, synapse, substrate)"
            }
          ]
        },
        "core_signature": "ed25519.5XmkQ9vZP8nL3xJdFtR7wNcA6sY2bKgU1eH9pXb4..."
      },
      "capsule_signature": "ed25519.5XmkQ9vZP8nL3xJdFtR7wNcA6sY2bKgU1eH9pXb4..."
    }
  }
}

GET /synapse/mycelium/:domain/spores

List all spore hashes published by a domain.

Response (200):

{
  "code": "ok",
  "trace": {},
  "result": {
    "query": { "domain": "cmn.dev" },
    "spores": [
      "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
      "b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5"
    ]
  }
}

GET /synapse/mycelium/:domain/spore/:hash

Retrieve a specific spore from a domain. Returns the full signed manifest if the spore belongs to the requested domain.

Response (200):

{
  "code": "ok",
  "trace": {},
  "result": {
    "query": { "domain": "cmn.dev", "hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2" },
    "spore": { ... }
  }
}

Taste API

GET /synapse/spore/:hash/tastes

Retrieve taste reports submitted by other domains for a spore. Returns aggregated verdict counts plus a flattened list of recent reports. Taste values and processing rules are defined in 03-spore §2.5.

Path Parameters:

Response (200):

{
  "code": "ok",
  "trace": {},
  "result": {
    "query": { "hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2" },
    "verdicts": {
      "sweet": 0,
      "safe": 1,
      "fresh": 0,
      "rotten": 1,
      "toxic": 0
    },
    "tastes": [
      {
        "target_uri": "cmn://cmn.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
        "domain": "alice.dev",
        "verdict": "safe",
        "notes": [],
        "tasted_at_epoch_ms": 1769787400000,
        "uri": "cmn://alice.dev/taste/b3.7tRkW2xPqL9nH4mYeZcFjA5sD8vBwKgU6pXb3",
        "replicates": ["mirror.net"]
      },
      {
        "target_uri": "cmn://cmn.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
        "domain": "bob.dev",
        "verdict": "rotten",
        "notes": ["Tests fail on ARM architecture"],
        "tasted_at_epoch_ms": 1769787500000,
        "uri": "cmn://bob.dev/taste/b3.8hVnR2xQmL5yK7p",
        "replicates": ["mirror.net"]
      }
    ]
  }
}

GET /synapse/mycelium/:domain/tastes

Retrieve taste reports for a domain target and its currently indexed mycelium target.

Path Parameters:

Response (200):

{
  "code": "ok",
  "trace": {},
  "result": {
    "query": {
      "domain": "cmn.dev"
    },
    "cmn": {
      "target": "cmn://cmn.dev",
      "verdicts": {
        "sweet": 0,
        "fresh": 0,
        "safe": 1,
        "rotten": 0,
        "toxic": 0
      },
      "tastes": [
        {
          "target_uri": "cmn://cmn.dev",
          "domain": "alice.dev",
          "verdict": "safe",
          "notes": [],
          "tasted_at_epoch_ms": 1769787400000,
          "uri": "cmn://alice.dev/taste/b3.7tRkW2xPqL9nH4mYeZcFjA5sD8vBwKgU6pXb3",
          "replicates": []
        }
      ]
    },
    "mycelium": {
      "hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
      "targets": [
        "cmn://cmn.dev/mycelium/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
        "cmn://mirror.net/mycelium/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2"
      ],
      "verdicts": {
        "sweet": 0,
        "fresh": 0,
        "safe": 1,
        "rotten": 0,
        "toxic": 1
      },
      "tastes": [
        {
          "target_uri": "cmn://mirror.net/mycelium/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
          "domain": "bob.dev",
          "verdict": "toxic",
          "notes": ["Manifest points to unsafe mirror endpoint"],
          "tasted_at_epoch_ms": 1769787500000,
          "uri": "cmn://bob.dev/taste/b3.8hVnR2xQmL5yK7p",
          "replicates": ["mirror.net"]
        }
      ]
    }
  }
}

Notes:

Pulse API

POST /synapse/pulse

Receive and index a signed spore or mycelium manifest (and taste reports). See Synapse Protocol §2 for the full specification including verification rules and error codes.

Search API

Requires the ruvector Cargo feature and search.enabled: true in config.

Semantic search over spore metadata using vector similarity.

Query Parameters:

Response (200):

{
  "code": "ok",
  "trace": {},
  "result": {
    "query": { "text": "HTTP client", "domain": null, "license": null, "bonds": null, "limit": 20 },
    "spores": [
      { "uri": "cmn://cmn.dev/b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2", "domain": "cmn.dev", "name": "reqwest-lite", "relevance": 0.92 },
      { "uri": "cmn://cmn.dev/b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5", "domain": "cmn.dev", "name": "hyper-client", "relevance": 0.87 }
    ]
  }
}

Error (503): Search engine not configured.

Graph API

Requires the ruvector Cargo feature and search.graph.enabled: true in config.

Semantic search over relationship edges. Requires search.graph.embed_relationships: true.

Query Parameters:

Response (200):

{
  "code": "ok",
  "trace": {},
  "result": {
    "query": { "text": "forked for security", "limit": 10 },
    "results": [
      {
        "edge_id": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2->b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5:spawned_from",
        "relation": "spawned_from",
        "child_hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
        "parent_hashes": ["b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5"],
        "score": 0.85
      }
    ]
  }
}

GET /synapse/graph/temporal

Query evolution events within a time range.

Query Parameters:

Response (200):

{
  "code": "ok",
  "trace": {},
  "result": {
    "query": { "start": 1700000000, "end": 1710000000 },
    "edges": [
      {
        "edge_id": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2->b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5:spawned_from",
        "relation": "spawned_from",
        "child_hash": "b3.3yMR7vZQ9hL2xKJdFtN8wPcB6sY1mXgU4eH5pTa2",
        "parent_hashes": ["b3.8cQnH4xPmZ2vLkJdRt7wNbA9sF3eYgU1hK6pXq5"],
        "score": 1.0
      }
    ]
  }
}

GET /synapse/graph/stats

Return graph statistics.

Response (200):

{
  "code": "ok",
  "trace": {},
  "result": {
    "graph": {
      "total_nodes": 12847,
      "total_edges": 9231,
      "avg_degree": 1.44
    }
  }
}

Error (503): Graph engine not configured.

Nostr Relay API

GET /synapse/nostr

WebSocket NIP-01 relay (CMN events only). See Synapse Protocol §4 for event format and relay details.

Error Codes

Pulse Error Codes

See Synapse Protocol §6 for the standardized pulse error codes that all Synapse implementations must support.

Query Error Codes

codeHTTPDescription
not_found404Spore or mycelium not in index
invalid_hash400Hash format is invalid
invalid_domain400Domain format is invalid
search_unavailable503Search engine not configured
search_failed500Search query failed
graph_unavailable503Graph engine not configured
graph_search_failed500Graph relationship search failed
graph_query_failed500Graph temporal query failed
graph_stats_failed500Graph stats query failed
field_missing400Required field is missing

Error Response Format:

Error type is carried in code directly — trace is only for operational metadata.

{
  "code": "not_found",
  "error": "Spore not found"

HTTP Status Codes

StatusDescription
200Request succeeded
400Invalid request format or unknown schema
401Signature verification failed
404Resource not found
422Invalid URI format
500Internal error
503Feature not configured (search/graph)

Configuration

Storage Options

OptionDescription
storage.type"redb" (embedded) or "postgres" (production)
storage.urlPostgreSQL connection string

Each backend requires its Cargo feature to be compiled in (postgres or redb). Both are enabled by default. See Synapse Storage for details.

HTTP/HTTPS Options

OptionDescriptionDefault
http.enabledEnable HTTP servertrue
http.portHTTP port3000
https.enabledEnable HTTPS serverfalse
https.portHTTPS port443
https.acme.enabledUse Let’s Encryptfalse
https.acme.renewal_before_daysRenew certs this many days before expiry30
https.self_signed_validity_daysSelf-signed certificate validity (days)365

Cache Options

OptionDescriptionDefault
cache.cmn_ttlcmn.json cache TTL in seconds300

Pulse Options

OptionDescriptionDefault
pulse.max_clock_skewMaximum allowed clock skew in seconds300

Search Options

Requires the ruvector Cargo feature (enabled by default).

OptionDescriptionDefault
search.enabledEnable semantic searchfalse
search.index_pathPath for vector index data"synapse-search"
search.embedding.providerEmbedding provider: "ollama" or "openai"
search.embedding.urlEmbedding API base URL
search.embedding.modelModel name (e.g., "bge-large")
search.embedding.api_keyAPI key (required for OpenAI)null
search.embedding.dimensionsEmbedding vector dimensions
search.graph.enabledEnable in-memory evolution graphfalse
search.graph.rebuild_on_startupRebuild graph from storage on boottrue
search.graph.embed_relationshipsEmbed edge descriptions (costs API calls)false
search.graph.rebuild_batch_sizeBatch size for startup rebuild100

When search.graph.enabled is true, bond queries are accelerated via in-memory graph traversal (falling back to storage on miss). The /synapse/graph/* endpoints are only available when the graph is enabled. Relationship search (/synapse/graph/search) additionally requires embed_relationships: true.

Nostr Options

Requires the nostr Cargo feature (enabled by default).

OptionDescriptionDefault
nostr.enabledEnable Nostr integrationfalse
nostr.relaysExternal relay URLs[]
nostr.key_storeKey storage: "storage" (database) or "file:<path>""storage"
nostr.subscribeListen for CMN events on external relaysfalse
nostr.forward_pulseForward HTTP pulses to Nostr relaysfalse

When nostr.enabled is true, the /synapse/nostr WebSocket endpoint is activated. A secp256k1 identity key is auto-generated on first run and persisted according to key_store.

CLI Flags

Rebuild the search index from storage on startup. Use this after schema changes, embedding model updates, or when the search index has become corrupted.

Prerequisites:

Usage:

synapse --rebuild-search

This triggers a full re-index: reads all spores from storage, recomputes embeddings, and rebuilds the vector index. When search.graph.enabled is also true, the in-memory graph is rebuilt simultaneously.

When to use:

Hash Format

Hashes use dot as separator everywhere: b3.<base58>. This format is used consistently across URIs, JSON fields, filenames, and API paths. No conversion is needed. Base58 uses the alphabet 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz (no 0, O, I, l). BLAKE3 hashes encode to ~44 base58 characters.