Skip to main content
Add one dependency:
Install the official capnp compiler when setting up the development environment: brew install capnp on macOS, or sudo apt-get install capnproto on Debian/Ubuntu. During compilation, the attribute macro invokes that compiler and uses the official capnpc generator for Rust bindings. Set RYDANTIC_CAPNP_COMPILER to use a specific compiler executable. Bare names (such as capnp) use PATH; relative paths (such as ./tools/capnp) are resolved from the compiler process’s working directory before entering the temporary schema directory.
Derive rydantic::Model explicitly on each model (an imported alias also works). capnp_models generates the schema and binding attributes; Rust resolves and runs the derives you write. Other derives such as Debug and PartialEq are preserved. The module exposes:
  • models::wire: public Rust Reader, Builder, and Enum bindings from the official generator.
  • models::CAPNP_SCHEMA and CAPNP_SCHEMA_ID: the complete schema text and file ID.
  • CapnpSchema for each model: capnp_schema(), write_capnp_schema(path), and the root type name CAPNP_TYPE. Unit enums also support binary roundtrips as root messages through automatically generated root wrapper types.
Default mappings cover nested and recursive models, Box, Option, Vec, fixed arrays, String-keyed BTreeMap/HashMap, unit enums, and data enums. Vec<u8> becomes Data; full-range i128/u128, dates, and Decimal become Text, including inside containers. Module-local type aliases, container aliases with concrete type arguments, explicit use aliases, and inline submodules work together. Dates and Decimal require the chrono and decimal features respectively. Models must use concrete, owned types within the annotated inline module. Data enums still declare their JSON representation: named fields can use #[model(tag = "kind")], while tuple variants use #[model(untagged)]. capnp_name controls the protocol name independently. capnp_skip with a default preserves a local field. JSON aliases, flattening, extra fields, and exclusion continue to control the JSON representation. Unsupported external types can use a default with capnp_skip, or the existing-schema integration.

Breaking change in 0.7.0-beta.1: explicit Model derives

Version 0.7.0-beta.1 removes automatic Model derive insertion inside capnp_models. Existing code that relied on it will no longer compile. Add #[derive(rydantic::Model)] to every model struct and enum in the annotated module, including nested modules, or add rydantic::Model to the model’s existing derive list. With a renamed dependency, use that dependency’s name instead. Already-explicit derives, including aliases imported outside the annotated module, need no change. An unrelated derive named Model does not implement Rydantic’s model traits. This is an intentional source-breaking change. Review and update your application before upgrading; the library does not maintain the old implicit-derive behavior. The derive change alone does not change the generated wire schema.

Protocol IDs and compatible evolution

The default file ID is derived deterministically from the Cargo package name and the current model module name. For production protocols, set a fixed namespace = "my_app.users" or id = 0x.... Give distinct namespaces to multiple model modules with the same name in one package. Keep the namespace/ID, model names, and submodule paths stable after publication. Fields and enum members are numbered from @0 in declaration order by default. Appending fields preserves existing ordinals. To reorder declarations, pin each wire field or enum member with #[model(capnp_ordinal = N)]; ordinals must be unique, contiguous, and start at zero. For example:
Named and tuple group fields in data enums share the protocol’s ordinal space. A variant’s capnp_ordinal specifies its first field ordinal; fields can also have explicit ordinals. Type changes, field removal, renaming, and union regrouping must follow the official compatibility rules. Export schemas for code review; Rust bindings update automatically with the models. See fixtures/auto-capnp for complete examples and interoperability tests:

Conditional compilation

capnp_models lets Rust evaluate cfg and cfg_attr in the consuming crate before collecting its models and generating the schema. Disabled inline modules, models, fields, enum variants, aliases and imports are omitted. This includes nested cfg_attr and consumer features or target predicates; predicates are not evaluated in the procedural macro’s host environment. Active declarations still have to meet all of the normal model restrictions. Out-of-line modules (mod file;) are not supported, including inside a disabled declaration on stable Rust. Wire compatibility warning: the exported schema describes only the active configuration. Conditional fields or variants can change implicit ordinals and the wire layout. A shared namespace or file ID does not make different configurations compatible. Use separate schema identities for distinct protocols, or use an explicit schema-first protocol when different builds must exchange stored or network data. Keep and review the exported schema before deploying a changed configuration, and back up existing data before any incompatible migration. This feature does not migrate or preserve data automatically.