myelin 0.1.0

Define async service APIs as traits, communicate over channels. Transport and serialization are pluggable.
Documentation

myelin

Define async service APIs as traits, communicate over channels.

Myelin lets you define service interfaces as Rust traits and generates all the channel plumbing — request/response enums, typed clients, server dispatch loops, and transport adapters — via a proc macro.

Features

  • Trait-driven — define your API once, get client and server code generated
  • Transport-agnostic — local channels (tokio, embassy, smol) or serialized streams (postcard over stdio/TCP/UART)
  • no_std compatible — core library works without allocator; embassy transport uses static allocation for embedded targets
  • Cancel-safe — dropping a client future mid-await never corrupts state
  • Sync + async — generated clients and servers come in both async and blocking (BlockOn-based) variants for thread interop
  • Infallible transports — local transports return values directly (no Result wrapping) via the TransportResult trait

Quick Example

#[myelin::service]
pub trait GreeterService {
    async fn greet(&self, name: String) -> String;
    fn health(&self) -> bool;
}

This generates GreeterClient, GreeterRequest/GreeterResponse enums, greeter_serve(), GreeterServiceSync, and transport-specific helpers.

Transports

Transport Feature Use case
Tokio (mpsc + oneshot) tokio In-process, tokio async
Embassy (static Channel + Signal) embassy Embedded, no_std
Smol (async-channel) smol In-process, smol async
Postcard stream postcard Cross-process, serialized (stdio/TCP/UART)
CBOR stream cbor Cross-process, self-describing (nitro bus, nbusctl tail)

Unreleased

  • stream::codec: add CborCodec alongside PostcardCodec, gated on the new cbor Cargo feature (off by default, pulls in minicbor-serde). Produces a self-describing wire format suitable for debuggable IPC buses (e.g. nitro's nbus / nbusctl tail). Postcard remains the default; the new codec is additive and slots into StreamTransport / DuplexStreamTransport unchanged through CborCodecError, which unifies minicbor-serde's distinct encode/decode error types (the transport requires Encoder::Error == Decoder::Error).
  • stream::routing: add MuxedSlots::new_boxed() -> Box<Self>, which constructs the N × BUF slot array directly in a heap allocation. The existing MuxedSlots::new() builds the slot array on the stack first, so it overflows typical runtime worker stacks (e.g. tokio's 2 MiB default) for large N * BUF configurations. new_boxed keeps stack use bounded regardless of BUF.
  • stream::duplex::DuplexShared::slots is now Box<MuxedSlots<N, BUF>>, and DuplexStreamTransport::with_layers uses MuxedSlots::new_boxed(). This lets DuplexStreamTransport<_, _, _, _, 32, 131_072> construct on a 1 MiB stack — see the new duplex_construction_on_restricted_stack regression test. Hot paths (acquire, deliver, recv_reply, try_recv_slot) are unchanged — access goes through Box's auto-deref.
  • stream::transport: add with_boxed_router constructor; consumes Box<MuxedSlots<N, BUF>> from MuxedSlots::new_boxed(), avoids stack-overflow for BUF ≥ 256 KiB.

Provenance

This library was coded by Anthropic's Claude Opus using the tau agent.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.