dyn-encoding
Wire-format codec abstraction for the Riak protocol layer of the Dynomite Rust port.
The Riak protocol has historically pinned its on-the-wire encoding to Google Protocol Buffers. Modern Riak deployments and the operator brief that drives this workspace want to negotiate a richer set of encodings on a per-request basis, so that clients that already speak JSON, CBOR, or one of the schema-first contenders (FlatBuffers, Cap'n Proto, Bebop) can talk to the same server without first shoehorning their messages through protobuf.
This crate provides the abstraction. It does not implement the
Riak protocol itself; that lives in the (forthcoming) dyniak
crate and consumes dyn-encoding to negotiate the wire format on
each connection.
Scope
Encoding negotiation applies only to the Riak protocol layer. Redis RESP, Memcached ASCII, and the internal DNODE peer-plane framing are unchanged.
Trait surface
Two cooperating traits.
WireTypeId is a newtype around &'static str keyed into the codec
registry. ErasedWireValue is the object-safe view over WireValue
that codec implementations consume; a blanket impl on every
T: WireValue makes the coercion implicit at call sites.
The CodecRegistry maps content-type headers (for example
application/json) to &dyn WireCodec:
let mut registry = with_baseline;
let codec = registry.for_content_type.unwrap;
Baseline codecs
Seven codecs ship in version 0.0.1.
| Module | Content-type | Backend |
|---|---|---|
json |
application/json |
serde_json |
cbor |
application/cbor |
ciborium |
protobuf |
application/x-protobuf |
prost |
flatbuffers |
application/octet-stream;schema=flatbuffers |
flatbuffers |
capnp |
application/capnproto |
capnp |
bebop |
application/x-bebop |
bebop |
bson |
application/bson |
bson |
All seven follow the same registration pattern: types are attached
to a codec instance through register::<T>() before the codec is
installed in the registry. The bound on T differs per codec:
JsonCodec::register::<T>()--T: WireValue + Serialize + DeserializeOwnedCborCodec::register::<T>()--T: WireValue + Serialize + DeserializeOwnedBsonCodec::register::<T>()--T: WireValue + Serialize + DeserializeOwnedProtobufCodec::register::<T>()--T: WireValue + prost::Message + DefaultFlatbuffersCodec::register::<T>()--T: FlatbuffersWireCapnpCodec::register::<T>()--T: CapnpWireBebopCodec::register::<T>()--T: BebopWire
The protobuf codec deliberately avoids prost-build. The three
schema-first newcomers similarly avoid their respective code
generators (flatc, capnpc, bebopc); each defines a small
per-codec trait so the schema and conversion glue live in the
crate that owns the message types (typically dyniak), not in
this codec abstraction. For testing, hand-rolled fixtures live
inline in each codec module's tests block.
Adding a codec
The trait surface is shaped to host new encodings without churn:
- Add the upstream crate to
[workspace.dependencies]in the rootCargo.toml. - Add a feature-flagged or unconditional dependency in
crates/dyn-encoding/Cargo.toml. - Create
src/codec/<name>.rsmodeled on the JSON codec module. The bound onregister::<T>()is the new format's native trait (flatbuffers::Follow + Push,capnp::Owned, ...), or a crate-defined trait if the upstream API does not fit. - Add
pub mod <name>;tosrc/codec/mod.rsand the correspondingpub useinsrc/lib.rs. - Add the codec to
CodecRegistry::with_baselineif it should be on by default. - Mirror the test suite from JSON/CBOR/protobuf: round-trip, idempotent encode, unknown-type-id (encode + decode), malformed-bytes.
The deferred-codec sketches and the on-the-ground deltas are in
docs/journal/2026-05-24-dyn-encoding-scaffold.md and
docs/journal/2026-05-24-dyn-encoding-deferred-codecs.md.
Acknowledgements
The Dynomite Rust port descends from Netflix's Dynomite (C). The encoding-negotiation idea is informed by the design notes at https://gist.github.com/MangaD/77dba2f4c7055b35637fb596c175ffb1; see that gist for the comparative analysis of the seven encodings.