bytesandbrains 0.3.6

Composable building blocks for decentralized + federated machine learning.
Documentation

bytesandbrains is a sans-IO Rust framework for authoring decentralized and federated learning systems. You describe what should run as a Module — a struct whose body() method records your computation into an ONNX ModelProto. The framework partitions the graph across BB Nodes, inlines role behavior, dispatches each op to one of your bound runtime impls, and routes inter-Node values via a single wire envelope.

Status: v0.3 — the canonical authoring + runtime surface. See docs/ for the design specification.

Quick start

cargo add bytesandbrains
[dependencies]
bytesandbrains = "0.3"
use std::task::{Context, Poll, Waker};

use bytesandbrains::placeholders::{DataLoaderSlot, ModelSlot};
use bytesandbrains::{install, Address, Compiler, Config, Graph, Module, PeerId};

/// A federated-learning client round. Pull a batch from the local
/// dataset, run a forward pass through the bound model, and publish
/// the locally-updated parameters for an aggregator to fold across
/// peers in a later round.
pub struct FederatedClient;

impl Module for FederatedClient {
    fn name(&self) -> &str { "FederatedClient" }
    fn body(&self, g: &mut Graph) {
        let (batch, _labels) = DataLoaderSlot.next_batch(g);
        let _prediction      = ModelSlot.forward(g, batch);
        let params           = ModelSlot.params(g);
        g.output("params", params);
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // You bring concrete `Model` and `DataSource` impls — derive
    // `bb::Concrete` + `bb::Model` / `bb::DataSource` over your
    // types and implement the matching Contract trait. The compiler
    // binds them to named slots at compile time; the engine never
    // sees the concrete types directly. See
    // `examples/single_node_federated_learning.rs` for ~100 lines
    // of minimal stubs you can copy.
    let compiled = Compiler::new()
        .bind_data_source::<MyDataLoader>("data_source")
        .bind_model::<MyModel>("model")
        .compile(FederatedClient.build()?)?;

    let peer = PeerId::from(0u64);
    let mut node = install(
        peer,
        vec![Address::empty().p2p(peer)],
        compiled,
        &["FederatedClient"],
        Config::new(),
    )?;

    // Drive the Node's poll loop on your runtime of choice.
    let waker  = Waker::noop();
    let mut cx = Context::from_waker(waker);
    while let Poll::Ready(_steps) = node.poll(&mut cx) {}
    Ok(())
}

The framework is sans-IO: the Node is a state machine; the caller drives poll() and ships outbound envelopes. There's no tokio in src/; transport adapters live outside the core crate.

Mental model in 60 seconds

  • Module = your code. Implements name() + body(). Every DSL call (self.backend.matmul(g, a, b)) records a NodeProto.
  • Module::build() returns a recorded ModelProto.
  • Compiler::new().bind_<role>::("slot").compile(model) runs the 18-pass canonical pipeline and stamps the result with the compilation passport + binding-table metadata.
  • bb::install(peer, addr, compiled, target, config) verifies the passport, constructs every bound concrete via the inventory, and returns a ready-to-poll Node.
  • Engine dispatches each op by (domain, op_type, instance) to one bound dispatch_atomic per instance. The wire opset is the one universal cross-Node transport.
  • ConcreteComponent trait is the polymorphism contract: TYPE_NAME + serialize + restore. Derives (bb::Concrete, bb::<Role>) emit the inventory submission that lets the installer find each type by name.

See docs/AUTHORING_COMPONENTS.md for the library-writer + extension-author walkthrough.

Workspace

Crate Notes
bytesandbrains Facade — re-exports the six workspace crates as one surface. End users depend on this.
bb-ir Foundation: prost-generated ONNX + bb.core proto bindings, wire envelope, type lattice, ids, atomic-op declarations.
bb-dsl Authoring surface: Module, Graph recorder, Output, Contract traits, placeholders.
bb-compiler Compilation pipeline (the 18 canonical passes) + Compiler driver + CompileError.
bb-runtime Sans-IO Engine + Node + ingress + envelope codec + <Role>Runtime trait surfaces + inventory registry.
bb-ops Concrete components: CPU backend, wire transport, syscall implementations, role placeholders, bundled protocols.
bb-derive Proc-macros: #[derive(bb::Concrete)], #[derive(bb::<Role>)], register_op!{}, register_protocol!{}.

All seven crates ship at the workspace version in lockstep, managed by release-plz.

Documentation

The canonical design specification lives under docs/:

  • API_DESIGN.md — Module trait + Graph DSL + install API.
  • AUTHORING_COMPONENTS.md — library-writer + app-extension walkthrough.
  • ROLES.md — the eight <Role>Runtime traits (Backend, Model, Index, Aggregator, Codec, DataSource, PeerSelector, Protocol).
  • COMPILER.md — the 18-pass compilation pipeline.
  • ENGINE.md — the sans-IO Engine + per-Node atomic dispatch.
  • IR_AND_DSL.md — how Module + Graph map onto ONNX ModelProto.
  • WIRE.md — wire envelope + transport-plane mechanics.
  • CHANGELOG.md — release notes.

License

Dual-licensed:

The dual-licensing model matches the framework's intended use: build open infrastructure for decentralized ML, or ship a commercial product with a clean license.

Contributing

See CONTRIBUTING.md for build/test/commit conventions. Issues are open; external pull requests are accepted by invitation during the v0.x stabilization period. Security disclosures go through SECURITY.md.