Skip to content

M17 centralize embassy connector spines#128

Merged
lxsaah merged 2 commits into
mainfrom
m17-centralize-embassy-connector-spines
Jun 9, 2026
Merged

M17 centralize embassy connector spines#128
lxsaah merged 2 commits into
mainfrom
m17-centralize-embassy-connector-spines

Conversation

@lxsaah

@lxsaah lxsaah commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Closes the M17 milestone: every Embassy connector used to hand-roll the same force-Send bridge between AimDB's Send-everywhere connector contract and Embassy's deliberately !Send primitives. That plumbing — and all of the single-core unsafe — now lives once, audited, in aimdb-embassy-adapter::connectors. The connector crates carry zero unsafe and zero SendFutureWrapper use.

Design 033 originally proposed dropping Send from the connector contract. That was rejected during implementation (it would have pushed !Send onto the std/Tokio side); the doc's Implementation Decision section records why the centralized-spine approach won. The std/Tokio side, aimdb-client, the WebSocket server, examples, and tests are unchanged.

What changed

aimdb-embassy-adapter — new connectors module (features connectors / connector-io)

The one home for the safety invariant (single-core, cooperative Embassy executor — no preemption, no thread migration; every unsafe impl cites it). Gated to no_std builds so the force-Send types can't leak into std/Tokio code.

  • Session spineEmbassySessionClient / EmbassySessionServer (Embassy duals of core's SessionClientConnector / SessionServerConnector), OneShotDialer / OneShotListener over a moved-in peripheral connection (the listener parks forever after the first accept — point-to-point), and the force-Send + Sync OneShotCell for builders holding a moved-in value.
  • Framed connection (connector-io) — EmbassyConnection<Rd, Wr, F> over embedded-io-async Read/Write halves with a pluggable Framer; chunks writes for atomic-or-error HAL BufferedUarts and skip-and-resyncs on undecodable runs.
  • Data-plane bridgesEmbassySinkRaw / EmbassySourceRaw (the !Send duals of core's Connector / Source) with force-Send EmbassySink / EmbassySource wrappers, so an Embassy connector rides core's existing pump_sink / pump_source unchanged; into_box_future for long-lived !Send protocol tasks.
  • EmbassySessionClient::new defaults to reconnect: false (unlike ClientConfig::default): a one-shot dialer can't redial, so the engine would otherwise spin on TransportError::Io forever. A re-dialable transport opts back in via with_config.

aimdb-serial-connector (Embassy half)

Now thin sugar over the spine: this crate contributes only the COBS CobsFramer. Down from a 407-line hand-roll with 7 unsafe impls to 0. The Embassy half is structurally a sibling of the Tokio half — both thin sugar over a shared spine. SerialClient::new(rx, tx) returns the spine's EmbassySessionClient; SerialServer stores the moved-in framed connection in a OneShotCell and drives serve.

aimdb-mqtt-connector (Embassy half)

The hand-rolled outbound publisher and inbound event-router loops are gone; the data-flow rides core's pump_sink / pump_source through the adapter bridges, exactly like the Tokio half. This crate contributes only the broker manager task (mountain-mqtt's run) and the MqttSink / MqttSource over its action/event channels. Per-route qos / retain still arrive from the link URL query (now via ConnectorConfig::protocol_options).

aimdb-knx-connector (Embassy half)

This crate now contributes only the KNX/IP protocol (UDP socket + tunnelling state machine, force-Sended once via into_box_future). Outbound rides pump_sink through the existing Connector impl; inbound telegrams now flow through pump_source via a new static 32-deep telegram channel.

Docs

Behavior changes (intentional, documented in the crate changelogs)

  • KNX outbound: an invalid dynamic group address from a topic provider now drops the value (PublishError::InvalidDestination, logged) instead of silently falling back to the URL's default address — the fallback was misrouting (writing a different group address than the provider asked for), and dropping matches the Tokio half.
  • KNX inbound: the protocol loop forwards telegrams with try_send and drops + logs when the 32-deep channel is full, rather than routing inline. A slow consumer can never stall the loop that answers TUNNELING_ACKs and heartbeats — a stalled loop would time out the gateway tunnel and lose far more than one telegram. (The router already dropped on full producer buffers; this moves the bounded loss point earlier.)
  • MQTT (Embassy) logging: per-message inbound routing logs moved from this crate's defmt calls into core's pump_source (tracing feature), so defmt-only MCU builds no longer log per-message routing failures.

Breaking changes

  • MqttConnectorImpl (Embassy, was pub) removed; its logic collapsed into the builder's build(). Registration via MqttConnectorBuilder is unchanged.
  • Serial Embassy types EmbassySerialConnection / SerialDialer / SerialListener replaced by the spine's EmbassyConnection / OneShotDialer / OneShotListener; SerialClient::new now returns the spine connector (the .scheme(...) / .with_config(...) chaining is unchanged). All crates are pre-1.0 / unreleased entries.

Housekeeping

  • .cargo/audit.toml: dropped the RUSTSEC-2026-0049 ignore — rustls-webpki is now at 0.103.x, the advisory no longer applies.
  • deny.toml: temporary ignore for RUSTSEC-2026-0173 (proc-macro-error2 unmaintained, transitive via defmt-macros/tabled_derive).
  • _external/embassy submodule fast-forwarded 18 upstream commits (with the matching stm32-metapac retag in Cargo.lock).

Testing

  • New host smoke tests for the server spine (aimdb-embassy-adapter/tests/connectors_smoke.rs): OneShotCell take-once semantics, OneShotListener parking forever after the first accept, and core's serve dispatching over the one-shot listener then staying alive on the parked accept. Wired into make test (adapter host tests now run with connectors).
  • The serial Embassy smoke test now exercises the exact MCU spine (OneShotDialer<EmbassyConnection<_, _, CobsFramer>>).
  • New make embedded-check line: adapter with embassy-runtime,connector-io on thumbv7em-none-eabihf.
  • Verified: all host test suites for the changed crates pass; serial / MQTT / KNX / adapter compile clean on thumbv7em-none-eabihf (incl. defmt variants); all three Embassy demos (serial on STM32H563ZI, MQTT, KNX) build; clippy clean with -D warnings on the changed configurations.

lxsaah added 2 commits June 9, 2026 18:14
- Updated documentation in `lib.rs` to clarify the role of `aimdb-embassy-adapter` in handling `Send` and `unsafe` plumbing.
- Modified `embassy_smoke.rs` test to utilize `CobsFramer` and `OneShotDialer` for a more accurate representation of the connection model.
- Added a temporary ignore for `RUSTSEC-2026-0173` in `deny.toml` due to unmaintained dependencies.
- Revised the Embassy implementation pattern in the development guide to emphasize the centralized adapter spine approach and remove unnecessary boilerplate.
- Introduced a new design document outlining the unification of Embassy connectors, focusing on the centralized adapter spine and the removal of `Send` from the connector contract.
@lxsaah lxsaah self-assigned this Jun 9, 2026
@lxsaah lxsaah added the 🔌 bridges Protocol bridges label Jun 9, 2026
@lxsaah lxsaah marked this pull request as ready for review June 9, 2026 20:47
@lxsaah lxsaah merged commit 1558af9 into main Jun 9, 2026
9 checks passed
@lxsaah lxsaah deleted the m17-centralize-embassy-connector-spines branch June 9, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔌 bridges Protocol bridges

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant