Skip to content

Commit f3becdc

Browse files
lxsaahclaude
andauthored
docs: fix AimX v1/v2 rot, dead spec links, and removed-API references (#145)
* refactor(core)!: fuse inbound deserialize+produce at registration (036 W1 inbound) Replace the per-message Box<dyn Any> inbound path (DeserializerKind -> produce_any downcast) with a fused IngestFn built in InboundConnectorBuilder::finish() where T is known: deserialize + produce in one typed closure, no erasure crossing and no boxed future per message (Producer::produce is sync + infallible, design 029). - IngestFn / IngestFactoryFn replace DeserializerFn/ContextDeserializerFn/ DeserializerKind and ProducerTrait/ProducerFactoryFn (all deleted) - InboundConnectorLink carries the ingest factory (non-optional; finish() validates the deserializer before registering, unchanged error strings) - Router::route is now a sync fn taking &RuntimeContext (every production caller already passed Some(&ctx); the context-skip branch is unrepresentable with fused closures, its test removed) - collect_inbound_routes returns Vec<(String, IngestFn)> - pump_source / pump_client inbound / ws dispatch drop one .await - delete dead TypedRecord::create_producer_trait Registrar API (with_deserializer/_raw, link_from) is source-compatible; MQTT/KNX/WS builders pass routes opaquely and compile unchanged. Part of design 036 W1 (data-plane de-Any). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(core)!: fuse outbound subscribe+serialize+topic at registration (036 W1 outbound) Replace the per-message Box<dyn Any> outbound path (subscribe_any -> recv_any -> SerializerFn(&dyn Any) downcast, plus topic_any(&dyn Any)) with a fused SerializedSource built in OutboundConnectorBuilder::finish() where T is known: its readers yield destination + serialized payload directly (subscribe -> recv -> resolve topic -> serialize, all typed). - SerializedSource / SerializedReader / SerializedValue / SourceFactoryFn replace ConsumerTrait/AnyReader/SerializerKind/SerializerFn/ ContextSerializerFn/ConsumerFactoryFn and the erased TopicProviderAny/ TopicProviderWrapper/TopicProviderFn (all deleted; the typed TopicProvider<T> trait is now stored as-is) - OutboundRoute is { topic, source, config }; ConnectorLink carries the source factory (non-optional; finish() validates the serializer before registering, unchanged error strings; the "skip links without serializer" branch in collect_outbound_routes is gone) - RuntimeContext is threaded into SerializedReader::recv per call (026 context serializers), not captured; raw serializers skip the ctx clone - Buffer errors propagate unchanged (BufferLagged => pump continues, other => pump stops); serialize failures are logged and skipped inside the reader, observably identical to the old pump-side continue - pump_sink / pump_client outbound collapse to recv + publish What disappears per message: the Box<dyn Any> allocation, two downcasts, the topic_any erasure crossing, and the subscribe_any boxed future. The one remaining Box::pin per recv is the object-safe-async cost that already existed. Registrar API (with_serializer/_raw, with_topic_provider, link_to) is source-compatible; the KNX fake-gateway and session smoke tests pass unmodified. Part of design 036 W1 (data-plane de-Any). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(core)!: drop unrepresentable error surface; document join erasure (036 W1 wrap-up) - delete SerializeError::TypeMismatch — both constructors died with the W1 fusion (the downcasts are gone); DbError::TypeMismatch is unrelated and stays - delete dead ConnectorClient (held Arc<dyn Any>, zero users) and OutboundConnectorLink (zero users) - document on JoinTrigger why the join fan-in deliberately keeps its Box<dyn Any + Send> (the erasure is the multi-type join API) - CHANGELOG entries (global + aimdb-core + websocket-connector) - check in design docs 034/035/036 and amend the 036 W1 acceptance grep: DynBuffer::as_any and the session auth ext slots are setup-time hits the original literal grep missed Part of design 036 W1 (data-plane de-Any). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(tests): clean up use statements and formatting in tests * docs(design): mark 036 W1 implemented in PR #141 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(core)!: split AnyRecord into capability traits (036 W2) AnyRecord carried ~22 methods spanning four concerns; every remote-access or profiling change churned the core storage contract. Split by consumer: - AnyRecord (6 methods): storage/lifecycle only — validate, as_any(_mut), drain_config_errors, set_writable_erased, plus the cfg-gated json_access() accessor so the remote-access gate lives in one place. - RecordIntrospect (supertrait): graph/metadata introspection consumed by the builder's dependency-graph construction, route collection, and list_records. - JsonRecordAccess (cfg remote-access): latest_json / subscribe_json / set_from_json, reached via json_access(); the runtime .with_remote_access() checks stay inside the methods, so behavior is unchanged. Gate is `remote-access` (the 036 sketch said json-serialize). - RecordMetricsReset (supertrait): the cfg-gated no-op-default resets. Supertrait wiring keeps every dyn AnyRecord call site compiling unchanged; only the four JSON call sites switch to the accessor. Registry storage stays Vec<Box<dyn AnyRecord>>. Drops the dead outbound_connector_urls (cfg std) — zero callers in-tree and in aimdb-pro. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(design): mark 036 W2 implemented in PR #142 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(core): dedup StringKey::intern; document the leak contract (036 W5) intern() leaked a fresh allocation on every call, so re-interning the same key leaked again. A global dedup table (std Mutex / spin Mutex, same pattern as the TypedRecord field locks) now returns the existing 'static allocation for a known key, bounding the leak by the number of *distinct* dynamic keys — the actual lifetime contract of a record key. The contract is documented loudly on intern(), and the debug-build tripwire now counts distinct keys instead of calls. The &'static str / Copy design stays; an Arc<str> key variant remains rejected (forks RecordKey into two shapes — the 034 §3.1 mistake). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(tests): host_test_stubs! macro for the defmt logger triplication (036 W6) The 18-line no-op #[defmt::global_logger] + panic handler + host time driver block existed in three copies (embassy-adapter session_smoke, embassy-adapter buffer.rs tests, serial-connector embassy_smoke). Per 035 §2.4, an exported host_test_stubs! macro in aimdb-embassy-adapter now holds the single definition; each test binary expands it once, preserving the once-per-binary requirement of #[global_logger]/time_driver_impl!. The time driver uses the wake_by_ref variant (buffer.rs's superset behavior: zero-duration sleeps complete; the smokes never sleep). The serial-connector defmt/embassy-time-driver dev-deps stay — the expansion references them at the invocation site; the Cargo.toml comment now says so. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(design): mark 036 W5+W6 implemented in PR #143 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(design): 036 status round — W3 prep done, W4 deferred on W3 data, W7 skipped - W3: Nucleo firmware build verified (thumbv8m.main-none-eabihf); single run on a main build is the bar now that #140 is merged. Bench session is the remaining work and the gate for closing 035. - W4: deferred pending W3 scenario-1 AckTimeout evidence (decision 2026-06-12). Design pre-decided for the trigger: buffer the sent frame (option a) — GroupWrite already carries a 254-byte APDU buffer, so the semantic-content variant saves ~350 B total, no real RAM argument. - W7: skipped entirely (decision 2026-06-12); no audit will be run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(knx): ACK-retransmit knob — retransmit once, disconnect on second miss (036 W4) KNXnet/IP 3.8.4 says: repeat an unACKed TUNNELING_REQUEST once after the timeout, then tear the connection down on the second miss. The engine previously expired and warned only — hardware bench evidence (W3, 2026-06-12) showed ten button-press writes silently lost during a link outage's heartbeat-detection window (~65 s). TunnelConfig gains ack_retransmits (default 1 = spec behavior; 0 = the old expire-and-warn for the previous semantics). The pending-ACK slot buffers the sent frame (option (a) per the 036 W4 decision record: GroupWrite already carries a 254-byte APDU, so rebuilding from semantic content saves ~350 B total — not worth the rebuild path). On expiry with retries left the identical frame is re-sent (same sequence counter) and the timer re-armed; on final expiry AckTimeout is reported and the engine disconnects so queued commands flush after the re-handshake instead of vanishing into a dead tunnel. Eviction on map overflow stays warn-only (overflow is not confirmed loss). Behavior change at default config: a tunnel with a persistently unanswered write now reconnects within ~2× ack_timeout_ms instead of staying connected. Retransmit delay is ack_timeout_ms (3 s, the pre-engine constant); strict spec timing is one config knob away (ack_timeout_ms = 1_000). Tests: engine units (identical-frame retransmit, ack-after-retransmit, disconnect on second miss, legacy mode pinned at ack_retransmits=0) + fake-gateway test dropping the first ACK and asserting the byte-identical repeat and tunnel survival. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(design): mark 036 W4 implemented in PR #144; record W3 bench findings Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(design): record W4 hardware validation (partial) and pre-release W3 scope Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: fix AimX v1/v2 rot, dead spec links, and removed-API references Doc-rot fixes from the post-036 debt scan; no wire or API behavior changes (the one wire-visible byte change — the client hello version — is corrected from a stale "1.0" to the "2.0" the server already announces; the server never validates it). - remote::PROTOCOL_VERSION corrected to "2.0", documented, and exported; the AimX dispatch Welcome and aimdb-client both use it now, so client and server can no longer drift. The dead, never-exported v1 untagged Message envelope and its helpers are deleted. - remote module docs: "AimX v1" + link to the nonexistent docs/design/remote-access/aimx-v1.md replaced with the v2 NDJSON tagged-frame description pointing at session::aimx and remote-access-via-connectors.md; stale .build()? example fixed to the (db, runner) = build().await? shape. - connector module docs: removed-`.link()` API replaced with the real configure/link_to pattern (the old example used a RecordConfig::builder API that never existed); ConnectorUrl no longer advertises Kafka/HTTP connector semantics for connectors that don't exist — documented as scheme-agnostic with real schemes (mqtt, knx, ws, uds, serial). - builder.rs AimDb example fixed: register_record returns &mut Self, so the old chained .build() could not compile. - aimdb-client README rewritten to match reality (AimxConnection, v2 wire, endpoint URLs incl. serial); crate doc + aimdb-cli doc updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 9de3e08 commit f3becdc

11 files changed

Lines changed: 84 additions & 160 deletions

File tree

aimdb-client/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- **Transport-agnostic endpoint resolver — pick the transport at runtime via a `scheme://` URL (Issue #123, follow-up to #39 / #122).** New `endpoint` module: `parse_endpoint` (pure, feature-independent grammar) and `dial(url) -> Box<dyn Dialer>` map an endpoint string to a transport `Dialer`, the way records already pick one for links. Schemes: `unix://PATH` / `uds://PATH`, a bare path (the `unix://` shorthand), and `serial://DEVICE?baud=N`. An unknown scheme — or one whose transport isn't compiled in — is rejected with a clear error. New `AimxConnection::connect_over(dialer)` / `connect_over_with_timeout` dial over an explicit `Dialer`, bypassing resolution. (Rides a new `impl Dialer for Box<dyn Dialer>` in `aimdb-core`.)
1313

14+
### Fixed
15+
16+
- **The `hello` handshake now reports protocol version `"2.0"` (was a stale `"1.0"`).** The client has spoken the AimX-v2 wire since the engine rewrite, but its local `PROTOCOL_VERSION` constant was never bumped; it is now a re-export of `aimdb_core::remote::PROTOCOL_VERSION`, so client and server can no longer drift. (The server does not validate the hello version, so this is cosmetic on the wire.) README updated to match the v2 reality (`AimxConnection`, endpoint URLs, v2 framing).
17+
1418
### Changed (breaking)
1519

1620
- **`AimxConnection::connect` now takes a `&str` endpoint, and transports are feature-gated (Issue #123).** `connect`/`connect_with_timeout` accept an endpoint string (was `impl AsRef<Path>`) — a `scheme://` URL or a bare path — resolved through the new `endpoint` module. The transports are now opt-in Cargo features: `transport-uds` (default; makes `aimdb-uds-connector` optional and gates the `discovery` module — a Unix-socket scan) and `transport-serial` (off by default; pulls `aimdb-serial-connector`, i.e. `tokio-serial` → libudev). `ClientError::ConnectionFailed`'s `socket` field is renamed `endpoint`, and a new `ClientError::UnsupportedEndpoint` covers malformed / not-built-in endpoints. The discovery `InstanceInfo.socket_path` field is likewise renamed `endpoint` — it now also carries a caller-supplied endpoint, not just a discovered socket path.

aimdb-client/README.md

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
# aimdb-client
22

3-
Internal client library for the AimX v1 protocol.
3+
Internal client library for the AimX remote access protocol (v2 NDJSON wire).
44

55
## Overview
66

7-
`aimdb-client` is an **internal library** that provides Rust client implementation for the AimX v1 remote access protocol. It enables programmatic connections to running AimDB instances via Unix domain sockets.
7+
`aimdb-client` is an **internal library** that provides the Rust client
8+
implementation for the AimX remote access protocol. It enables programmatic
9+
connections to running AimDB instances over a transport picked at runtime via
10+
a `scheme://` endpoint URL — Unix domain sockets (`unix://PATH` / `uds://PATH`,
11+
or a bare path) and serial (`serial://DEVICE?baud=N`).
812

913
**This library is used by:**
1014
- `tools/aimdb-cli` - Command-line interface for AimDB
@@ -14,51 +18,43 @@ Internal client library for the AimX v1 protocol.
1418

1519
## Features
1620

17-
- **Async Connection Management**: Non-blocking Unix socket communication
18-
- **Protocol Implementation**: Full AimX v1 handshake and message handling
19-
- **Instance Discovery**: Automatic detection of running AimDB instances
20-
- **Record Operations**: List, get, set, and subscribe to records
21+
- **Async Connection Management**: `AimxConnection` over the shared session engine
22+
- **Protocol Implementation**: AimX-v2 handshake plus RPC and streaming subscriptions
23+
- **Instance Discovery**: Automatic detection of running AimDB instances (UDS)
24+
- **Record Operations**: List, get, set, subscribe, drain, graph introspection, query
2125
- **Type-Safe**: Strongly typed API with serde integration
2226

2327
## API Overview
2428

2529
### Core Types
2630

27-
- `AimxClient` - Main client for connecting to AimDB instances
31+
- `AimxConnection` - Main client for connecting to AimDB instances
2832
- `InstanceInfo` - Information about discovered instances
2933
- `RecordMetadata` - Metadata about registered records
3034
- `ClientError` - Error types for client operations
3135

3236
### Main Operations
3337

3438
- **Discovery**: `discover_instances()`, `find_instance()`
35-
- **Connection**: `AimxClient::connect()`
36-
- **Records**: `list_records()`, `get_record()`, `set_record()`
37-
- **Subscriptions**: `subscribe()`, `unsubscribe()`, `receive_event()`
39+
- **Connection**: `AimxConnection::connect(endpoint)`, `connect_over(dialer)`
40+
- **Records**: `list_records()`, `get_record()`, `set_record()`, `drain_record()`
41+
- **Subscriptions**: `subscribe()` (returns a `Stream` of values)
42+
- **Introspection**: `graph_nodes()`, `graph_edges()`, `graph_topo_order()`, `query()`
3843

3944
### Discovery
4045

4146
Automatically scans for running AimDB instances:
4247
- `/tmp/*.sock`
4348
- `/var/run/aimdb/*.sock`
4449

45-
### Error Types
46-
47-
- `ClientError::NoInstancesFound` - No running instances discovered
48-
- `ClientError::ConnectionFailed` - Socket connection failed
49-
- `ClientError::ServerError` - Server returned error response
50-
- `ClientError::Io` - I/O operation failed
51-
- `ClientError::Json` - JSON serialization failed
52-
5350
## Protocol
5451

55-
The client implements **AimX v1** protocol over Unix domain sockets:
52+
The client speaks the **AimX v2** wire: NDJSON (newline-delimited JSON) tagged
53+
frames mapping onto the session engine's role-neutral message set. It is not
54+
backward-compatible with the legacy AimX v1 framing.
5655

57-
- **Transport**: Unix domain sockets
58-
- **Encoding**: NDJSON (Newline Delimited JSON)
59-
- **Pattern**: JSON-RPC 2.0 style request/response
60-
61-
See `docs/design/008-M3-remote-access.md` for full protocol specification.
56+
See `docs/design/remote-access-via-connectors.md` for the architecture and
57+
`aimdb-core/src/session/aimx/` for the codec.
6258

6359
## Usage Examples
6460

@@ -86,8 +82,6 @@ For detailed API documentation:
8682
cargo doc -p aimdb-client --open
8783
```
8884

89-
For protocol specification, see `docs/design/008-M3-remote-access.md`.
90-
9185
## License
9286

9387
See [LICENSE](../LICENSE) file.

aimdb-client/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//!
33
//! This library provides a client implementation for the AimX remote access
44
//! protocol, enabling connections to running AimDB instances via Unix domain
5-
//! sockets.
5+
//! sockets or serial.
66
//!
77
//! ## Overview
88
//!

aimdb-client/src/protocol.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ use serde::{Deserialize, Serialize};
88
// Re-export protocol types from aimdb-core
99
pub use aimdb_core::remote::{
1010
ErrorObject, Event, HelloMessage, RecordMetadata, Request, Response, WelcomeMessage,
11+
PROTOCOL_VERSION,
1112
};
1213

13-
/// Protocol version supported by this client
14-
pub const PROTOCOL_VERSION: &str = "1.0";
15-
1614
/// Client identifier
1715
pub const CLIENT_NAME: &str = "aimdb-cli";
1816

aimdb-core/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
- **AimX protocol doc rot cleaned up; `remote::PROTOCOL_VERSION` corrected to `"2.0"` and exported.** The `remote` module docs claimed "AimX v1" and linked a spec file that no longer exists; they now describe the v2 NDJSON tagged-frame wire and point at `crate::session::aimx` / `docs/design/remote-access-via-connectors.md`. The AimX dispatch's Welcome uses the constant instead of a hardcoded `"2.0"` (same bytes on the wire). The dead, never-exported v1 `Message` untagged envelope and its helpers were removed from `remote::protocol`. Also de-advertised Kafka/HTTP connector semantics from `ConnectorUrl` docs (the parser is scheme-agnostic; those connectors never existed) and updated the `connector` module docs from the removed `.link()` API to `.link_to()`/`.link_from()`.
1213
- **`build()` reports a missing runtime alongside every other configuration error (issue #133 contract).** The missing-runtime check no longer short-circuits: it is collected as a `ConfigError` and returned in the one `DbError::InvalidConfiguration` with all other findings (previously the collected errors were silently dropped and only a `RuntimeError` surfaced). The error type for a runtime-less build changes accordingly from `DbError::RuntimeError` to `DbError::InvalidConfiguration`.
1314

1415
### Changed (breaking)

aimdb-core/src/builder.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -871,11 +871,9 @@ impl Default for AimDbBuilder {
871871
/// ```rust,ignore
872872
/// use aimdb_tokio_adapter::TokioAdapter;
873873
///
874-
/// let runtime = Arc::new(TokioAdapter);
875-
/// let (db, runner) = AimDbBuilder::new()
876-
/// .runtime(runtime)
877-
/// .register_record::<Temperature>(&TemperatureConfig)
878-
/// .build().await?;
874+
/// let mut builder = AimDbBuilder::new().runtime(Arc::new(TokioAdapter::new()?));
875+
/// builder.register_record::<Temperature>(&TemperatureConfig);
876+
/// let (db, runner) = builder.build().await?;
879877
/// ```
880878
#[derive(Clone)]
881879
pub struct AimDb {

aimdb-core/src/connector.rs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Connector infrastructure for external protocol integration
22
//!
3-
//! Provides the `.link()` builder API for ergonomic connector setup with
4-
//! automatic client lifecycle management. Connectors bridge AimDB records
5-
//! to external systems (MQTT, Kafka, HTTP, etc.).
3+
//! Provides the `.link_to()` / `.link_from()` builder API for ergonomic
4+
//! connector setup with automatic client lifecycle management. Connectors
5+
//! bridge AimDB records to external systems (MQTT, KNX, WebSocket, …).
66
//!
77
//! # Design Philosophy
88
//!
@@ -14,17 +14,14 @@
1414
//! # Example
1515
//!
1616
//! ```rust,ignore
17-
//! use aimdb_core::{RecordConfig, BufferCfg};
17+
//! use aimdb_core::BufferCfg;
1818
//!
19-
//! fn weather_alert_record() -> RecordConfig<WeatherAlert> {
20-
//! RecordConfig::builder()
21-
//! .buffer(BufferCfg::SingleLatest)
22-
//! .link_to("mqtt://broker.example.com:1883")
23-
//! .out::<WeatherAlert>(|reader, mqtt| {
24-
//! publish_alerts_to_mqtt(reader, mqtt)
25-
//! })
26-
//! .build()
27-
//! }
19+
//! builder.configure::<WeatherAlert>("weather.alert", |reg| {
20+
//! reg.buffer(BufferCfg::SingleLatest)
21+
//! .link_to("mqtt://alerts/weather")
22+
//! .with_serializer_raw(|alert: &WeatherAlert| Ok(alert.to_json_vec()))
23+
//! .finish();
24+
//! });
2825
//! ```
2926
3027
use core::fmt::{self, Debug};
@@ -182,23 +179,25 @@ pub trait TopicProvider<T>: Send + Sync {
182179

183180
/// Parsed connector URL with protocol, host, port, and credentials
184181
///
185-
/// Supports multiple protocol schemes:
182+
/// The parser is scheme-agnostic: any `scheme://…` URL parses, and the scheme
183+
/// is matched against whatever connectors are registered on the builder.
184+
/// Connectors in this workspace use e.g.:
186185
/// - MQTT: `mqtt://host:port`, `mqtts://host:port`
187-
/// - Kafka: `kafka://broker1:port,broker2:port/topic`
188-
/// - HTTP: `http://host:port/path`, `https://host:port/path`
186+
/// - KNX: `knx://gateway:3671`
189187
/// - WebSocket: `ws://host:port/path`, `wss://host:port/path`
188+
/// - UDS / serial (session transports): `uds://topic`, `serial://topic`
190189
#[derive(Clone, Debug, PartialEq)]
191190
pub struct ConnectorUrl {
192-
/// Protocol scheme (mqtt, mqtts, kafka, http, https, ws, wss)
191+
/// Protocol scheme (e.g. mqtt, mqtts, knx, ws, wss, uds, serial)
193192
pub scheme: String,
194193

195-
/// Host or comma-separated list of hosts (for Kafka)
194+
/// Host, or a comma-separated host list (preserved verbatim)
196195
pub host: String,
197196

198197
/// Port number (optional, protocol-specific defaults)
199198
pub port: Option<u16>,
200199

201-
/// Path component (for HTTP/WebSocket)
200+
/// Path component (optional)
202201
pub path: Option<String>,
203202

204203
/// Username for authentication (optional)
@@ -219,11 +218,12 @@ impl ConnectorUrl {
219218
/// - `mqtt://host:port`
220219
/// - `mqtt://user:pass@host:port`
221220
/// - `mqtts://host:port` (TLS)
222-
/// - `kafka://broker1:9092,broker2:9092/topic`
223-
/// - `http://host:port/path`
224-
/// - `https://host:port/path?key=value`
225-
/// - `ws://host:port/mqtt` (WebSocket)
226-
/// - `wss://host:port/mqtt` (WebSocket Secure)
221+
/// - `knx://gateway:3671`
222+
/// - `ws://host:port/path?key=value` (WebSocket)
223+
/// - `wss://host:port/path` (WebSocket Secure)
224+
///
225+
/// Any other `scheme://…` parses the same way; comma-separated host
226+
/// lists are preserved verbatim in [`host`](ConnectorUrl::host).
227227
///
228228
/// # Example
229229
///
@@ -281,7 +281,7 @@ impl ConnectorUrl {
281281
///
282282
/// - `mqtt://commands/temperature` → `"commands/temperature"` (topic)
283283
/// - `mqtt://sensors/temp` → `"sensors/temp"` (topic)
284-
/// - `kafka://events` → `"events"` (topic)
284+
/// - `uds://events` → `"events"` (topic)
285285
///
286286
/// The format is `scheme://resource` where resource = host + path combined.
287287
pub fn resource_id(&self) -> String {
@@ -665,7 +665,7 @@ pub trait ConnectorBuilder: Send + Sync {
665665

666666
/// The URL scheme this connector handles
667667
///
668-
/// Returns the scheme (e.g., "mqtt", "kafka", "http") that this connector
668+
/// Returns the scheme (e.g., "mqtt", "knx", "uds") that this connector
669669
/// will be registered under. Used for routing `.link_from()` and `.link_to()`
670670
/// declarations to the appropriate connector.
671671
fn scheme(&self) -> &str;

aimdb-core/src/remote/mod.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66
//!
77
//! # Protocol
88
//!
9-
//! AimX v1 uses NDJSON (newline-delimited JSON) over Unix domain sockets.
10-
//! See `docs/design/remote-access/aimx-v1.md` for full specification.
9+
//! AimX v2 uses NDJSON (newline-delimited JSON) tagged frames over a session
10+
//! transport (Unix domain sockets via `aimdb-uds-connector`, serial via
11+
//! `aimdb-serial-connector`). The envelope codec lives in
12+
//! [`crate::session::aimx`]; see `docs/design/remote-access-via-connectors.md`
13+
//! for the architecture. The v2 wire is not backward-compatible with the
14+
//! legacy AimX v1 framing.
1115
//!
1216
//! # Security
1317
//!
@@ -32,10 +36,11 @@
3236
//! .max_connections(16)
3337
//! .max_subs_per_connection(32);
3438
//!
35-
//! let db = AimDbBuilder::new()
39+
//! let (db, runner) = AimDbBuilder::new()
3640
//! .runtime(tokio_adapter)
3741
//! .with_connector(UdsServer::from_config(config))
38-
//! .build()?;
42+
//! .build()
43+
//! .await?;
3944
//! ```
4045
4146
mod config;
@@ -47,7 +52,9 @@ mod query;
4752
pub use config::{AimxConfig, SecurityPolicy};
4853
pub use error::{RemoteError, RemoteResult};
4954
pub use metadata::RecordMetadata;
50-
pub use protocol::{ErrorObject, Event, HelloMessage, Request, Response, WelcomeMessage};
55+
pub use protocol::{
56+
ErrorObject, Event, HelloMessage, Request, Response, WelcomeMessage, PROTOCOL_VERSION,
57+
};
5158
pub use query::{QueryHandlerFn, QueryHandlerParams};
5259

5360
// Internal exports for implementation

0 commit comments

Comments
 (0)