Skip to content

aimdb-dev/aimdb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

202 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AimDB

Distributed by design. Data-driven by default.

Stars Release Crates.io Build License

AimDB turns data contracts into the architecture. Define your schemas once — with built-in versioning, observability and serialization — and deploy them unchanged across microcontrollers, edge gateways, Kubernetes and the browser.


Vision

A future where every system — from a $2 sensor to a global fleet — shares one data language. Contracts define how data moves, evolves and is observed. Infrastructure adapts to the data, not the other way around.


Design Philosophy

In a data-driven architecture, every design decision starts with the data, not the service that produces it.

Records declare their own semantics. When you register a record in AimDB, you choose a buffer type that defines how the data moves:

Buffer Semantics Use Cases
SPMC Ring Bounded stream with independent consumers Sensor telemetry, event logs
SingleLatest Only the current value matters Feature flags, configuration, UI state
Mailbox Latest instruction wins Device commands, actuation, RPC

These are the three universal primitives of data movement — portable, typed and runtime-agnostic.

Observability becomes automatic. A record that exists is observable by definition. Every producer and consumer relationship is declared in the builder, not discovered through instrumentation.

Synchronization becomes declarative. You don't build a sync layer between your MCU, edge gateway and cloud backend. You declare a record with connector metadata on its key and the same typed data flows across all environments without translation.

Cross-cutting concerns derive from the schema. Instead of adding observability libraries, feature flag SDKs and experiment frameworks as separate integrations, they become intrinsic properties of records — declared once, applied everywhere.


How It Works

Define your contracts, choose buffer semantics and wire up connectors — all in one builder block:

// A sensor node: produce temperature readings, publish over MQTT
builder.configure::<Temperature>("sensor::temp", |reg| {
    reg.buffer(BufferCfg::SpmcRing { capacity: 256 })
        .source(|ctx, producer| async move {
            loop {
                let reading = read_sensor().await;
                producer.send(Temperature::set(reading, now())).await.ok();
                ctx.sleep(Duration::from_secs(1)).await;
            }
        })
        .link_to("mqtt://sensors/temperature")
        .with_serializer(Temperature::to_bytes)
        .finish();
});

// An edge gateway: receive from MQTT, observe and forward
builder.configure::<Temperature>("gateway::temp", |reg| {
    reg.buffer(BufferCfg::SingleLatest)
        .link_from("mqtt://sensors/temperature")
        .with_deserializer(Temperature::from_bytes)
        .tap(log_tap::<Temperature>("edge"))   // [edge] 22.5 °C
        .finish();
});

Transport topics can be passed as strings to link_to / link_from, or declared on key enums with #[link_address = "mqtt://..."] and resolved at runtime. No separate instrumentation. No SDK integration. No sync code.


Data Contracts

Data contracts are the heart of AimDB. A contract is a plain Rust struct that carries its own identity, version and capabilities — the single source of truth from sensor firmware to browser UI.

use aimdb_data_contracts::{SchemaType, Settable};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Temperature {
    pub celsius: f32,
    pub timestamp: u64,
}

impl SchemaType for Temperature {
    const NAME: &'static str = "temperature";
    const VERSION: u32 = 1;
}

impl Settable for Temperature {
    type Value = f32;
    fn set(value: Self::Value, timestamp: u64) -> Self {
        Self { celsius: value, timestamp }
    }
}

This struct compiles for no_std embedded targets and standard Rust alike. SchemaType gives the record its identity and version. Settable provides a canonical constructor so producers can create records from a raw value — this is the interface used by producer.send(Temperature::set(reading, now())) in the builder.

Contract Attributes

Contracts gain capabilities through trait implementations. Each trait is a compile-time declaration of what a contract can do, not a runtime configuration:

Attribute Trait What It Enables
Settable Settable Canonical constructor from a raw value — the interface behind producer.send(T::set(value, ts)).
Streamable Streamable Cross-boundary transport — WASM, WebSocket, CLI. One registry, zero parallel type systems.
Migratable MigrationStep Bidirectional schema evolution with typed up/down transforms and chained version steps.
Observable Observable Signal extraction for thresholds, logging and monitoring. Icon, unit and format_log() built in.
Linkable Linkable Wire-format serialization for connectors — MQTT, KNX and any future transport.
Simulatable Simulatable Realistic test data generation with random walks, trends and configurable parameters.

For example, Observable turns a contract into a loggable, monitorable signal:

impl Observable for Temperature {
    type Signal = f32;
    const ICON: &'static str = "thermometer";
    const UNIT: &'static str = "°C";

    fn signal(&self) -> f32 { self.celsius }

    fn format_log(&self, node_id: &str) -> String {
        format!("[{}] {:.1} °C", node_id, self.celsius)
    }
}

Each trait you implement unlocks a capability — contracts without Observable simply can't be tapped; contracts without Linkable can't be wired to connectors. The type system enforces what your data can do.

Platform-Agnostic by Design

The same contract works across all runtimes without modification:

┌───────────────────────────────────────────────────────────────────────────────┐
│                           Temperature Contract                                │
├───────────────────┬───────────────────┬───────────────────┬───────────────────┤
│  MCU (Embassy)    │  Edge (Tokio)     │  Cloud (Tokio)    │  Browser (WASM)   │
│  no_std + alloc   │  std              │  Kubernetes       │  wasm32           │
│  Cortex-M4        │  Linux / RPi      │  Full featured    │  Single-threaded  │
└───────────────────┴───────────────────┴───────────────────┴───────────────────┘

The Rust type system enforces correctness at compile time. The dataflow engine's buffer semantics enforce flow guarantees at runtime. Connectors wire everything to your infrastructure without an integration layer.


Getting Started

1. See it live

Explore a running sensor mesh — no setup required:

AimDB Live Demo

aimdb.dev — live weather stations streaming typed contracts across MCU, edge and cloud.

2. Run locally

Spin up a full MCU → edge → cloud mesh with one command:

cd examples/weather-mesh-demo
docker compose up

This starts three weather stations, an MQTT broker and a central hub — all wired together with typed data contracts.

3. Explore with AI

With the mesh running, connect an MCP-compatible editor to query your data in natural language:

AimDB MCP Live Demo

Install the MCP server and add it to your workspace:

cargo install aimdb-mcp

.vscode/mcp.json:

{
  "servers": {
    "aimdb": {
      "type": "stdio",
      "command": "${userHome}/.cargo/bin/aimdb-mcp"
    }
  }
}

Then ask: "What's the current temperature from station alpha?" — see the MCP server docs for Claude Desktop and other editors.

4. Build your own

  • Quick Start Guide — Dependencies, platform setup and your first contract
  • Data Contracts — Type-safe schemas with built-in capabilities
  • Connectors — MQTT, KNX, WebSocket and more
  • Deployment — Running on MCU, edge, cloud and browser
  • API Reference — Full Rust API documentation
  • Blog — News, tutorials and insights from the AimDB team

Connectors

Protocol Crate Status Runtimes
MQTT aimdb-mqtt-connector ✅ Ready std, no_std
KNX aimdb-knx-connector ✅ Ready std, no_std
WebSocket aimdb-websocket-connector ✅ Ready std, wasm
Kafka 📋 Planned std
Modbus 📋 Planned std, no_std

Platform Support

Target Runtime Adapter Features Footprint
ARM Cortex-M (STM32H5, STM32F4) Embassy aimdb-embassy-adapter no_std, async ~50KB+
Linux Edge (RPi, gateways) Tokio aimdb-tokio-adapter Full std ~10MB+
Containers / K8s Tokio aimdb-tokio-adapter Full std ~10MB+
Browser / SPA WASM aimdb-wasm-adapter wasm32, single-threaded ~2MB+

Contributing

Found a bug or want a feature? Open a GitHub issue.

Have questions or ideas? Join the discussion on GitHub Discussions.

Want to contribute? See the contributing guide. We have good first issues to get started.


License

Apache 2.0


Define once. Deploy anywhere. Observe everything.

Get started · Live demo · Join the discussion