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.
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_SCHEMAandCAPNP_SCHEMA_ID: the complete schema text and file ID.CapnpSchemafor each model:capnp_schema(),write_capnp_schema(path), and the root type nameCAPNP_TYPE. Unit enums also support binary roundtrips as root messages through automatically generated root wrapper types.
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 automaticModel 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 fixednamespace = "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:
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.