All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Note: This is the global changelog for the AimDB project. For detailed changes to individual crates, see their respective CHANGELOG.md files:
- aimdb-core/CHANGELOG.md
- aimdb-codegen/CHANGELOG.md
- aimdb-data-contracts/CHANGELOG.md
- aimdb-derive/CHANGELOG.md
- aimdb-tokio-adapter/CHANGELOG.md
- aimdb-embassy-adapter/CHANGELOG.md
- aimdb-mqtt-connector/CHANGELOG.md
- aimdb-knx-connector/CHANGELOG.md
- aimdb-websocket-connector/CHANGELOG.md
- aimdb-uds-connector/CHANGELOG.md
- aimdb-serial-connector/CHANGELOG.md
- aimdb-tcp-connector/CHANGELOG.md
- aimdb-ws-protocol/CHANGELOG.md
- aimdb-wasm-adapter/CHANGELOG.md
- aimdb-sync/CHANGELOG.md
- aimdb-client/CHANGELOG.md
- aimdb-persistence/CHANGELOG.md
- aimdb-persistence-sqlite/CHANGELOG.md
- tools/aimdb-cli/CHANGELOG.md
- tools/aimdb-mcp/CHANGELOG.md
- Issue #200: the implementation of the synchronous interface (
aimdb-sync) has been simplified by removing the channels used internally to communicate with thetokiothread, with a number of API changes. (aimdb-sync)
- Submodule: bump
_external/knx-picoto upstream0.3(commit 158325bd4)
- Issue #178: one
Linkablerecord type can now select JSON, bounded Postcard, or a custom codec independently on each inbound/outbound connector route throughlinked_*_with(url, codec)or builder-levelwith_link_codec(codec). Selection is fused at registration time; bounded Postcard routes retain the reusable scratch/owned-overflow behavior from issue #177 without a per-message registry, lock, or lookup. See Design 045. Re-selecting a codec replaces both the owned and scratch serializers, so changing a route from bounded postcard to owned JSON cannot retain stale postcard output.
- Issue #196: state
record.getand AimX subscription events now serialize typed records directly to JSON bytes and hand them to the existing opaquePayload/RawValueenvelope path, avoiding an intermediateserde_json::Value. All public tree APIs and manual codec/reader implementations remain compatible through defaulted byte methods; AimX v2, write parsing/safety, ring drain fallback, WebSocket/client/CLI/MCP/WASM behavior andno_std + allocsupport are unchanged. The focused in-memory production-boundary gate records 16 → 6 allocation calls per reply/event and a 1.55x–1.61x Criterion slope-estimate speedup on the measured host. (aimdb-core)
Implementation of the accepted items of design 038 (§3.1, §3.3–§3.8, §3.11, D10, plus the CI drift guards). Net −2,500 lines across the library crates with no capability loss. Breaking changes and migrations:
- Submodule: bump
_external/knx-picoto upstream0.3(commit 158325bd4) aimdb-executoris retired (§3.1).RuntimeOps,LogLevel,BoxFuture, andExecutorError/ExecutorResultnow live inaimdb_core::executor(re-exported at the crate root). The superseded generic trait family (RuntimeAdapter,TimeOps,Logger,Runtime,RuntimeInfo),aimdb_core::time(TimestampProvider/SleepCapable), and the pre-runner spawn surface (TokioAdapter::spawn_task,TokioBuffer::spawn_dispatcher,EmbassyBuffer::dispatcher_task) are deleted. Migration:use aimdb_executor::X→use aimdb_core::X; adapters implement one trait (RuntimeOps) instead of four.DbErrordiet (§3.3). Deleted with zero production consumers:with_context(),into_anyhow(), the tenis_*()predicates, the numericerror_code()/error_category()registry,RESOURCE_TYPE_*, and the never-constructedAmbiguousType,DuplicateRecordKey,ResourceUnavailable, andHardwareErrorvariants (with the deadEmbassyErrorSupport/TokioErrorSupporttraits). Blocking-facade errors move to the newaimdb_sync::SyncError(AttachFailed,DetachFailed,SetTimeout,GetTimeout,RuntimeShutdown,Db(DbError)); all aimdb-sync APIs returnSyncResult<T>.- Dead API pruned (§3.8).
RecordT+register_record(_with_key)(self-registering records;configure()is the API),ConnectorLink::with_config,RecordValue::cloned, and theoutbound_connector_counttrait method (useoutbound_connectors().len()). - Internal consolidation (§3.4–§3.6). The registry collapses to one
Vec<RecordEntry>+ key map (records_of_typenow returnsVec<RecordId>);RecordIntrospectandRecordMetricsResetfold intoAnyRecord(JsonRecordAccessstays);AnyRecord::validate()is gone; writer exclusivity (.source()/.transform()/.link_from()) is validated once, inbuild(), reported as"conflicting writers: …"with the record key attached. LinkAddressreplacesConnectorUrlfor record links (§3.13/D5)..link_to()/.link_from()addresses parse as plainscheme://resource— no host/port/credential parsing;ConnectorUrlremains the connector-constructor endpoint URL. Phantomdefault_portentries (kafka/http/https) are gone.- Tokio log backend (§3.13).
TokioAdapterforwardsctx.log()to thelogfacade — the binary picks the backend (env_logger, tracing-log, …). With no backend configured it falls back to plain[info]-style stdout so demos work without setup; the emojiprintln!backend is gone. - Serializer API (§3.7).
with_serializer_raw/with_deserializer_raware gone. Migration:.with_serializer_raw(|v| …)→.with_serializer(|_ctx, v| …)(deserializer likewise). - Feature collapse (§3.11).
observability= the oldmetrics+profiling;remote= the oldjson-serialize+remote-access;connector-sessionstays independent. Migration: rename the features; there are no aliases. - CI drift guards (§2.6/§3.10).
make readme-checkcompiles the README quickstart verbatim (examples/readme-quickstart) and diffs it against the README;make codegen-driftcompiles aimdb-codegen's generated output (common crate, hub crate, flat schema) against the workspace. Both run in CI. The README quickstart itself and three already-drifted codegen templates (0-input source tasks, inbound deserializer arity, the flat schema's missing registrar-ext import) are fixed. - D10 docs cleanup. ~170 provenance-narration comment sites ("design 036 W1", "issue #133", "pre-W8") rewritten to state the invariant directly; history lives in git blame and docs/design/.
Schema migration is the last capability trait freed from std, so a versioned
contract can migrate old payloads on a bare-metal target (e.g. Embassy on an MCU),
not just under std. Verified on thumbv7em-none-eabihf in CI, with a
multi-step migration round-trip suite and a trybuild compile-fail harness.
migratableno longer requiresstd(breaking). The feature is now["alloc", "serde_json", "dep:aimdb-derive"](was["std", "serde_json"]), and the crate'sserde_jsonfollows the workspace pin (default-features = false,features = ["alloc"]).migration_chain!and theLinkable-with-migration patterns now compile onno_std + alloc. Migration: a crate that enabled onlymigratableand relied on it transitively activatingstdmust now enablestdexplicitly; in-repo consumers default tostdand are unaffected. (aimdb-data-contracts)migration_chain!moves to anaimdb-deriveproc-macro. The 3-armmacro_rules!is replaced by a variable-arity proc-macro (re-exported unchanged asaimdb_data_contracts::migration_chain!) withO(N)-in-code-size dispatch and a tree-free version probe inmigrate_from_bytes(peak allocation O(concrete struct), not O(payload tree)). A below-MIN_VERSIONsource version now reportsVersionTooOldinstead of the contradictoryVersionTooNew. (aimdb-derive)
-
Zero-allocation generated Postcard encoding (Issue #177).
Linkablegains a source-compatibleencode_into(&mut [u8])fast path and generated Postcard records implement it withpostcard::to_slice. Core reuses onebounded 256-byte scratch allocation per generated Postcard outbound route (raw builders choose their capacity) and falls back to the existing owned serializer when a value does not fit. The codec seam is gated by a 10,000-iteration allocation-counting bench (0 allocations, 0 bytes) plus host,no_stdCortex-M, and codegen-drift tests. The claim intentionally excludes connector-internal payload copies and the existing boxed connector reader future. (aimdb-core, aimdb-data-contracts, aimdb-codegen) -
Embassy MQTT client TLS —
mqtts://for embedded, WP7 (design 044). The Embassy MQTT connector can now dial a broker over TLS 1.3 (embedded-tls, pure-Rustrustpkicertificate verification withrsa/p384so public CA chains verify out of the box), with SNI/hostname verification, DNS resolution, and a connector-internal SNTP time source that gates the first handshake so certificate validity is always checked against real time. Behind the newembassy-tlsfeature;MqttConnectorBuilder::newpicks the transport from the URL scheme (mqtt://plain,mqtts://TLS) and gainswith_tls(TlsOptions)(entropy, record buffers, SNTP server) andwith_credentials(username, password)(MQTT CONNECT auth, both transports). The plainmqtt://path is untouched. Theembassy-mqtt-connector-demoexample gains atlsfeature demonstrating the full flow against the repo'sdev/mosquittobench broker. (aimdb-mqtt-connector) -
Design 034 Phase 3 — sans-io KNX/IP tunneling engine shared by both transports (Issue #135, review doc §3.7). The entire tunneling lifecycle — CONNECT_REQUEST/RESPONSE handshake, TUNNELING_REQUEST/ACK sequence + pending-ACK bookkeeping, keepalive (CONNECTIONSTATE_REQUEST) scheduling, ACK-timeout sweeps, and reconnect-with-backoff — now lives once, in the new runtime-neutral
aimdb_knx_connector::tunnelmodule (no_std + alloc, no tokio/embassy imports), driven as a poll-based state machine (events in,Actions out,next_deadline()for timer arming).tokio_client.rs(988 → ~530 lines incl. a new fake-gateway integration test) andembassy_client.rs(1,055 → ~450 lines) are reduced to socket shims; the previously untestable handshake/ACK/keepalive/reconnect paths now have 15 host-run unit tests plus a scripted localhost-UDP roundtrip test. Behavioral unifications: Embassy gains the 5 s CONNECT_RESPONSE timeout (previously waited forever), both shims reconnect on fatal socket errors, and the dead tokio-only per-publish ACK oneshot is dropped (it was alwaysNone); the CONNECT_REQUEST HPAI stays per-transport (LocalEndpoint: tokio = real bound address, Embassy = NAT mode). (aimdb-knx-connector) -
Design 034 Phase 2 — dyn-safe
RuntimeOpscapability trait (Issue #130, review doc). New object-safe trait inaimdb-executor(name/now_nanos/unix_time/ boxedsleep/log(LogLevel, …)) so a runtime adapter can travel asArc<dyn RuntimeOps>instead of a generic parameter — the groundwork for removingRfrom the record object graph (#131). Implemented byTokioAdapter,EmbassyAdapter, andWasmAdapter, each covered by a shared behavioral contract test.BoxFuture's canonical definition moves toaimdb-executor(re-exported unchanged fromaimdb-core). (aimdb-executor, aimdb-tokio-adapter, aimdb-embassy-adapter, aimdb-wasm-adapter) -
M17 — centralized Embassy connector spine: one audited home for the single-core
unsafe(Design 033). Newaimdb-embassy-adapter::connectorsmodule (featuresconnectors/connector-io) collects the force-Sendplumbing every Embassy connector used to hand-roll: session transports getEmbassySessionClient/EmbassySessionServer,OneShotDialer/OneShotListener/OneShotCell, and the framedEmbassyConnection+Framer; data-plane transports get theEmbassySink/EmbassySourcebridges (overEmbassySinkRaw/EmbassySourceRaw) that ride core's existingpump_sink/pump_source, plusinto_box_futurefor protocol tasks. The serial Embassy half is now thin sugar (just a COBSFramer) with zerounsafe(down from a 407-line hand-roll with 7unsafe impls); the MQTT and KNX Embassy halves dropped their hand-rolled publisher/router loops andSendFutureWrapperuse to ride core's pumps (KNX inbound telegrams now flow throughpump_source). All connector-crateunsafe/SendFutureWrapperis gone — confined to the adapter. The std/Tokio side,aimdb-client, the WebSocket server, examples, and tests are unchanged. (Chosen over Design 033's original "dropSendfrom the contract", which would have pushed!Sendonto the std side; see the doc's Implementation Decision.) (aimdb-embassy-adapter, aimdb-serial-connector, aimdb-mqtt-connector, aimdb-knx-connector) -
Remote access via connectors — Phases 0–6: converge four hand-rolled networking stacks onto two shared engines (Issue #39, design doc). AimX remote access (and any future transport) now rides the connector layer instead of a bespoke I/O abstraction. New, runtime-neutral
aimdb-core::sessionmodule (featureconnector-session,no_std + alloc): the three-layer substrate (Connection/Listener/Dialer+EnvelopeCodec+Dispatch/Session), the reactive server engine (serve/run_session) and proactive client engine (run_client/pump_client), thepump_sink/pump_sourcedata-plane toolkit, and the transport-agnosticSessionClientConnector/SessionServerConnector. The AimX-v2 NDJSON protocol (session::aimx:AimxCodec+AimxDispatch) and the WebSocket connector are ports onto this substrate, so the AimX server/client and WS server/client stacks collapse onto the two engines. Newaimdb-uds-connectorcrate carries the UDS transport (UdsClient/UdsServer). (aimdb-core, aimdb-uds-connector, aimdb-websocket-connector, aimdb-client, aimdb-mqtt-connector, aimdb-knx-connector, aimdb-embassy-adapter) -
M16 — JSON codec extracted behind the
json-serializefeature;RecordValue::as_json()now works onno_std + alloc, not juststd(Design 032). Newaimdb-core::codecmodule:RemoteSerialize(blanket-impl'd for everyserdeSerialize + DeserializeOwnedtype), the object-safeJsonCodec<T>, and the zero-sizedSerdeJsonCodec.serde_jsonruns onalloc, so embedded targets can opt in;stdenables the feature transitively, so std builds are unaffected. (aimdb-core) -
Embassy buffer + join-queue tests now run in CI (Issue #85). The join-queue tests previously sat behind
embassy-runtime, which pullsembassy-executor's cortex-m assembly and can't compile undercargo teston x86_64 — so ordering / backpressure / clone-routing regressions were never caught. Thejoin_queuemodule is now gated onembassy-sync, andmake testruns the embassy adapter's unit tests + doctests on the host (no executor). Also addsEmbassyBuffer::peek()and fixes a staleEmbassyBufferdoc example. (aimdb-embassy-adapter) -
no_stdAimX server — a board can serve a host, not just dial one (Issue #120, follow-up to #39). Cross-cutting de-std ofaimdb-core's central record API behind a newremote-accessfeature (= ["json-serialize", "thiserror"], transitively enabled bystd): the type-erasedAnyRecordJSON + metadata methods, theAimDbJSON read/write/subscribe API, theremotemodule (config / protocol / security / error), and the AimX server dispatch (AimxDispatch/AimxSession) now all compile onno_std + alloc— swappingstd::collections→hashbrown,std::sync::Arc→alloc::sync::Arc, andthiserrortodefault-features = false. Adds a runtime-neutral wall clock,TimeOps::unix_time(), implemented from the OS clock on Tokio and from anEmbassyAdapter::set_unix_time(...)anchor on Embassy. Verified by a newthumbv7em-none-eabihfdispatch cross-check in the Makefile. (aimdb-core, aimdb-executor, aimdb-tokio-adapter, aimdb-embassy-adapter, aimdb-uds-connector, tools/aimdb-mcp) -
aimdb-serial-connector— COBS-framed serial/UART transport, the headline embedded scenario (Issue #122, follow-up to #39 / #120). New crate: a sensor MCU dials a gateway over UART (and, on theno_stdAimxDispatchfrom #120, an MCU serves a host over UART). Contributes only theDialer/Listener/Connectiontriple +SerialClient/SerialServersugar over theserial://scheme; reuses the AimX codec/dispatch and the session engines from core. Same compact AimX JSON, framed with COBS + a0x00sentinel (self-synchronizing on a lossy serial line). Dual halves: a stdtokio-runtime(tokio-serial) riding the genericSession*Connector, and ano_std + allocembassy-runtimegeneric overembedded-io-asyncUART halves that hand-rollsConnectorBuilderand force-Sends the single-core futures viaSendFutureWrapper. Cross-compiles tothumbv7em-none-eabihf(new Makefiletest-embeddedchecks). Ships a real end-to-end demo: a hostserial_demoexample and anembassy-serial-connector-demoSTM32H563ZI firmware (serves a record over USART3 ↔ the ST-LINK Virtual COM Port, queried from the host). The workspaceembedded-io-asyncdep is bumped 0.6 → 0.7 to match the Embassy STM32 HAL. (aimdb-serial-connector) -
Transport-agnostic host client — pick the transport at runtime with
--connect <scheme://url>(Issue #123, follow-up to #39 / #122).aimdb-clientgains anendpointresolver (unix:///uds:/// bare path /serial://DEVICE?baud=N) over a newimpl Dialer for Box<dyn Dialer>inaimdb-core;AimxConnection::connecttakes an endpoint string, and transports are opt-in features (transport-udsdefault,transport-serialoff-by-default). TheaimdbCLI replaces the per-command--socketflags with a global--connect(+AIMDB_CONNECT), andaimdb-mcptools take anendpointparam with--connect/AIMDB_CONNECT(wassocket_path/--socket/AIMDB_SOCKET). See breaking below. (aimdb-client, tools/aimdb-cli, tools/aimdb-mcp, aimdb-core)
-
Design 036 W1 — data-plane de-
Any: per-message type erasure removed from the connector SPI (design doc §W1). #131 removed the control-plane erasure; this removes the per-message kind. The typed pipeline is now fused inside closures at registration time, so noBox<dyn Any>is constructed on any per-message path (connectors, session pumps, AimX client mirroring). Inbound: a syncIngestFn(deserialize + produce in one typed closure — the per-message boxed future disappears too;Router::routebecomes a sync fn with a mandatoryRuntimeContext) replacesDeserializerKind+ProducerTrait::produce_any. Outbound: a fusedSerializedSource/SerializedReaderyieldingSerializedValue { dest, payload }replacesConsumerTrait::subscribe_any→AnyReader::recv_any→SerializerKind(&dyn Any), and absorbs the third erasure crossing (TopicProviderAny::topic_any— the typedTopicProvider<T>is now stored directly).SerializeError::TypeMismatchand the deadConnectorClient/OutboundConnectorLinkare deleted. The user-facing registrar API is source-compatible (serializer/deserializer/topic-provider registration signatures unchanged — examples, codegen output, and aimdb-pro compile untouched);JoinTriggerdeliberately keeps its erasure (it is the multi-type join API, documented on the type). Behavior pinned by the unmodified KNX fake-gateway and session smoke tests. (aimdb-core) -
Design 034 Phase 3 — runtime type parameter
Rremoved from the object graph (Issue #131, review doc §3.2/§3.3). The runtime is now a value (Arc<dyn aimdb_executor::RuntimeOps>, the #130 groundwork) instead of a type parameter; the only generic left on the user-facing object graph is the record typeT. The full break inventory:- Types lose
R:AimDb<R>→AimDb,AimDbBuilder<R>→AimDbBuilder(theNoRuntimetypestate is gone — a missing runtime is abuild()error per the #133 contract),TypedRecord<T, R>→TypedRecord<T>,RecordRegistrar<'a, T, R>→RecordRegistrar<'a, T>,TransformBuilder<I, O, R>→TransformBuilder<I, O>,JoinBuilder<O, R>→JoinBuilder<O>,RecordT<R>→RecordT, andConnectorBuilder<R>→ConnectorBuilder(itsbuild()takes&AimDb). Turbofish updates:get_typed_record_by_key::<T, R>→::<T>,as_typed::<T, R>→::<T>. RuntimeContextis a concrete struct wrappingArc<dyn RuntimeOps>.ctx.time().now()returnsu64nanoseconds from an arbitrary monotonic epoch (wasR::Instant);sleeptakes a plaincore::time::Duration(plussleep_millis/sleep_secshelpers);millis()/secs()/micros()/duration_since()/duration_as_nanos()are deleted (durations are concrete, instants are integer math); the panickingextract_from_anyis deleted.source/tap/transform/transform_joinare inherent methods onRecordRegistrar— the per-adapter extension-trait wrappers andaimdb-core'sext_macros.rsare gone, as are the*_rawvariants (the inherent methods are the foundational API; closures keep the(ctx, producer)arg order, so user code mostly just drops an import). The adapter ext traits (TokioRecordRegistrarExt,EmbassyRecordRegistrarExt(+Custom),WasmRecordRegistrarExt) shrink to the one genuinely adapter-specific step:.buffer(cfg)construction.JoinFanInRuntimedeleted (withJoinQueue/JoinSender/JoinReceiverand all three per-adapterjoin_queue.rsfiles): multi-input join fan-in now uses one boundedasync-channelqueue in core — the same primitive the session engine already uses on tokio, Embassy, and WASM. Capacity stays 64 onstd/wasm32 and rises to 16 on embeddedno_std(up from Embassy's 8); the queue now closes on every runtime when all forwarders exit (the Embassy queue previously never closed).- Runtime accessors:
runtime_arc()→runtime_ops()(returnsArc<dyn RuntimeOps>), newruntime_ctx(); the borrowedAimDb::runtime()and the type-erasedruntime_any()are deleted (zero callers in aimdb/aimdb-pro —&*db.runtime_ops()covers the borrowed flavor; follow-up doc §2.5).AimDbBuilder::on_startclosures receiveRuntimeContext(wasArc<R>). Context-aware (de)serializers receive the concreteRuntimeContext(wasArc<dyn Any + Send + Sync>);Router::route's ctx argument follows. TheRuntimeForProfilingmarker-trait workaround is deleted (profiling clocks rideRuntimeOps::now_nanos); the session client engine's clock isArc<dyn RuntimeOps>(wasArc<R: TimeOps>). - Embassy network capability moves to construction: the
EmbassyNetworkruntime trait is deleted (adyn RuntimeOpscan't surface adapter-specific capabilities) along withEmbassyAdapter::new_with_network—EmbassyAdapteris a stateless unit type with zerounsafe. The Embassy MQTT/KNX connector builders take theembassy_net::Stackat construction (MqttConnectorBuilder::new(url, stack)/KnxConnectorBuilder::new(url, stack)), wrapped in the new force-Send + Syncaimdb_embassy_adapter::connectors::NetStackso the single-coreunsafestays in the one audited module. - Downstream follows mechanically:
aimdb-sync(AimDbhandles),aimdb-persistence(.persist()/.with_persistence()ext traits de-genericized),aimdb-data-contracts::log_tap,aimdb-codegentemplates (generatedconfigure_schema(builder: &mut AimDbBuilder)), and all examples/tools. - Acceptance:
grep -rn "extract_from_any|runtime_any|RuntimeForProfiling|JoinFanInRuntime"over the workspace sources returns nothing; the remainingdyn Anyin core is data-plane only (SerializerFn/produce_any/JoinTrigger/TopicProviderAny— the #131 §6 stretch, tracked as a follow-up). (aimdb-core, aimdb-executor, aimdb-tokio-adapter, aimdb-embassy-adapter, aimdb-wasm-adapter, aimdb-mqtt-connector, aimdb-knx-connector, aimdb-persistence, aimdb-sync)
- Types lose
-
Design 034 Phase 2 — MQTT knobs move out of core;
ConnectorConfigpruned (Issue #134, review doc §3.6). Core's generic link builders dropwith_qos/with_retain(with_timeout_msstays, de-MQTT'd); the knobs now live inaimdb-mqtt-connectoras theMqttLinkExt(qos, outbound + inbound) andMqttOutboundLinkExt(retain, publish-side only) extension traits, pushing the same("qos", …)/("retain", …)option keys the MQTT clients have always read — wire behavior unchanged; importing the trait makes the MQTT intent explicit at the call site (generic escape hatch:with_config(key, value)).ConnectorConfigloses its never-read typedqos/retainfields and the speculative Kafka/HTTP/shmem interpretation docs — it keepstimeout_ms+protocol_options, and core now documents no protocol that lacks an in-tree connector. (aimdb-core, aimdb-mqtt-connector) -
Design 034 Phase 2 — panic-free builder validation:
build()reports every configuration mistake at once (Issue #133, review doc §3.4). Builder methods never panic on user mistakes anymore. Conflicting.source()/.transform()/.link_from()registrations, missing serializers/deserializers, invalid connector URLs, unregistered schemes, and key-reused-with-different-type are recorded (the conflicting registration is skipped) andbuild()returns oneDbError::InvalidConfiguration { errors: Vec<ConfigError> }carrying all findings — each with the record key and, where applicable, the connector URL. The worst panic — "requires a buffer" firing at spawn time inside a connector factory closure — is now a build()-time check, which also makes.buffer()after.link_to()/.link_from()legal (order-independent). Duplicate keys and dependency-graph cycles fold into the same collected report (previously distinctDuplicateRecordKey/CyclicDependencyreturns frombuild()). Remainingpanic!/expects in the builder path are internal invariants and say "this is a bug in aimdb-core". (aimdb-core) -
Design 034 Phase 2 — registrar lifetime fix + de-erased builder internals (Issue #130, review doc).
RecordRegistrar's fluent methods now take fresh borrows (&mut self -> &mut Self) instead of borrowing the registrar for its entire lifetime — aconfigureclosure can finally use separate statements (reg.source_raw(…); reg.tap_raw(…);) and reuse the registrar after a chain.configure's closure bound drops its HRTB;OutboundConnectorBuilder/InboundConnectorBuildergain a second lifetime parameter (<'r, 'a, T, R>);RecordT::registerand the adapter/persistence extension traits follow. Internally,AimDbBuilderstores its spawn/start functions typed (SpawnFnType<R>/StartFnType<R>) instead ofBox<dyn Any>— the panicking downcasts inbuild()are gone, andAimDb<R>'s struct-level bound moved to its impls. Closure-based user code compiles unchanged. (aimdb-core, aimdb-embassy-adapter, aimdb-persistence) -
Design 034 Phase 1 — mechanical debt cleanup (Issues #129, #132, review doc).
DbErroris unified onalloc::string::String: every variant has one shape on all targets,thiserrorderivesDisplay/Errorunconditionally (now a mandatory no_std dependency ofaimdb-core), and no_std builds produce the same error messages as std builds instead ofError 0xNNNNcodes — on no_std the_field: ()placeholders become the realStringfields. The deadDatabase<A>wrapper, theTokioDatabase/EmbassyDatabasealiases, and the deprecatedRecordRegistrar::link()are removed.ConsumerTrait::subscribe_anyis infallible (returnsBox<dyn AnyReader>),OutboundRouteis a struct with named fields, andaimdb-core'sstdfeature no longer pulls atokiodependency (tests cover it via dev-deps). Internally: all dual std/alloc import pairs and the duplicatedRuntimeContextimpl blocks are collapsed, and crate-privatelog_*!macros replace the 62 per-call-site#[cfg(feature = "tracing")]gates. Theaimdb-wasm-adapterring-lag error now carries abuffer_namelike the other adapters. (aimdb-core, aimdb-tokio-adapter, aimdb-embassy-adapter, aimdb-mqtt-connector, aimdb-knx-connector) -
CLI/MCP endpoint surface reworked to
--connect <url>(Issue #123).aimdb's per-command--socket <path>is replaced by a global--connect <endpoint>(AIMDB_CONNECTenv; bare paths still work).aimdb-mcprenames the tools'socket_pathparam toendpoint, the startup--socketto--connect, andAIMDB_SOCKETtoAIMDB_CONNECT; the pool is keyed by endpoint URL.aimdb-client'sAimxConnection::connecttakes a&strendpoint (was a path) andClientError::ConnectionFailed.socketis renamedendpoint. Serial endpoints (serial://…) require building the CLI/MCP with--features transport-serial. (aimdb-client, tools/aimdb-cli, tools/aimdb-mcp) -
AimDbBuilder::with_remote_access(config)removed — remote-access servers are now registered like any other connector (Issue #39). Replace.with_remote_access(config)with.with_connector(aimdb_uds_connector::UdsServer::from_config(config)). The AimX wire was reshaped to v2 (NDJSON tagged frames mapping onto the engine's role-neutral message set) and is not backward-compatible with the legacy AimX v1 framing; the bundledaimdb-client/ CLI / MCP speak v2. The UDS transport types moved out ofaimdb-coreintoaimdb-uds-connector. (aimdb-core, aimdb-uds-connector, aimdb-client) -
M15 —
latest_snapshotremoved; point-in-time reads go through the new buffer-nativeDynBuffer::peek()(Design 031).TypedRecord::latest()and AimXrecord.getread the buffer directly instead of a per-record snapshot mutex (one lock + clone off theproduce()hot path). Consequences: a.with_remote_access()record with no buffer now failsbuild()(was a silent runtime no-op);record.get/latest()on anSpmcRingrecord returnsnot_found/None(rings have no canonical latest — userecord.drain/record.subscribe);SingleLatestandMailboxare unaffected.TypedRecord::produceis removed — all writes go throughWriteHandle::push. Adapters implementpeek()per buffer type. (aimdb-core, aimdb-tokio-adapter, aimdb-embassy-adapter, aimdb-wasm-adapter) -
M16 —
with_remote_access()now requires thejson-serializefeature (transitively enabled bystd);with_read_only_serialization()removed (Design 032). The stored serializer/deserializer closures are replaced by a type-erasedArc<dyn JsonCodec<T>>. ASerialize-only record can no longer be exposed read-only over remote access. (aimdb-core) -
RecordMetadatadropscreated_at/last_update;AimxConfig.socket_pathis nowString(Issue #120). Core stopped keeping per-record timestamp state for theno_stdAimX server (theMutex-+-SystemTimeRecordMetadataTrackeris deleted), so AimXrecord.listand the MCPlist_records/ schema outputs no longer include the two timestamp fields.RecordMetadata::newdrops itscreated_atparameter.AimxConfig::socket_path/UdsServer::newtakeimpl Into<String>instead ofimpl Into<PathBuf>(&Path/PathBufcallers must convert). (aimdb-core, aimdb-uds-connector, tools/aimdb-mcp) -
M13 —
Spawntrait removed across the workspace;AimDbBuilder::build()now returns(AimDb, AimDbRunner)(Issue #88, Design 028). Every future the database drives — producer services, taps, transforms, join forwarders, connector loops, the remote-access supervisor,on_starttasks — is collected at build time and driven by a singleFuturesUnorderedinsiderunner.run().await. Adapter implementations (TokioAdapter,EmbassyAdapter,WasmAdapter) drop theirimpl Spawn. Theembassy-task-pool-8/16/32features are deleted andEmbassyAdapter::new_with_networkno longer takes aSpawner. Connector authors must updateConnectorBuilder::build()to returnVec<BoxFuture>instead ofArc<dyn Connector>. See each crate's CHANGELOG for the per-crate impact.
1.1.0 - 2026-05-22
- Automatic stage profiling (Issue #58, RFC 014): New
profilingfeature onaimdb-coreautomatically measures wall-clock time per.source()/.tap()/.link()stage and exposescall_count/total/avg/min/maxcounters per stage. Name stages withRecordRegistrar::with_name("..."). Works onno_std + allocvia the runtime clock (aimdb_executor::TimeOps) andportable-atomicfor 64-bit atomics onthumbv7em. New MCP toolsget_stage_profiling(with bottleneck detection + recommendation) andreset_stage_profiling. Feature is off by default and zero-cost when disabled. (aimdb-core, aimdb-executor, aimdb-tokio-adapter, aimdb-embassy-adapter, aimdb-wasm-adapter, aimdb-client, tools/aimdb-mcp) remote-access-demoexercises stage profiling: TheTemperatureandSystemStatusrecords now run from in-AimDB.source()+.tap()tasks (with.with_name(...)stage labels) and the demo enables theprofilingfeature.SystemStatus.slow_status_processordeliberately sleeps 100 ms per value soget_stage_profilingflags it as the bottleneck. README documents how to query and reset profiling via MCP /socat.hello-mailboxexample (Issue #94): New minimal example demonstrating the Mailbox buffer (latest-wins) semantics using the synchronous API. First community contribution from @ggmaldo — see examples/hello-mailbox/.- Writer-exclusivity validation (Issue #89): Combining
.source(),.transform(), and.link_from()on the same record now panics at configuration time with a clear message instead of silently producing a last-writer-wins race on the buffer. Multiple.link_from()inbound connectors (fan-in) remain allowed. (aimdb-core) no_stdTransform API (Design 027):.transform()and.transform_join()are now available onno_std + alloctargets — no longer Tokio-only. Multi-input join fan-in moved out ofaimdb-coreinto the newJoinFanInRuntimetraits inaimdb-executor, with implementations in the Tokio (mpsc::channel, capacity 64), Embassy (embassy_sync::Channel, capacity 8), and WASM (futures_channel::mpsc, capacity 64) adapters. (aimdb-core, aimdb-executor, aimdb-tokio-adapter, aimdb-embassy-adapter, aimdb-wasm-adapter)- Task-model join handler: New
JoinBuilder::on_triggers(FnOnce(JoinEventRx, Producer) -> impl Future)API replaces the previous callback model. Eliminates per-event heap allocation and lets handler state borrow across.awaitpoints. Breaking change vs. the oldwith_state().on_trigger(...)form — see aimdb-core. - Weather-mesh
DewPointdemo: All three weather stations (alpha, beta, gamma) now derive aDewPointrecord fromTemperatureandHumidityviatransform_join, demonstrating the API end-to-end on Tokio and Embassy. - Design document: 027 (
no_stdSupport for Transform API) - MCP public mode: New
--publicflag restricts the MCP server to read-only tools for safe internet-facing deployments with SSRF protection (tools/aimdb-mcp) - MCP
--socketflag: Default socket path can be set at startup, simplifying single-instance workflows (tools/aimdb-mcp) - Context-Aware Deserializers (Design 026): Inbound connector deserializers can now receive a
RuntimeContext<R>for platform-independent timestamps and logging- New
.with_deserializer(|ctx, bytes| ...)API onInboundConnectorBuilderprovidesRuntimeContext<R>to deserialization closures - New
.with_deserializer_raw(|bytes| ...)for plain bytes-only deserialization when context is unnecessary DeserializerKindenum enforces mutual exclusivity between raw and context-aware deserializersRouter::route()propagates optional runtime context to context-aware routes
- New
- Context-Aware Serializers: Outbound connector serializers can now receive a
RuntimeContext<R>, symmetric with deserializers- New
.with_serializer(|ctx, value| ...)API onOutboundConnectorBuilderprovidesRuntimeContext<R>to serialization closures - New
.with_serializer_raw(|value| ...)for plain value-only serialization when context is unnecessary SerializerKindenum (Raw/Context) enforces mutual exclusivity- All outbound connector publishers updated to propagate runtime context via
db.runtime_any()
- New
- Design document: 026 (Context-Aware Deserializers)
- aimdb-executor:
TimeOpstrait gained a requiredduration_as_nanos(duration) -> u64method (runtime-agnostic numeric representation of elapsed time, used by stage profiling). Implemented in tokio, embassy, and wasm adapters. (aimdb-executor) - aimdb-embassy-adapter:
SpmcRingsubscriber-slot exhaustion now emits adefmt::error!with guidance to increase theCONSUMERSconst generic. Counting rule: one slot per.tap(),.link_to(), andtransform_joininput. - aimdb-codegen: Generated join handler stubs updated to the new
on_triggerstask model (async fn task_handler(JoinEventRx, Producer<...>)). - aimdb-core: Breaking API changes to
InboundConnectorLink,Router, andRouterBuilderto supportDeserializerKind(see aimdb-core/CHANGELOG.md) - aimdb-core: Breaking API change —
ConnectorLink.serializernow storesSerializerKindinstead ofSerializerFn - aimdb-core:
.with_serializer()renamed to.with_serializer_raw()for the old single-argument pattern - aimdb-mqtt-connector: Updated router dispatch for new
route()signature; outbound publishers dispatch viaSerializerKind - aimdb-knx-connector: Updated router dispatch for new
route()signature; outbound publishers dispatch viaSerializerKind - aimdb-websocket-connector: Updated router dispatch for new
route()signature; outbound publishers dispatch viaSerializerKind - All connector examples updated to use new
.with_deserializer(|_ctx, bytes| ...)and.with_serializer_raw(|value| ...)signatures rand0.8 → 0.10.1: Upgraded across workspace (aimdb-data-contracts,aimdb-embassy-adapter, examples). Migrated API:gen→random,SmallRngseed size 16 → 32, addedRngExtimports.- README: Reordered quickstart — remote MCP exploration (zero install) is now step 2, local Docker setup moved to step 3.
1.0.0 - 2026-03-16
- aimdb-core promoted to 1.0.0 — stable public API milestone
- aimdb-websocket-connector: New crate for real-time bidirectional WebSocket streaming
- Server mode (Axum-based) with
link_to("ws://topic")for pushing data to browser clients - Client mode (tokio-tungstenite) with
link_to("ws-client://host/topic")for AimDB-to-AimDB sync AuthHandlertrait for pluggable authentication- Client session management with automatic cleanup and late-join support
StreamableRegistryfor extensible type-erased dispatch — register types via.register::<T>()
- Server mode (Axum-based) with
- aimdb-ws-protocol: New crate with shared wire protocol types for the WebSocket ecosystem
ServerMessageandClientMessageenums with JSON-encoded"type"discriminant- MQTT-style wildcard topic matching (
#multi-level,*single-level)
- aimdb-wasm-adapter: New crate enabling AimDB to run in the browser via WebAssembly
- Full
aimdb-executortrait implementations (RuntimeAdapter,Spawn,TimeOps,Logger) Rc<RefCell<…>>buffers — zero-overhead for single-threaded WASMWasmDbfacade via#[wasm_bindgen]withconfigureRecord,get,set,subscribeWsBridge— WebSocket bridge connecting browser to remote AimDB serverSchemaRegistryfor type-erased record dispatch with extensible.register::<T>()API- React hooks:
useRecord<T>,useSetRecord<T>,useBridge
- Full
- aimdb-codegen: New crate for architecture-to-code generation
ArchitectureStatetype for reading.aimdb/state.tomldecision records- Mermaid diagram generation (
generate_mermaid) - Rust source generation (
generate_rust) — value structs, key enums,SchemaType/Linkableimpls,configure_schema()scaffolding - Common crate, hub crate, and binary crate scaffolding
- State validation module for architecture integrity checks
- aimdb-data-contracts: Refocused as a pure trait-definition crate (
SchemaType,Streamable,Linkable,Simulatable,Observable,Migratable)Streamabletrait as capability marker for types crossing serialization boundariesMigratabletrait withMigrationChainandMigrationStepfor schema evolution- Concrete contracts (Temperature, Humidity, GpsLocation) moved to application-level crates
- Removed closed
StreamableVisitordispatcher in favor of extensible registry pattern - Removed
tsfeature (ts-rsdependency)
- aimdb-core: Added
ws://andwss://URL scheme support inConnectorUrlfor WebSocket connectors - tools/aimdb-cli: New
aimdb generatesubcommand for Mermaid diagrams, Rust schema generation, and crate scaffolding viaaimdb-codegen - tools/aimdb-cli: New
aimdb watchsubcommand for live record monitoring - tools/aimdb-mcp: Architecture agent module with session state machine (
Idle → Gathering → Proposing → Resolve) - tools/aimdb-mcp: 16+ new MCP tools:
propose_add_record,propose_add_connector,propose_modify_buffer,propose_modify_fields,propose_modify_key_variants,remove_record,rename_record,reset_session,resolve_proposal,save_memory,validate_against_instance,get_architecture,get_buffer_metrics, and more - tools/aimdb-mcp: Architecture MCP resources — Mermaid diagram and validation results
- tools/aimdb-mcp:
CONVENTIONS.mdasset for architecture agent prompts - Design documents: 023 (Architecture Agent), 024 (Codegen Common Crate), 025 (WASM Adapter)
- aimdb-data-contracts: Restructured from schema+contract crate to pure trait-definition crate (version reset to 0.1.0)
- aimdb-websocket-connector: Replaced closed dispatcher with extensible
StreamableRegistry— users register types via builder.register::<T>() - aimdb-wasm-adapter:
SchemaRegistryupdated to use extensible.register::<T>()pattern - aimdb-knx-connector: Updated Embassy dependency versions (executor 0.10.0, time 0.5.1, sync 0.8.0, net 0.9.0)
- aimdb-mqtt-connector: Updated Embassy dependency versions (executor 0.10.0, time 0.5.1, sync 0.8.0, net 0.9.0)
- Embassy submodules updated to latest upstream commits
| Crate | Previous | New |
|---|---|---|
aimdb-core |
0.5.0 | 1.0.0 |
aimdb-data-contracts |
0.5.0 | 0.1.0 |
aimdb-cli |
0.5.0 | 0.6.0 |
aimdb-mcp |
0.5.0 | 0.6.0 |
aimdb-codegen |
— | 0.1.0 (new) |
aimdb-websocket-connector |
— | 0.1.0 (new) |
aimdb-ws-protocol |
— | 0.1.0 (new) |
aimdb-wasm-adapter |
— | 0.1.1 (new) |
aimdb-tokio-adapter |
0.5.0 | unchanged |
aimdb-embassy-adapter |
0.5.0 | unchanged |
aimdb-client |
0.5.0 | unchanged |
aimdb-sync |
0.5.0 | unchanged |
aimdb-mqtt-connector |
0.5.0 | 0.5.1 |
aimdb-knx-connector |
0.3.0 | 0.3.1 |
aimdb-persistence |
0.1.0 | unchanged |
aimdb-persistence-sqlite |
0.1.0 | unchanged |
aimdb-derive |
0.1.0 | unchanged |
aimdb-executor |
0.1.0 | unchanged |
0.5.0 - 2026-02-21
- aimdb-persistence: New crate providing a pluggable persistence layer for long-term record history
PersistenceBackendtrait withstore,query,cleanup, andinitializehooksAimDbBuilderPersistExttrait adding.with_persistence(backend, retention)toAimDbBuilder<R>RecordRegistrarPersistExttrait adding.persist(record_name)toRecordRegistrar<T, R>AimDbQueryExttrait withquery_latest,query_range, andquery_rawmethods- Automatic 24-hour retention cleanup task registration
- aimdb-persistence-sqlite: New crate providing a SQLite backend for
aimdb-persistenceSqliteBackendwith WAL journal mode, actor-model command dispatch, and window-function queries- Pattern-based queries with
*wildcard support and optional time/row-count limits - Dedicated OS writer thread;
Clone=O(1)handle copy
- aimdb-data-contracts: New crate for portable, versioned AimDB data schemas
- Optional features:
observable,simulatable,migratable,ts(TypeScript bindings viats-rs) - Schema versioning and migration support
- Optional features:
- Transform API (Design 020) in
aimdb-core: Reactive data transformations between recordstransform_raw()for single-input derivations andtransform_join_raw()for multi-input joinsTransformBuilderandJoinBuilderfluent APIs with optional stateful handlers- Transforms spawned as tasks during
AimDb::build(); mutual exclusion with.source()enforced
- Graph Introspection API (Design 021) in
aimdb-coreandaimdb-client:RecordOriginenum classifying record sources (Source,Link,Transform,TransformJoin,Passive)GraphNode,GraphEdge,DependencyGraphtypes for full graph representationgraph_nodes(),graph_edges(),graph_topo_order()methods onAimDbandAimDbClient
- Record Drain API (Design 019) in
aimdb-core,aimdb-tokio-adapter, andaimdb-client:try_recv()onBufferReaderfor non-blocking batch pullsrecord.drainAimX protocol method with cold-start semantics and optional limitDrainResponsetype inaimdb-client
- Dynamic Topic/Address Routing (Design 018) in
aimdb-core,aimdb-mqtt-connector,aimdb-knx-connector:TopicProvider<T>trait for per-message outbound topic/address determinationTopicResolverFnfor late-binding inbound topic resolution at connector startupwith_topic_provider()andwith_topic_resolver()builder methods on connector links
- Graph Tools in
tools/aimdb-mcp:graph_nodes,graph_edges,graph_topo_orderMCP tools for LLM-powered graph explorationdrain_recordMCP tool for batch history access with persistent cold-start reader
- Graph Commands in
tools/aimdb-cli:aimdb graph nodes,graph edges,graph order,graph dotsubcommands- Color-coded output and DOT format export for Graphviz visualization
- Extension Macro in
aimdb-core:impl_record_registrar_ext!for generating runtime adapter extension traits
- aimdb-core: Renamed
.with_serialization()to.with_remote_access()for clearer semantics (breaking) - aimdb-core:
RecordId::new()now requires aRecordOriginparameter for dependency graph support (breaking) - aimdb-core:
set_from_jsonnow also rejects writes on records with active transforms - aimdb-core:
collect_outbound_routes()returnsOutboundRoutetuples with optionalTopicProviderFn - aimdb-core:
collect_inbound_routes()callslink.resolve_topic()for dynamic topic resolution - tools/aimdb-mcp: Replaced real-time subscription system with simpler drain-based polling
- Removed
subscribe_record,unsubscribe_record,list_subscriptions,get_notification_directorytools - Simplified architecture reduces infrastructure complexity for LLM clients
- Removed
| Crate | Previous | New |
|---|---|---|
aimdb-core |
0.4.0 | 0.5.0 |
aimdb-tokio-adapter |
0.4.0 | 0.5.0 |
aimdb-embassy-adapter |
0.4.0 | 0.5.0 |
aimdb-client |
0.4.0 | 0.5.0 |
aimdb-sync |
0.4.0 | 0.5.0 |
aimdb-mqtt-connector |
0.4.0 | 0.5.0 |
aimdb-knx-connector |
0.2.0 | 0.3.0 |
aimdb-cli |
0.4.0 | 0.5.0 |
aimdb-mcp |
0.4.0 | 0.5.0 |
aimdb-persistence |
— | 0.1.0 (new) |
aimdb-persistence-sqlite |
— | 0.1.0 (new) |
aimdb-derive |
0.1.0 | unchanged |
aimdb-executor |
0.1.0 | unchanged |
0.4.0 - 2025-12-25
- aimdb-derive: New crate providing
#[derive(RecordKey)]macro for compile-time checked record keys#[key = "..."]attribute for string representation#[key_prefix = "..."]attribute for namespace prefixing#[link_address = "..."]attribute for connector metadata (MQTT topics, KNX addresses)- Auto-generates
Hashimplementation to satisfyBorrow<str>contract
- aimdb-core:
RecordKeyis now a trait instead of a struct, enabling user-defined enum keysRecordKeytrait withas_str()and optionallink_address()methodsStringKeytype replaces oldRecordKeystruct (static/interned string keys)derivefeature flag to enable#[derive(RecordKey)]macro- Blanket implementation for
&'static str
- examples: New
mqtt-connector-demo-commonandknx-connector-demo-commoncrates for shared cross-platform types and monitors - docs: Added design documents:
016-M6-record-key-trait.md- RecordKey trait design017-M6-record-key-hash-impl.md- Hash implementation for Borrow compliance
- aimdb-mqtt-connector: Fixed initialization deadlock when subscribing to >10 MQTT topics (Issue #63)
- Event loop now spawned before subscribing (spawn-before-subscribe pattern)
- Dynamic channel capacity scales with topic count (
topics + 10)
- aimdb-mqtt-connector: Upgraded
rumqttcdependency from 0.24 to 0.25 - deny.toml: Added
OpenSSLlicense allowance (transitive via rumqttc/rustls) - deny.toml: Ignored
RUSTSEC-2025-0134advisory (rustls-pemfile unmaintained but not vulnerable)
0.3.0 - 2025-12-15
- aimdb-core: New
RecordIdtype for stable O(1) record indexing (Issue #60) - aimdb-core: New
RecordKeytype for string-based record identification with zero-alloc static keys - aimdb-core: Key-based producer/consumer API:
produce_by_key(),subscribe_by_key(),producer_by_key(),consumer_by_key() - aimdb-core:
ProducerByKey<T, R>andConsumerByKey<T, R>types for key-bound access - aimdb-core:
records_of_type::<T>()introspection method to find all records of a given type - aimdb-core:
resolve_key()method for O(1) key-to-RecordId lookups - aimdb-core: New error variants:
RecordKeyNotFound,InvalidRecordId,TypeMismatch,AmbiguousType,DuplicateRecordKey - aimdb-core:
RecordMetadatanow includesrecord_idandrecord_keyfields - aimdb-tokio-adapter: Multi-instance tests validating same-type different-key scenarios
- aimdb-core: New
BufferMetricstrait andBufferMetricsSnapshotstruct for buffer introspection (feature-gated behindmetrics)produced_count: Total items pushed to the bufferconsumed_count: Total items consumed across all readersdropped_count: Total items dropped due to lag (with per-reader semantics documented)occupancy: Current buffer fill level as(current, capacity)tuple
- aimdb-core:
RecordMetadatanow includes optional buffer metrics fields whenmetricsfeature is enabled - aimdb-tokio-adapter: Full
BufferMetricsimplementation for all buffer types (SPMC Ring, SingleLatest, Mailbox) - aimdb-tokio-adapter: Comprehensive test suite for metrics (
metrics_testsmodule) - Makefile: Added test targets for
metricsfeature combinations - docs: Added design document
015-M6-record-id-architecture.mdfor future RecordId-based storage
- aimdb-core:
configure<T>()now requires a key parameter:configure::<T>("key", |reg| ...) - aimdb-core: Internal storage changed from
BTreeMap<TypeId>toVec+HashMapindexes for O(1) hot-path access - aimdb-core:
SecurityPolicy::ReadWritenow usesHashSet<String>for writable record keys - aimdb-core: Added
hashbrowndependency for no_std-compatible HashMap - deny.toml: Added
Zliblicense allowance (used byfoldhash, hashbrown's default hasher) - aimdb-core:
DynBuffertrait no longer has a blanket implementation. Each adapter now provides its own explicit implementation. This enables adapters to providemetrics_snapshot()when themetricsfeature is enabled.
Breaking: configure<T>() now requires a key parameter
The record registration API now requires an explicit key for each record:
// Before (v0.2.x)
builder.configure::<Temperature>(|reg| {
reg.buffer(BufferCfg::SingleLatest);
});
// After (v0.3.0)
builder.configure::<Temperature>("sensor.temperature", |reg| {
reg.buffer(BufferCfg::SingleLatest);
});Key naming conventions:
- Use dot-separated hierarchical names:
"sensors.indoor","config.app" - Keys must be unique across all records (duplicate keys panic at registration)
- For single-instance records, any descriptive string works (e.g.,
"sensor.temperature","app.config")
Breaking: Type-based lookup may return AmbiguousType error
If you register multiple records of the same type, type-based methods (produce(), subscribe(), producer(), consumer()) will return AmbiguousType error:
// With multiple Temperature records registered...
db.produce(temp).await // Returns Err(AmbiguousType { count: 2, ... })
// Use key-based methods instead:
db.produce_by_key("sensors.indoor", temp).await // Works correctlyNew key-based API (recommended for multi-instance scenarios):
// Key-bound producer/consumer
let indoor_producer = db.producer_by_key::<Temperature>("sensors.indoor");
let outdoor_producer = db.producer_by_key::<Temperature>("sensors.outdoor");
// Each produces to its own record
indoor_producer.produce(temp).await?;
outdoor_producer.produce(temp).await?;
// Introspection
let temp_ids = db.records_of_type::<Temperature>(); // Returns &[RecordId]
let id = db.resolve_key("sensors.indoor"); // Returns Option<RecordId>Breaking: DynBuffer no longer has blanket impl
If you have custom Buffer<T> implementations, you now need to also implement DynBuffer<T>:
// Before (v0.2.x) - automatic via blanket impl
impl<T: Clone + Send> Buffer<T> for MyBuffer<T> { ... }
// DynBuffer was automatically implemented
// After (v0.3.0) - explicit implementation required
impl<T: Clone + Send> Buffer<T> for MyBuffer<T> { ... }
impl<T: Clone + Send + 'static> DynBuffer<T> for MyBuffer<T> {
fn push(&self, value: T) {
<Self as Buffer<T>>::push(self, value)
}
fn subscribe_boxed(&self) -> Box<dyn BufferReader<T> + Send> {
Box::new(self.subscribe())
}
fn as_any(&self) -> &dyn core::any::Any {
self
}
// Optional: implement metrics_snapshot() if you support metrics
#[cfg(feature = "metrics")]
fn metrics_snapshot(&self) -> Option<BufferMetricsSnapshot> {
None // or Some(...) if you track metrics
}
}0.2.0 - 2025-11-20
This release introduces bidirectional connector support, enabling true two-way data synchronization between AimDB and external systems. The new architecture supports simultaneous publishing and subscribing with automatic message routing, working seamlessly across both Tokio (std) and Embassy (no_std) runtimes.
- 🔄 Bidirectional Connectors: New
.link_to()and.link_from()APIs for clear directional data flows - 🎯 Type-Erased Router: Automatic routing of incoming messages to correct typed producers
- 🏗️ ConnectorBuilder Pattern: Simplified connector registration with automatic initialization
- 📡 Enhanced MQTT Connector: Complete rewrite supporting simultaneous pub/sub with automatic reconnection
- 🌐 Embassy Network Integration: Connectors can now access Embassy's network stack for network operations
- 📚 Comprehensive Guide: New 1000+ line connector development guide with real-world examples
- aimdb-core:
.build()is now async; connector registration changed from.with_connector(scheme, instance)to.with_connector(builder) - aimdb-core:
.link()deprecated in favor of.link_to()(outbound) and.link_from()(inbound) - aimdb-core: Outbound connector architecture refactored to trait-based system:
- Removed:
TypedRecord::spawn_outbound_consumers()method (was called automatically) - Added:
ConsumerTrait,AnyReadertraits for type-erased outbound routing - Added:
AimDb::collect_outbound_routes()method to gather configured routes - Required: Connectors must implement
spawn_outbound_publishers()and call it inConnectorBuilder::build()
- Removed:
- aimdb-mqtt-connector: API changed from
MqttConnector::new()toMqttConnectorBuilder::new(); automatic task spawning removes need for manual background task management - aimdb-mqtt-connector: Added
spawn_outbound_publishers()method; must be called inbuild()for outbound publishing to work
See individual changelogs for detailed changes:
- aimdb-core: Core connector architecture, router system, bidirectional APIs
- aimdb-tokio-adapter: Connector builder integration
- aimdb-embassy-adapter: Network stack access, connector support
- aimdb-mqtt-connector: Complete bidirectional rewrite for both runtimes
- aimdb-sync: Compatibility with async build
1. Update connector registration:
// Old (v0.1.0)
let mqtt = MqttConnector::new("mqtt://broker:1883").await?;
builder.with_connector("mqtt", Arc::new(mqtt))
// New (v0.2.0)
builder.with_connector(MqttConnectorBuilder::new("mqtt://broker:1883"))2. Make build() async:
// Old (v0.1.0)
let db = builder.build()?;
// New (v0.2.0)
let db = builder.build().await?;3. Use directional link methods:
// Old (v0.1.0)
.link("mqtt://sensors/temp")
// New (v0.2.0)
.link_to("mqtt://sensors/temp") // For publishing (AimDB → External)
.link_from("mqtt://commands/temp") // For subscribing (External → AimDB)4. Remove manual MQTT task spawning (Embassy):
// Old (v0.1.0) - Manual task spawning required
let mqtt_result = MqttConnector::create(...).await?;
spawner.spawn(mqtt_task(mqtt_result.task))?;
builder.with_connector("mqtt", Arc::new(mqtt_result.connector))
// New (v0.2.0) - Automatic task spawning
builder.with_connector(MqttConnectorBuilder::new("mqtt://broker:1883"))
// Tasks spawn automatically during build()5. Update custom connectors to spawn outbound publishers:
If you've implemented a custom connector, you must add spawn_outbound_publishers() support:
// Old (v0.1.0) - Outbound consumers spawned automatically
impl ConnectorBuilder for MyConnectorBuilder {
fn build<R>(&self, db: &AimDb<R>) -> DbResult<Arc<dyn Connector>> {
// ... setup code ...
Ok(Arc::new(MyConnector { /* fields */ }))
}
// Outbound publishing happened automatically via TypedRecord::spawn_outbound_consumers()
}
// New (v0.2.0) - Must explicitly spawn outbound publishers
impl ConnectorBuilder for MyConnectorBuilder {
fn build<R>(&self, db: &AimDb<R>) -> DbResult<Arc<dyn Connector>> {
// ... setup code ...
let connector = MyConnector { /* fields */ };
// REQUIRED: Collect and spawn outbound publishers
let outbound_routes = db.collect_outbound_routes(self.protocol_name());
connector.spawn_outbound_publishers(db, outbound_routes)?;
Ok(Arc::new(connector))
}
}
// REQUIRED: Implement spawn_outbound_publishers method
impl MyConnector {
fn spawn_outbound_publishers<R: RuntimeAdapter + 'static>(
&self,
db: &AimDb<R>,
routes: Vec<(String, Box<dyn ConsumerTrait>, SerializerFn, Vec<(String, String)>)>,
) -> DbResult<()> {
for (topic, consumer, serializer, _config) in routes {
let client = self.client.clone();
let topic_clone = topic.clone();
db.runtime().spawn(async move {
// Subscribe to record updates using ConsumerTrait
match consumer.subscribe_any().await {
Ok(mut reader) => {
loop {
match reader.recv_any().await {
Ok(value) => {
// Serialize and publish
if let Ok(bytes) = serializer(&*value) {
let _ = client.publish(&topic_clone, bytes).await;
}
}
Err(_) => break,
}
}
}
Err(_) => { /* Log error */ }
}
})?;
}
Ok(())
}
}Why this change? The new trait-based architecture provides:
- ✅ Symmetry with inbound routing (
ProducerTrait↔ConsumerTrait) - ✅ Testability (can mock
ConsumerTraitwithout real records) - ✅ Type safety via factory pattern (type capture at configuration time)
- ✅ Maintainability (connector logic stays in connector crate)
Migration checklist for custom connectors:
- Add
spawn_outbound_publishers()method to connector implementation - Call
db.collect_outbound_routes(protocol_name)inConnectorBuilder::build() - Call
connector.spawn_outbound_publishers(db, routes)?before returning - Use
ConsumerTrait::subscribe_any()to get type-erased readers - Handle serialization with provided
SerializerFn - Test both inbound (
.link_from()) and outbound (.link_to()) data flows
See Connector Development Guide for complete examples.
- Added comprehensive Connector Development Guide
- Updated MQTT connector examples for both Tokio and Embassy
- Enhanced API documentation with bidirectional patterns
0.1.0 - 2025-11-06
- Initial release of AimDB async in-memory database engine
- Type-safe record system using
TypeId-based routing - Three buffer types for different data flow patterns:
- SPMC Ring Buffer: High-frequency data streams with bounded memory
- SingleLatest: State synchronization and configuration updates
- Mailbox: Commands and one-shot events
- Producer-consumer model with async task spawning
- Runtime adapter abstraction for cross-platform support
no_stdcompatibility for embedded targets- Error handling with comprehensive
DbResult<T>andDbErrortypes - Remote access protocol (AimX v1) for cross-process introspection
- Tokio Adapter (
aimdb-tokio-adapter): Full-featured std runtime support- Lock-free buffer implementations
- Configurable buffer capacities
- Comprehensive async task spawning
- Embassy Adapter (
aimdb-embassy-adapter): Embeddedno_stdruntime support- Configurable task pool sizes (8/16/32 concurrent tasks)
- Optimized for resource-constrained devices
- Compatible with ARM Cortex-M targets
- Dual runtime support for both Tokio and Embassy
- Automatic consumer registration via builder pattern
- Topic mapping with QoS and retain configuration
- Pluggable serializers (JSON, MessagePack, Postcard, custom)
- Automatic reconnection handling
- Uses
rumqttcfor std environments - Uses
mountain-mqttfor embedded environments
- MCP Server (
aimdb-mcp): LLM-powered introspection and debugging- Discover running AimDB instances
- Query record values and schemas
- Subscribe to live updates
- Set writable record values
- JSON Schema inference from record values
- Notification persistence to JSONL files
- CLI Tool (
aimdb-cli): Command-line interface (skeleton)- Instance discovery and management commands
- Record inspection capabilities
- Live watch functionality
- Client Library (
aimdb-client): Reusable connection and discovery logic- Unix domain socket communication
- AimX v1 protocol implementation
- Clean error handling
- Blocking wrapper around async AimDB core
- Thread-safe synchronous record access
- Automatic Tokio runtime management
- Ideal for gradual migration from sync to async
- Comprehensive README with architecture overview
- Individual crate documentation with examples
- 12 detailed design documents in
/docs/design - Working examples:
tokio-mqtt-connector-demo: Full Tokio MQTT integrationembassy-mqtt-connector-demo: Embedded RP2040 with WiFi MQTTsync-api-demo: Synchronous API usage patternsremote-access-demo: Cross-process introspection server
- Comprehensive Makefile with color-coded output
- GitHub Actions workflows:
- Continuous integration (format, lint, test)
- Security audits (weekly schedule + on-demand)
- Documentation generation
- Release automation
- Cross-compilation testing for
thumbv7em-none-eabihftarget cargo-denyconfiguration for license and dependency auditing- Dev container setup for consistent development environment
- ✅ Sub-50ms latency for data synchronization
- ✅ Lock-free buffer operations
- ✅ Cross-platform support (MCU → edge → cloud)
- ✅ Type safety with zero-cost abstractions
- ✅ Protocol-agnostic connector architecture
- Kafka and DDS connectors planned for future releases
- CLI tool is currently skeleton implementation
- Performance benchmarks not yet included
- Limited to Unix domain sockets for remote access (no TCP yet)
- Rust 1.75+ required
- Tokio 1.47+ for std environments
- Embassy 0.9+ for embedded environments
- See
deny.tomlfor approved dependency licenses
None (initial release)
Not applicable (initial release)
AimDB v0.1.0 establishes the foundational architecture for async, in-memory data synchronization across MCU → edge → cloud environments. This release focuses on core functionality, dual runtime support, and developer tooling.
Highlights:
- 🚀 Dual runtime support: Works on both standard library (Tokio) and embedded (Embassy)
- 🔒 Type-safe record system eliminates runtime string lookups
- 📦 Three buffer types cover most data patterns
- 🔌 MQTT connector works in both std and
no_stdenvironments - 🤖 MCP server enables LLM-powered introspection
- ✅ 27+ core tests, comprehensive CI/CD, security auditing
Get Started:
cargo add aimdb-core aimdb-tokio-adapterSee README.md for quickstart guide and examples.
Feedback Welcome: This is an early release. Please report issues, suggest features, or contribute at: https://github.com/aimdb-dev/aimdb