AimDB v2.0.0 Release Notes
One wire protocol, one runtime abstraction, three new transports.
Typed dataplane for distributed systems
📄 Full release notes: docs/releases/v2.0.0.md
🎯 Highlights
AimDB 2.0 converges every transport on a single wire protocol (AimX 3.0), removes the runtime generic R from the public API, retires aimdb-executor, and adds Unix-socket, serial and TCP transports alongside the existing MQTT, KNX and WebSocket connectors. The simplification passes (designs 034/036/037/038) cut ~2,500 lines from the library crates with no capability loss.
This is a breaking release. A pre-3.0 client is now refused at the handshake rather than failing on its first call, so servers and clients must be upgraded together.
💥 Breaking Changes
1. AimX 3.0 — one protocol for every transport
aimdb-ws-protocol is deleted. The WebSocket connector and the browser WsBridge now speak the same AimX tagged frames as UDS, serial and TCP, over the same session engine. AimX gained the two features that justified the old WS fork: wildcard/multi-record subscribe and shared record.query / record.list result shapes.
PROTOCOL_VERSION moves "2.0" → "3.0", and hello now negotiates instead of ignoring the version. Compatibility is by major version; a missing or malformed version fails closed with RpcError::VersionMismatch.
record.query results changed shape:
Browser clients must declare the version in the upgrade URL — a browser cannot set WebSocket handshake headers, so the server refuses an incompatible or absent version with HTTP 426 Upgrade Required before the socket opens. The JS WsBridge API is unchanged and appends it automatically; hand-rolled clients need …/ws?v=3.0. See aimdb_core::remote::{VERSION_PARAM, ws_url_with_version}.
Caveat (design 047 §3.6): server-seeded auto-subscriptions are invisible to
run_client-based consumers. Drive your ownsubscribefor records you want streamed.
2. Dot is the only topic separator
topic_matches now splits patterns on . only — RabbitMQ topic-exchange semantics (* one segment, # zero or more). Previously it split on /, so a dot-separated key like temp.vienna silently failed to match temp.*. / survives only inside external broker addresses (mqtt://sensors/temp/x), which are unaffected.
- ws.subscribe("sensors/#")
+ ws.subscribe("sensors.#")3. The runtime generic R is gone
Records (T) are the only generic surface left; the runtime travels as Arc<dyn RuntimeOps>. AimDb, AimDbBuilder, TypedRecord<T>, RecordRegistrar<'a, T>, TransformBuilder<I, O>, JoinBuilder<O> and ConnectorBuilder are all non-generic over the runtime, and there is no NoRuntime typestate.
- async fn task(mut rx: Consumer<Temperature, TokioAdapter>) { … }
+ async fn task(mut rx: Consumer<Temperature>) { … }RuntimeContext is now a concrete struct: time().now() returns u64 nanos, with sleep(Duration) / sleep_millis / sleep_secs.
Close semantics changed on Embassy: a join fan-in queue now closes on all runtimes once every input forwarder exits, so a no_std handler's while let Ok(_) = rx.recv().await loop ends instead of parking forever. Treat Err(QueueClosed) as end-of-inputs.
4. aimdb-executor is retired
RuntimeOps, LogLevel, BoxFuture and ExecutorError / ExecutorResult now live in aimdb_core::executor, re-exported at the crate root. The superseded generic trait family (RuntimeAdapter, TimeOps, Logger, Runtime, RuntimeInfo) and aimdb_core::time are deleted — adapters implement one trait instead of four.
5. Remote access is a connector
AimDbBuilder::with_remote_access(config) is removed. Register a session connector instead; it binds synchronously, so bind errors still surface from build().
6. CLI and MCP take an endpoint, not a socket path
The per-command --socket flags are gone in favour of a global --connect <scheme://url> (unix://, serial://DEVICE?baud=N, or a bare path). AIMDB_SOCKET → AIMDB_CONNECT; every MCP tool's socket_path parameter is renamed endpoint. discover_instances keeps socket_path.
7. Capability traits and codecs (aimdb-data-contracts)
linkable is now format-neutral — derive users must enable the new linkable-json. Simulatable::simulate takes an associated Self::Params (SimulationConfig / SimulationParams are removed); Observable loses ICON and format_log() (use ObservableRegistrarExt::log(node_id)); Settable moves behind a new settable feature.
8. Smaller surface changes
DbErrorandSyncErrorare#[non_exhaustive]— add a wildcard arm, or match on the newkind()/DbErrorKind.BufferReaderis poll-based (design 037 W8), removing the last per-message heap allocation on the consume path.ConsumerTrait::subscribe_anyis infallible — drop theOk(…)wrap and the deadErrarm.RecordMetadatadropscreated_at/last_update; core keeps no wall-clock state for theno_stdAimX server.with_serializer_raw/with_deserializer_raware gone — use.with_serializer(|_ctx, v| …).- The
stdfeature no longer pullstokio. Depend on it directly if you relied on the transitive dependency. - MQTT knobs (
with_qos/with_retain) leave the generic link builders — useaimdb-mqtt-connector'sMqttLinkExt, or genericwith_config(key, value).
✨ Also New
- Three transports —
aimdb-uds-connector,aimdb-serial-connectorandaimdb-tcp-connector, each contributing only aDialer/Listener/Connectiontriple over the shared session engine. One code path serves both Tokio and Embassy. - A
logdestination for FFI hosts (design 050). An optional, non-defaultlogfeature onaimdb-coresits alongsidetracing, for hosts that cannot be handed a process-global subscriber. Every crate that reports now goes through the same facade; with the feature off — the default, and every MCU build — nothing changes. - Per-link record codecs (#178). One
Linkabletype can select JSON, bounded Postcard or a custom codec independently per route, fused at registration time with no runtime registry. ConnectorBuilder::owns_scheme— a connector can declare it must be the only one registered under its scheme;build()rejects a collision by name instead of silently double-publishing every route.AimDbHandle::shutdown(&self)/is_closed()— the shutdown contract a foreign-language binding needs. A shutdown now also releases the database, waking a consumer parked inget().- Faster remote reads (#196). Typed records serialize straight to JSON bytes: 16 → 6 allocations per reply/event and a 1.55×–1.61× speedup on the measured host.
🔒 Security & Correctness Fixes
- A non-terminal
#no longer matches everything after it.topic_matches("a.#.secret", "a.public")returnedtrue, so an ACL grant of that shape behaved like a blanketa.#over the whole subtree.#now absorbs zero or more segments with the pattern continuing afterwards. Only ever tightens what an interior#admits; trailing#and*grants are unaffected. - SQLite queries honour pattern semantics. The
*→ SQL%rewrite is gone:%crosses., sosensors.*also returnedsensors.secret.deep— outside the pattern, and on the WebSocket path outside the grant that authorized it. - A peer's
seqcan no longer crash the client demux. An update withseq == u64::MAXoverflowed on the next frame — a debug-build panic from one crafted frame — and a repeated or reorderedseqinvented phantom gaps. - An abandoned RPC call no longer leaks.
ClientHandle::callis now cancel-safe; a timed-out request frees its pending-call entry inO(1)instead of growing the demux map for the life of the connection.
🚀 Migration Guide
Step 1 — swap the executor import.
- use aimdb_executor::{RuntimeOps, BoxFuture};
+ use aimdb_core::{RuntimeOps, BoxFuture};Custom adapters implement RuntimeOps only; delete the RuntimeAdapter / TimeOps / Logger / Runtime impls.
Step 2 — drop the runtime type parameter.
- Producer<Temperature, TokioAdapter> Consumer<Temperature, TokioAdapter>
+ Producer<Temperature> Consumer<Temperature>Regenerate any aimdb-codegen scaffolds — the emitted configure_schema is now non-generic (fn configure_schema(builder: &mut AimDbBuilder)) and the emitted prelude imports from aimdb_core only.
Step 3 — replace with_remote_access.
let db = AimDbBuilder::new()
- .with_remote_access(config)
+ .with_connector(aimdb_uds_connector::UdsServer::from_config(config))
.build()?;Step 4 — move subscriptions to dot patterns.
- ws.subscribe("sensors/#")
+ ws.subscribe("sensors.#")Step 5 — update tooling invocations.
- aimdb record list --socket /tmp/app.sock
+ aimdb record list --connect unix:///tmp/app.sock
- export AIMDB_SOCKET=/tmp/app.sock
+ export AIMDB_CONNECT=unix:///tmp/app.sockStep 6 — if you use #[derive(Linkable)], enable linkable-json.
- aimdb-data-contracts = { version = "0.2", features = ["linkable"] }
+ aimdb-data-contracts = { version = "0.2", features = ["linkable-json"] }Step 7 — upgrade clients and servers together. A 2.x client cannot talk to a 1.x server, and vice versa.
📦 Published Crates
New
| Crate | Version |
|---|---|
aimdb-uds-connector |
0.1.0 |
aimdb-serial-connector |
0.1.0 |
aimdb-tcp-connector |
0.1.0 |
Updated
| Crate | Version | Crate | Version | |
|---|---|---|---|---|
aimdb-core |
1.1.0 → 2.0.0 | aimdb-persistence |
0.1.1 → 0.2.0 | |
aimdb-derive |
0.1.0 → 0.2.0 | aimdb-persistence-sqlite |
0.1.1 → 0.2.0 | |
aimdb-data-contracts |
0.1.1 → 0.2.0 | aimdb-mqtt-connector |
0.6.0 → 0.7.0 | |
aimdb-client |
0.6.0 → 0.7.0 | aimdb-knx-connector |
0.4.0 → 0.5.0 | |
aimdb-codegen |
0.2.0 → 0.3.0 | aimdb-websocket-connector |
0.2.0 → 0.3.0 | |
aimdb-embassy-adapter |
0.6.0 → 0.7.0 | aimdb-wasm-adapter |
0.2.0 → 0.3.0 | |
aimdb-tokio-adapter |
0.6.0 → 0.7.0 | aimdb-cli |
0.6.0 → 0.7.0 | |
aimdb-sync |
0.5.0 → 0.6.0 | aimdb-mcp |
0.8.0 → 0.9.0 |
Retired
aimdb-executor— folded intoaimdb_core::executor. Last published version 0.2.0.aimdb-ws-protocol— superseded by AimX. Last published version 0.1.0, which speaks a protocol 2.0.0 refuses at the handshake.
📖 Design Documents
- 036–038 — follow-up refactoring, zero-alloc consume path, and the technical-debt/simplification review
- 041 — data-contracts integration: capability traits as first-class verbs
- 045 — per-link codec selection
- 047 — retire the WS protocol, converge on AimX
- 050 — a
logdestination for FFI hosts - 052 — runtime-neutral connectors
🤝 Contributing
git clone https://github.com/aimdb-dev/aimdb.git
cd aimdb
make check # fmt + clippy + test + embedded + wasm cross-compile📄 License
Apache License 2.0 — see LICENSE.