Skip to content

API and wire stability

SYNAPSE CHANNEL is in its pre-1.0 (0.x) line. This page states what counts as a stable surface, how each surface is guarded against accidental change, and what 1.0.0 will lock. It is the contract an integrator can rely on when pinning a version — in particular an out-of-tree consumer that builds on the core, such as the commercial fleet tier, which speaks the wire across hubs and imports the federation primitives directly.

See the 0.x to 1.0 migration guide for the operator upgrade runbook and release-cut checklist.

Versioning

Releases follow semantic versioning. In the 0.x line a minor release may change a public surface, but only as a reviewed edit to the pin that guards it (below), never silently. 1.0.0 locks the public Python API and the wire; after it, a breaking wire change bumps the wire-protocol version and a breaking API change bumps the major version.

The wire-protocol version is decoupled from the package version. synapse_channel.core.protocol.WIRE_PROTOCOL_VERSION (an integer, currently 2) changes only on a wire-incompatible change, so it is a stable compatibility signal rather than a release counter. The hub advertises it in the welcome handshake as protocol_version and in /health; a client reads the peer's version on connect as hub_protocol_version. Version mismatches are accepted with warning and negotiate down to the lowest common wire version. A peer that omits the field is treated as legacy wire version 1; a peer that advertises a future version is served at the local version until this build learns the newer capabilities.

The stable surfaces and how they are guarded

Each stable surface is pinned by a test, so a rename, removal, or value change fails CI rather than reaching a release:

Surface What is frozen Guard
Public Python API the exact package __all__ export list tests/test_public_api.py
Wire message vocabulary every MessageType name→value pair, the envelope's reserved keys, and the wire-protocol version tests/test_wire_surface_freeze.py
Federation primitives the deep core.* symbols out-of-tree consumers import — the persisted event shape, the multihub follower and its fetcher, claim forwarding, the federation store and its peer records, the TLS pin helper tests/test_federation_consumer_contract.py
CLI surface every subcommand is classified into a stability tier tests/test_surface_taxonomy.py
Capability counts class, module, wire-type, subcommand, and test counts the capability manifest (tools/capability_manifest.py --check)
Error taxonomy codes every domain exception's class→code pair tests/test_core_errors.py

The manifest pins counts, which catches an addition or removal; the freeze tests pin identities and values, which catches a rename that keeps a count constant. The two together close the gap either leaves open alone.

Error taxonomy

Every domain exception derives from synapse_channel.core.errors.SynapseError and carries a stable machine-readable code (snake_case), so a boundary layer — the CLI, the A2A bridge, the MCP server, an embedding application — can classify a failure without matching on message text (synapse_channel.core.errors.error_code(exc) returns the code, or "" for a foreign exception). Each class keeps its historical built-in base (ValueError, RuntimeError, PermissionError) through multiple inheritance, so pre-existing except clauses keep catching exactly what they caught before the taxonomy landed. A released code never changes meaning and is never reused; the registry test freezes the full map, and an AST drift gate refuses any new *Error class that does not join the taxonomy.

Boundary projections are explicit and frozen by focused tests. A2A validation, not-found, conflict, quota, and store codes map to HTTP 400, 404, 409, 429, and 500 respectively. Outbound MCP config, access, and tool codes map to CLI exits 2, 3, and 1. Foreign exceptions take the boundary caller's explicit default; neither projection parses exception text or infers semantics from wording. Caller-actionable 4xx responses retain their detail, while mapped 5xx responses omit exception text so internal paths and storage diagnostics stay server-side.

Stability tiers

Every CLI subcommand carries a tier (synapse_channel.surface_taxonomy):

  • stable — daily-safe coordination core with a stable wire and CLI surface.
  • analysis — read-only inspection and reporting with no coordination side effects.
  • governance — advisory governance: policy, approvals, access control, release integrity.
  • adapter — bridges to other ecosystems and tools; optional extras, not core.
  • experimental — newer or advisory surfaces still settling; shape may change before 1.0.

An experimental surface is explicitly outside the stability guarantee until it graduates to another tier.

Deprecation

Within 0.x, a surface removed or changed is a reviewed edit to its guard plus a CHANGELOG entry; a wire change also bumps WIRE_PROTOCOL_VERSION. After 1.0.0, a stable surface is removed only across a major version, with the prior behaviour kept for a deprecation window where feasible.