Skip to main content

Module api

Module api 

Source
Expand description

The single API layer (D60/D61).

§Dated API versions

An API version is a dated module (phoxal::api::y2026_1, …) generated by phoxal_api_tree!. Each version module carries:

  • a zero-variant marker enum Api {} implementing ApiVersion, whose ApiVersion::ID is the dated module name ("y2026_1") - the canonical version identity;
  • the version-local wire bodies, one pub mod per contract node holding plain serde structs/enums and their ContractBody impls;
  • an api-local topic builder rooted at topic::new().

Versions can build on earlier ones with version y2026_2 extends y2026_1: every inherited node and type is re-emitted fresh under the new module - same ContractBody::FAMILY and ContractBody::TOPIC, a different Api - so an unchanged contract stays wire-identical while the compile-time Api bound stays distinct. See phoxal_api_tree! for the inheritance and shape-identity rules.

§One API version per runtime, one per graph

A runtime authors against exactly one version module (use phoxal::api::y2026_1 as api;) and declares it on the derive (#[phoxal(api = y2026_1)]). Every handle body is bound ContractBody<Api = R::Api>, so passing a body from another API version is a compile error (D59/D60). At deploy time a single graph runs a single API version: all of its runtimes share one Api, so contracts on the bus are mutually consistent by construction (D59).

§Plain serde wire bodies, version in metadata

A wire body is just its serde encoding - there is no {"v":…} envelope or any other version tag inside the payload (D62). The three pieces of routing identity travel as bus metadata alongside the encoded body, not in it:

Keeping these out of the payload means the body bytes for an unchanged contract are identical across versions and codecs; the envelope/metadata layer owns version and family.

§Family and topic

Both identifiers are derived from the contract node’s path in the tree, never written by hand:

  • ContractBody::FAMILY is the ::-joined node path plus the body type name, e.g. component::motor::Command. It excludes dynamic variables, so every instance of a per-instance node shares one family.
  • ContractBody::TOPIC is the versionless /-joined key with each dynamic node contributing a {var} placeholder, e.g. component/{instance}/motor/{capability}/command. A fully static path has a literal key (drive/state).

§The api-local topic builder

Each version module exposes a topic builder that mirrors the node tree: api::topic::new() returns a root, one method per top-level node walks down the tree, a dynamic node’s method takes its variable as impl Display, and a leaf method binds the topic’s kind to its version-local body. For example api::topic::new().drive().state() yields a Topic<PubSub<drive::State>> over the static key drive/state, and api::topic::new().component("base").motor("left").command() fills the dynamic segments to produce component/base/motor/left/command. Because the builder is generated from the same tree as FAMILY/TOPIC, the built key and the documented key stay in lockstep.

Modules§

y2026_1
Dated API version #id — version-local wire bodies + topics.

Traits§

ApiVersion
Marker trait identifying one dated API version (D60).
ContractBody
A version-local wire body: a plain serde type bound to exactly one ApiVersion and one contract family/topic (D61).