Connector framework

This page captures the requirements for taktora-connector: a framework that connects taktora-executor applications to external protocols (MQTT, OPC UA, gRPC, fieldbus) through a controlled boundary, so messy network code lives outside the application’s deterministic core.

The decomposition is two-tier:

This round covers the framework core plus an MQTT reference connector (rumqttc-backed). OPC UA, gRPC, and Beckhoff ADS connectors are deferred to follow-on specs that will reuse the same five contracts.

Top-level umbrella

Feature: Connector framework FEAT_0030

A Rust framework that bridges taktora-executor applications to external protocols through a typed envelope carried over iceoryx2 shared memory. The framework provides five contracts — envelope, codec, routing, health, lifecycle — that every protocol connector instantiates as a plugin (in-app side) and a gateway (out-of-app side). Both halves are taktora-executor ExecutableItem consumers; protocol-specific async work runs on a tokio sidecar contained inside each connector crate.

Deployment chooses whether the gateway runs as a tokio task in-process alongside the plugin host, or as a separate gateway binary. The envelope contract is identical either way; only process-startup wiring differs.

This umbrella is a peer of PLC runtime heart on iceoryx2 (FEAT_0010) “PLC runtime heart”; the connector framework is a general-purpose mechanism, not PLC-specific. Fieldbus integration interface (FEAT_0023) “Fieldbus integration interface” is later expected to :refines: this umbrella once a fieldbus connector spec lands.


Capability clusters

The umbrella decomposes into seven capability clusters. Each cluster is a sub-feature :satisfies: Connector framework (FEAT_0030), with concrete shall-clauses underneath.

Envelope transport

Feature: Envelope transport FEAT_0031
status: open
satisfies: FEAT_0030
is refined by: QG_0003

The on-wire form of every message crossing the plugin↔gateway boundary and the iceoryx2 service shape that carries it. Defines header fields, per-channel sizing, and the zero-copy publish path.

Requirement: ConnectorEnvelope is a POD type REQ_0200
status: open
satisfies: FEAT_0031
is refined by: IMPL_0020
is implemented by: BB_0002, BB_0010

The framework shall define ConnectorEnvelope as a #[repr(C)] plain-old-data type that derives ZeroCopySend (iceoryx2) and contains a fixed header (sequence number, timestamp, payload length, correlation id, reserved word) followed by an inline payload buffer.

Requirement: Per-channel max payload size REQ_0201
status: open
satisfies: FEAT_0031
is refined by: ADR_0004, IMPL_0010
is implemented by: BB_0010
is verified by: TEST_0103, TEST_0124, TEST_0125

The framework shall allow each channel to declare its maximum payload size at service-creation time, carried in ChannelDescriptor. A channel’s envelope payload buffer shall be sized to that maximum; no universal payload ceiling is imposed across the framework.

Requirement: Sequence number monotonically increasing REQ_0202
status: open
satisfies: FEAT_0031
is refined by: IMPL_0020
is implemented by: BB_0010
is verified by: TEST_0121

For each (publisher, channel) pair, the framework shall populate ConnectorEnvelope::sequence_number with a strictly monotonically increasing u64 so receivers can detect missed envelopes.

Requirement: Timestamp recorded at send REQ_0203
status: open
satisfies: FEAT_0031
is refined by: IMPL_0020
is implemented by: BB_0010
is verified by: TEST_0122

The framework shall populate ConnectorEnvelope::timestamp_ns with nanoseconds since the UNIX epoch at the moment the envelope is loaned for send.

Requirement: Correlation id is a passive carrier REQ_0204
status: open
satisfies: FEAT_0031
is refined by: IMPL_0020
is implemented by: BB_0010
is verified by: TEST_0123

The framework shall carry the 32-byte correlation_id field end-to-end from sender to receiver without inspecting it. Application layers may use this field for request/response matching; the framework itself shall not.

Requirement: Zero-copy publish via iceoryx2 loan REQ_0205
status: open
satisfies: FEAT_0031
is refined by: ARCH_0010, ARCH_0011, IMPL_0020
is implemented by: BB_0002
is verified by: TEST_0120

The framework shall publish envelopes via Publisher::loan such that the codec writes the payload directly into shared memory. No envelope shall be copied between an intermediate user-side buffer and shared memory on the send path.

Requirement: One iceoryx2 service per channel direction REQ_0206
status: open
satisfies: FEAT_0031
is refined by: IMPL_0020
is implemented by: BB_0002, BB_0011
is verified by: TEST_0126

For each logical channel direction (outbound app→gateway, inbound gateway→app), the framework shall create a separate iceoryx2 publish-subscribe service whose name is derived deterministically from ChannelDescriptor::name.

Codec abstraction

Feature: Codec abstraction FEAT_0032
status: open
satisfies: FEAT_0030

How typed values become payload bytes, and back. Codec selection is a compile-time decision via a generic parameter on the connector type; no runtime codec dispatch.

Requirement: PayloadCodec trait REQ_0210
status: open
satisfies: FEAT_0032
is refined by: IMPL_0010, IMPL_0030
is implemented by: BB_0003
is verified by: TEST_0110

The framework shall define a PayloadCodec trait carrying format_name(), encode<T: Serialize>(value, &mut [u8]) -> Result<usize>, and decode<T: DeserializeOwned>(&[u8]) -> Result<T>.

Requirement: Codec is a generic parameter on connectors REQ_0211
status: open
satisfies: FEAT_0032
is refined by: ADR_0005

Each Connector implementation shall expose its codec as a generic parameter (MqttConnector<C: PayloadCodec>), monomorphised at compile time. The framework shall not provide runtime codec dispatch or erased_serde-style indirection.

Requirement: JsonCodec is the default codec REQ_0212
status: open
satisfies: FEAT_0032
is refined by: IMPL_0030
is implemented by: BB_0003
is verified by: TEST_0110

The framework shall ship a JsonCodec implementation in taktora-connector-codec behind a default-on json cargo feature.

Requirement: Codec encode error variant REQ_0213
status: open
satisfies: FEAT_0032
is refined by: ARCH_0031, IMPL_0010, IMPL_0030
is verified by: TEST_0111

When PayloadCodec::encode fails (buffer too small, serializer error), ChannelWriter::send shall return ConnectorError::Codec carrying the codec’s format_name() and the underlying source error.

Requirement: Codec decode error variant REQ_0214
status: open
satisfies: FEAT_0032
is verified by: TEST_0112

When PayloadCodec::decode fails on a received envelope, ChannelReader::try_recv shall return ConnectorError::Codec and shall not silently drop the envelope.

Connector trait and routing

Feature: Connector trait and routing FEAT_0033
status: open
satisfies: FEAT_0030

The plugin-side public API: a Connector trait every connector implements, parameterised on a typed routing struct so plugin code is compile-time-checked against the protocol it targets.

Requirement: Connector trait REQ_0220
status: open
satisfies: FEAT_0033
is refined by: IMPL_0040
is implemented by: BB_0001

The framework shall define a Connector trait with associated types Routing: Routing and Codec: PayloadCodec, plus methods name, health, subscribe_health, create_writer<T>, and create_reader<T>.

Requirement: ChannelDescriptor carries typed routing REQ_0221
status: open
satisfies: FEAT_0033
is refined by: ADR_0008, IMPL_0010
is implemented by: BB_0001
is verified by: TEST_0103

ChannelDescriptor<R: Routing> shall carry a logical channel name, the per-channel max payload size, and a typed routing struct R declared by the connector crate.

Requirement: Routing is a marker trait with bounds REQ_0222
status: open
satisfies: FEAT_0033
is refined by: IMPL_0010
is implemented by: BB_0001

The Routing trait shall require Clone + Send + Sync + Debug + 'static and shall add no methods of its own.

Requirement: create_writer / create_reader return concrete handles REQ_0223
status: open
satisfies: FEAT_0033
is refined by: IMPL_0040
is verified by: TEST_0120

Connector::create_writer<T> and Connector::create_reader<T> shall return concrete generic types ChannelWriter<T, C, N> and ChannelReader<T, C, N>, not boxed trait objects.

Requirement: Connector ships its own routing struct REQ_0224
status: open
satisfies: FEAT_0033

Each connector crate (taktora-connector-mqtt, future taktora-connector-opcua, etc.) shall define its own routing struct (MqttRouting, OpcUaRouting, …) implementing the Routing marker trait, exposing protocol-specific fields.

Connection lifecycle

Feature: Connection lifecycle FEAT_0034
status: open
satisfies: FEAT_0030
is refined by: QG_0004, ADR_0009

The observable health state of every connector and the policy by which a connector retries after a stack-level disconnect. Both surfaces are uniform across protocols, regardless of which protocol stack owns the reconnect mechanism.

Requirement: ConnectorHealth state machine REQ_0230
status: open
satisfies: FEAT_0034
is refined by: ARCH_0012, IMPL_0010
is verified by: TEST_0101, TEST_0161

The framework shall define ConnectorHealth as an enum with variants Up, Connecting { since }, Degraded { reason }, and Down { reason, since }. Every connector shall report current health via Connector::health().

Requirement: subscribe_health returns a Channel of HealthEvent REQ_0231
status: open
satisfies: FEAT_0034
is refined by: IMPL_0040

Connector::subscribe_health() shall return an observable handle over the connector’s HealthEvent stream so callers can wire health transitions into ExecutableItem triggers. The handle type is connector-implementation dependent — typically a taktora-executor Channel<HealthEventWire> (where HealthEventWire is the POD wire form, preferred for cross-process gateways) or a thin in-process wrapper around a crossbeam_channel::Receiver<HealthEvent> (acceptable when the plugin and gateway share an address space). The choice is recorded in the connector’s impl:: directive (e.g. taktora-connector-host crate (IMPL_0040)).

Requirement: ReconnectPolicy trait REQ_0232
status: open
satisfies: FEAT_0034
is refined by: IMPL_0010
is verified by: TEST_0137

The framework shall define a ReconnectPolicy trait with next_delay() -> Duration and reset() for connectors whose protocol stack exposes raw connect events.

Requirement: ExponentialBackoff default policy REQ_0233
status: open
satisfies: FEAT_0034
is refined by: IMPL_0010
is verified by: TEST_0100, TEST_0137

The framework shall ship an ExponentialBackoff implementation of ReconnectPolicy configurable with initial delay, max delay, growth factor, and jitter ratio.

Requirement: HealthEvent emitted on every transition REQ_0234
status: open
satisfies: FEAT_0034
is refined by: ARCH_0012, IMPL_0010
is verified by: TEST_0101, TEST_0138, TEST_0161

Every transition between ConnectorHealth variants shall emit a HealthEvent on the connector’s health channel.

Requirement: Stack-internal-reconnect connectors emit health uniformly REQ_0235
status: open
satisfies: FEAT_0034

Connectors whose underlying protocol stack manages reconnect internally (e.g. tonic-managed gRPC channels) shall not be required to use ReconnectPolicy, but shall emit HealthEvent on every observed transition between ConnectorHealth variants.

Process boundary

Feature: Process boundary deployments FEAT_0035
status: open
satisfies: FEAT_0030
is refined by: ADR_0003

The framework supports two deployment shapes — gateway as an in-process tokio task or as a separate gateway binary — using the same envelope contract on both sides.

Requirement: Same envelope contract for both deployments REQ_0240
status: open
satisfies: FEAT_0035
is refined by: ARCH_0020, ARCH_0021

The framework shall use the same ConnectorEnvelope definition, iceoryx2 service shape, and ChannelDescriptor semantics regardless of whether the gateway runs in-process or as a separate binary.

Requirement: In-process gateway is a tokio task REQ_0241
status: open
satisfies: FEAT_0035
is refined by: ARCH_0020
is verified by: TEST_0150

The framework shall support running the gateway as a tokio task spawned by ConnectorHost alongside the plugin’s executor, in a single process.

Requirement: Separate-process gateway is a self-contained binary REQ_0242
status: open
satisfies: FEAT_0035
is refined by: ARCH_0021
is verified by: TEST_0151

The framework shall support running the gateway as a self-contained binary in its own OS process, communicating with the plugin only through iceoryx2 shared memory.

Requirement: Clean exit on SIGINT / SIGTERM on both sides REQ_0243
status: open
satisfies: FEAT_0035
is refined by: ARCH_0013
is verified by: TEST_0152

Both the plugin host and a separate gateway binary shall return cleanly from Executor::run() on SIGINT/SIGTERM, drain any tokio runtime sidecar, and release iceoryx2 services.

Requirement: No app↔gateway control-plane envelopes REQ_0244
status: open
satisfies: FEAT_0035
is verified by: TEST_0153

The framework shall not introduce envelopes carrying control-plane semantics (“ping”, “version”, “shutdown handshake”) on the SHM channel. Health is observed via ConnectorHealth, not negotiated.

MQTT reference connector

Feature: MQTT reference connector FEAT_0036
status: open

The first concrete connector instantiating the framework’s contracts: rumqttc-backed MQTT 3.1.1 plugin and gateway with bidirectional pub/sub, QoS 0+1, retained messages, wildcard subscriptions, and optional TLS.

Requirement: MqttConnector implements Connector REQ_0250
status: open
satisfies: FEAT_0036
is implemented by: BB_0004, BB_0020

The connector crate shall expose MqttConnector<C: PayloadCodec> that implements the Connector trait with type Routing = MqttRouting.

Requirement: MqttRouting carries topic, qos, retained REQ_0251
status: open
satisfies: FEAT_0036
is implemented by: BB_0004, BB_0020

The MqttRouting struct shall carry the MQTT topic name, the QoS level, and a retained-message flag. It shall implement the Routing marker trait.

Requirement: QoS 0 and 1 supported REQ_0252
status: open
satisfies: FEAT_0036
is verified by: TEST_0130, TEST_0131

The connector shall support MQTT QoS levels AtMostOnce (0) and AtLeastOnce (1). QoS 2 is deferred to a follow-on spec.

Requirement: Retained-message publish supported REQ_0253
status: open
satisfies: FEAT_0036
is verified by: TEST_0132

When MqttRouting::retained is true, the connector shall publish the envelope payload as a retained MQTT message.

Requirement: Wildcard subscriptions supported REQ_0254
status: open
satisfies: FEAT_0036
is refined by: ARCH_0011
is verified by: TEST_0102, TEST_0133, TEST_0134
links incoming: RISK_0005

The connector shall accept inbound subscriptions whose topic includes the MQTT wildcards + (single-level) and # (multi-level), and shall demultiplex received messages to the matching ChannelReader instance(s).

Requirement: Username/password authentication REQ_0255
status: open
satisfies: FEAT_0036
is verified by: TEST_0135

The connector shall accept username and password credentials in MqttConnectorOptions and present them on the MQTT CONNECT packet.

Requirement: TLS is optional via cargo feature REQ_0256
status: open
satisfies: FEAT_0036
is verified by: TEST_0136

The connector shall provide TLS support via rustls behind a default-off tls cargo feature. Client-certificate authentication is deferred to a follow-on spec.

Requirement: MQTT 3.1.1 baseline REQ_0257
status: open
satisfies: FEAT_0036

The connector shall target MQTT protocol version 3.1.1. MQTT 5.0 features (user properties, shared subscriptions, response topic) are deferred to a follow-on spec.

Requirement: Tokio sidecar inside the gateway crate REQ_0258
status: open
satisfies: FEAT_0036
is implemented by: BB_0004, BB_0021

The MQTT gateway shall host rumqttc::EventLoop on a tokio runtime contained inside taktora-connector-mqtt. Tokio shall not leak into taktora-executor’s WaitSet thread.

Requirement: Bridge channels are bounded REQ_0259
status: open
satisfies: FEAT_0036
is implemented by: BB_0021, BB_0022
is verified by: TEST_0160

The outbound (taktora-executor → tokio) and inbound (tokio → taktora-executor) bridges shall be bounded channels with configurable capacity in MqttConnectorOptions.

Requirement: Outbound bridge saturation surfaces as BackPressure REQ_0260
status: open
satisfies: FEAT_0036
is refined by: ARCH_0033
is implemented by: BB_0021, BB_0022
is verified by: TEST_0139

When the outbound bridge channel is full, ChannelWriter::send shall return ConnectorError::BackPressure and the connector shall report ConnectorHealth::Degraded.

Requirement: Inbound bridge saturation surfaces as DroppedInbound HealthEvent REQ_0261
status: open
satisfies: FEAT_0036
is refined by: ARCH_0033
is implemented by: BB_0021, BB_0022
is verified by: TEST_0140

When the inbound bridge channel is full, the gateway shall emit HealthEvent::DroppedInbound { count }. For QoS 1 messages, the gateway shall withhold PUBACK until bridge capacity is restored.

EtherCAT reference connector

Feature: EtherCAT reference connector FEAT_0041

A second concrete connector instantiating the framework’s contracts: ethercrab-backed EtherCAT plugin and gateway with cyclic process-data exchange, static per-SubDevice PDO mapping, optional Distributed Clocks bring-up, and ReconnectPolicy-driven bus re-bringup. The gateway owns a single ethercrab MainDevice on one Linux network interface and runs the TX/RX cycle on a tokio sidecar contained inside taktora-connector-ethercat. Linux is the only supported host OS in the first cut.

Requirement: EthercatConnector implements Connector REQ_0310
status: open
satisfies: FEAT_0041
is refined by: IMPL_0050
is implemented by: BB_0030, BB_0031
is verified by: TEST_0200

The connector crate shall expose EthercatConnector<C: PayloadCodec> that implements the Connector trait with type Routing = EthercatRouting.

Requirement: EthercatRouting carries SubDevice and PDO addressing REQ_0311
status: open
satisfies: FEAT_0041
is refined by: IMPL_0050
is implemented by: BB_0030, BB_0031
is verified by: TEST_0201

The EthercatRouting struct shall identify one process-data slice by SubDevice configured address, PDO direction, bit offset within the SubDevice’s process data, and bit length of the mapped object. It shall implement the Routing marker trait.

Requirement: Single MainDevice per gateway instance REQ_0312
status: open
satisfies: FEAT_0041
is refined by: ADR_0021, IMPL_0050
is implemented by: BB_0030, BB_0032
is verified by: TEST_0202

A single EthercatGateway instance shall own at most one ethercrab MainDevice bound to one network interface. Multi-NIC deployments shall instantiate multiple gateways.

Requirement: Bus reaches OP before serving traffic REQ_0313
status: open
satisfies: FEAT_0041
is refined by: ARCH_0040, IMPL_0050
is implemented by: BB_0032
is verified by: TEST_0203

The gateway shall transition the EtherCAT bus to the OP state before accepting envelope traffic from the plugin side.

Requirement: Static PDO mapping per SubDevice REQ_0314
status: open
satisfies: FEAT_0041
is implemented by: BB_0033
is verified by: TEST_0204

The connector shall accept a static PDO-mapping description per SubDevice at build time, declared by the application crate via EthercatConnectorOptions.

Requirement: PDO mapping applied during PRE-OP to SAFE-OP transition REQ_0315
status: open
satisfies: FEAT_0041
is implemented by: BB_0033
is verified by: TEST_0205

The gateway shall apply the configured PDO mapping by issuing SDO writes to the sync-manager assignment indices 0x1C12 (RxPDO) and 0x1C13 (TxPDO) during the PRE-OP to SAFE-OP transition.

Requirement: Cycle time configurable with millisecond resolution REQ_0316
status: open
satisfies: FEAT_0041
is refined by: ARCH_0041, IMPL_0050
is verified by: TEST_0206

The gateway shall accept a configurable cycle duration via EthercatConnectorOptions::cycle_time with a default of 2 ms and a minimum resolution of 1 ms.

Requirement: Missed cycle ticks are skipped not queued REQ_0317
status: open
satisfies: FEAT_0041
is refined by: ARCH_0041, IMPL_0050
is verified by: TEST_0207

When the gateway misses one or more cycle ticks, it shall skip the missed ticks rather than queue them for catch-up execution.

Requirement: Distributed Clocks bring-up is opt-in REQ_0318
status: open
satisfies: FEAT_0041
is refined by: ADR_0023, ARCH_0042, IMPL_0050
is verified by: TEST_0208

The connector shall perform Distributed Clocks bring-up only when EthercatConnectorOptions::distributed_clocks is enabled by the application.

Requirement: Working-counter-based health policy REQ_0319
status: open
satisfies: FEAT_0041
is refined by: ARCH_0041, IMPL_0050
is verified by: TEST_0209

The gateway shall report ConnectorHealth::Up only when the bus is in OP and the working counter on the latest cycle matches the expected value derived from the configured PDO mapping.

Requirement: Working-counter mismatch degrades health REQ_0320
status: open
satisfies: FEAT_0041
is refined by: ARCH_0041, IMPL_0050
is verified by: TEST_0210

When the working counter on a completed cycle is below the expected value, the gateway shall transition ConnectorHealth to Degraded with a reason naming the offending cycle count.

Requirement: Tokio sidecar contained inside the connector crate REQ_0321
status: open
satisfies: FEAT_0041
is refined by: ADR_0026, IMPL_0050
is implemented by: BB_0030
is verified by: TEST_0211

The EtherCAT gateway shall host the ethercrab TX/RX task on a tokio runtime contained inside taktora-connector-ethercat. Tokio shall not leak into taktora-executor’s WaitSet thread.

Requirement: Bridge channels are bounded REQ_0322
status: open
satisfies: FEAT_0041
is refined by: IMPL_0050
is implemented by: BB_0034
is verified by: TEST_0212

The outbound (taktora-executor → tokio) and inbound (tokio → taktora-executor) bridges between the plugin and the gateway sidecar shall be bounded channels with configurable capacity in EthercatConnectorOptions.

Requirement: Outbound bridge saturation surfaces as BackPressure REQ_0323
status: open
satisfies: FEAT_0041
is refined by: IMPL_0050
is implemented by: BB_0034
is verified by: TEST_0213

When the outbound bridge channel is full, ChannelWriter::send shall return ConnectorError::BackPressure and the gateway shall report ConnectorHealth::Degraded.

Requirement: Inbound bridge saturation surfaces as DroppedInbound HealthEvent REQ_0324
status: open
satisfies: FEAT_0041
is refined by: IMPL_0050
is implemented by: BB_0034
is verified by: TEST_0214

When the inbound bridge channel is full, the gateway shall emit HealthEvent::DroppedInbound { count } and drop the inbound process image for that cycle.

Requirement: Linux raw socket required on gateway host REQ_0325
status: open
satisfies: FEAT_0041
is refined by: ADR_0024, IMPL_0050
is implemented by: BB_0032
is verified by: TEST_0215

The gateway shall open the EtherCAT network interface via a Linux raw socket, requiring the CAP_NET_RAW capability on the gateway process.

Requirement: Outbound payload written to PDI bit slice per routing REQ_0326
status: open
satisfies: FEAT_0041
is refined by: IMPL_0050

When a plugin publishes a value through ChannelWriter::send, the gateway shall, before the next cycle’s tx_rx call, write the codec-encoded payload into the cycle’s outbound PDI buffer at the bit offset and bit length declared by the channel’s EthercatRouting carries Sub... (REQ_0311) EthercatRouting. The write shall target the SubDevice’s process image starting at bit_offset from the start of that SubDevice’s outputs region, covering exactly bit_length bits. The framework shall preserve adjacent bit slices (read-modify-write on partial leading / trailing bytes).

Requirement: Inbound payload read from PDI bit slice per routing REQ_0327
status: open
satisfies: FEAT_0041
is refined by: IMPL_0050

After each cycle’s tx_rx call returns successfully, the gateway shall, for every registered inbound channel, extract bit_length bits starting at bit_offset from the SubDevice’s process image inputs region (per the channel’s EthercatRouting carries Sub... (REQ_0311) EthercatRouting), and publish the resulting byte slice on the channel’s inbound iceoryx2 service as a ConnectorEnvelope whose payload_len is ceil(bit_length / 8). The gateway shall not invoke the channel’s codec on this path — codec decoding is the responsibility of the plugin-side ChannelReader::try_recv, keeping the gateway a byte-only mover (symmetric with Outbound payload written to... (REQ_0326), where the plugin’s ChannelWriter::send encodes and the gateway moves the already-encoded bytes). Reads shall not modify the PDI buffer.

Requirement: Per-channel routing registry on the gateway REQ_0328
status: open
satisfies: FEAT_0041
is refined by: IMPL_0050
is verified by: TEST_0219, TEST_0220, TEST_0221

The gateway shall maintain a registry mapping each open ChannelDescriptor to its EthercatRouting and direction (RxPDO outbound / TxPDO inbound), populated when the application calls Connector::create_writer / Connector::create_reader. The cycle loop shall iterate this registry on every cycle — draining the outbound bridge for each Rx channel, repopulating the inbound iceoryx2 service for each Tx channel — without per- cycle heap allocation (no Vec resize, no HashMap re-hash). Required by No heap allocation in dispatch (REQ_0060) from the steady-state posture: connector dispatch shall not allocate.

Host wiring

Feature: Host wiring and builder FEAT_0037
status: open
satisfies: FEAT_0030
is satisfied by: REQ_0270, REQ_0271, REQ_0272, REQ_0273

The composition layer that wraps a taktora_executor::Executor and registers each connector’s contributed ExecutableItem instances — matching taktora-executor’s existing builder idiom.

Requirement: ConnectorHost builder API REQ_0270
status: open
satisfies: FEAT_0037
is refined by: ADR_0006, IMPL_0040
is implemented by: BB_0005

taktora-connector-host shall expose ConnectorHost::builder()...with(connector)...build() returning a ConnectorHost that owns a taktora_executor::Executor.

Requirement: ConnectorGateway builder API REQ_0271
status: open
satisfies: FEAT_0037
is refined by: IMPL_0040
is implemented by: BB_0005

taktora-connector-host shall expose a parallel ConnectorGateway::builder() for the gateway-side composition, producing a binary that owns its own taktora_executor::Executor.

Requirement: Host registers connector items with the executor REQ_0272
status: open
satisfies: FEAT_0037
is refined by: IMPL_0040
is implemented by: BB_0005

ConnectorHost::build() shall call Executor::add for every ExecutableItem contributed by registered connectors and shall return an executor ready to run.

Requirement: Optional Observer adapter for tracing REQ_0273
status: open
satisfies: FEAT_0037
is refined by: ARCH_0032, IMPL_0040

Behind a default-off tracing cargo feature, the host shall provide an Observer adapter (using taktora-executor-tracing) that forwards HealthEvent and ExecutionMonitor callbacks through the global tracing subscriber.

Zenoh reference connector

Feature: Zenoh reference connector FEAT_0042
status: open
satisfies: FEAT_0030
is satisfied by: FEAT_0043, FEAT_0044, FEAT_0045

A third concrete connector instantiating the framework’s contracts: zenoh-backed plugin and gateway with bidirectional pub/sub and queries. The session topology is configurable between peer and client modes; reconnect is delegated to the Zenoh session itself (stack-internal posture mirroring Stack-internal-reconnect co... (REQ_0235)). Queries are exposed via concrete methods on ZenohConnector only — the shared Connector trait is not modified. The gateway owns one zenoh::Session and runs Zenoh’s async callbacks on a tokio sidecar contained inside taktora-connector-zenoh. Linux, macOS, and Windows are supported host operating systems.

Feature: Zenoh pub/sub FEAT_0043
status: open
satisfies: FEAT_0042
is refined by: ADR_0042

The pub/sub half of the Zenoh connector. ChannelWriter and ChannelReader carry codec-encoded values through iceoryx2 SHM services to / from Zenoh publishers and subscribers running on the gateway’s tokio sidecar. Bridges between taktora-executor and tokio are bounded; saturation surfaces as BackPressure on outbound and DroppedInbound health events on inbound.

Requirement: ZenohConnector implements Connector REQ_0400
status: open
satisfies: FEAT_0043
is refined by: IMPL_0060
is implemented by: BB_0040, BB_0041
is verified by: TEST_0301

The connector crate shall expose ZenohConnector<C: PayloadCodec> that implements the Connector trait with type Routing = ZenohRouting and type Codec = C.

Requirement: ZenohRouting carries key_expr and pub/sub QoS fields REQ_0401
status: open
satisfies: FEAT_0043
is refined by: IMPL_0060
is implemented by: BB_0041
is verified by: TEST_0300

The ZenohRouting struct shall carry the Zenoh key expression (key_expr: KeyExprOwned), congestion control mode (Block | Drop), priority (RealTime..Background), reliability (Reliable | BestEffort), and a boolean express flag (batching opt-out). It shall implement the Routing marker trait. Validation of the key expression shall occur on the plugin side inside create_writer / create_reader (and the query-side analogues), before any iceoryx2 service is created; an invalid expression shall yield ConnectorError::Configuration.

Requirement: JsonCodec is the default codec for Zenoh REQ_0402
status: open
satisfies: FEAT_0043
is refined by: IMPL_0060
is verified by: TEST_0302

The Zenoh connector shall accept any PayloadCodec via its C generic parameter (re-affirming Codec is a generic paramete... (REQ_0211)), with JsonCodec as the default codec used by example wiring (re-affirming JsonCodec is the default codec (REQ_0212)).

Requirement: Tokio sidecar contained inside the Zenoh connector crate REQ_0403
status: implemented
satisfies: FEAT_0043
is refined by: IMPL_0060
is implemented by: BB_0042, BB_0044
is verified by: TEST_0314
links outgoing: BB_0042, BB_0044, TEST_0314

The Zenoh gateway shall host the zenoh::Session and all Zenoh async callbacks on a tokio runtime contained inside taktora-connector-zenoh. Tokio shall not leak into taktora-executor’s WaitSet thread (mirrors Tokio sidecar contained ins... (REQ_0321) and Tokio sidecar inside the ga... (REQ_0258)).

Requirement: Zenoh bridge channels are bounded REQ_0404
status: open
satisfies: FEAT_0043
is refined by: IMPL_0060
is implemented by: BB_0044
is verified by: TEST_0305

The outbound (taktora-executor → tokio) and inbound (tokio → taktora-executor) bridges between the plugin and the Zenoh gateway sidecar shall be bounded channels with capacities configurable via ZenohConnectorOptions (outbound_bridge_capacity and inbound_bridge_capacity).

Requirement: Outbound bridge saturation surfaces as BackPressure REQ_0405
status: open
satisfies: FEAT_0043
is refined by: IMPL_0060
is implemented by: BB_0044
is verified by: TEST_0305

When the outbound bridge channel is full, ChannelWriter::send (and any other plugin-side write entry-point that feeds the outbound bridge) shall return ConnectorError::BackPressure and the gateway shall report ConnectorHealth::Degraded.

Requirement: Inbound bridge saturation surfaces as DroppedInbound REQ_0406
status: open
satisfies: FEAT_0043
is refined by: IMPL_0060
is implemented by: BB_0044
is verified by: TEST_0306

When the inbound bridge channel is full, the gateway shall emit HealthEvent::DroppedInbound { count } and drop the offending inbound Zenoh sample or reply chunk for that callback.

Requirement: Zenoh zero-copy publish via iceoryx2 loan REQ_0407
status: open
satisfies: FEAT_0043
is refined by: IMPL_0060
is verified by: TEST_0302

ChannelWriter::send on a Zenoh channel shall publish envelopes via Publisher::loan so that the codec writes the payload directly into shared memory (re-affirms Zero-copy publish via iceor... (REQ_0205)).

Requirement: Zenoh gateway is byte-only on the inbound publish path REQ_0408
status: open
satisfies: FEAT_0043
is refined by: IMPL_0060
is verified by: TEST_0302

On the inbound leg (Zenoh peer → plugin), the gateway shall publish the raw payload bytes received from the Zenoh subscriber or reply callback onto the channel’s inbound iceoryx2 service as a ConnectorEnvelope without invoking the channel’s codec — codec decoding is the responsibility of the plugin-side ChannelReader::try_recv (symmetric with Inbound payload read from P... (REQ_0327)).

Feature: Zenoh queries FEAT_0044
status: open

The query half of the Zenoh connector — Zenoh’s signature request/response primitive, layered on top of the same ConnectorEnvelope shape used by pub/sub. Exposed via concrete non-trait methods on ZenohConnector: create_querier and create_queryable. The framework’s anti-goal NO request/response matchin... (REQ_0290) (no framework-level correlation matching) is preserved — correlation lives inside the Zenoh-specific handle types, using the framework’s existing 32-byte passive correlation_id carrier (Correlation id is a passive... (REQ_0204)).

Requirement: ZenohConnector exposes create_querier and create_queryable REQ_0420
status: open
satisfies: FEAT_0044
is refined by: IMPL_0060
is implemented by: BB_0040, BB_0041, BB_0043
is verified by: TEST_0303

ZenohConnector shall expose, as concrete methods (NOT on the Connector trait), create_querier<Q, R, const N: usize> and create_queryable<Q, R, const N: usize>, returning ZenohQuerier<Q, R, C, N> and ZenohQueryable<Q, R, C, N> respectively, with Q and R bound by serde::Serialize / serde::de::DeserializeOwned as appropriate per direction.

Requirement: ZenohQuerier maps QueryId to envelope correlation_id REQ_0421
status: open
satisfies: FEAT_0044
is refined by: IMPL_0060
is implemented by: BB_0043
is verified by: TEST_0303

ZenohQuerier::send(q: Q) shall mint a fresh QueryId for each call, populate the outbound envelope’s correlation_id with the QueryId, and return the QueryId to the caller so incoming replies on the matching {name}.reply.in iceoryx2 service can be demultiplexed by QueryId.

Requirement: ZenohQueryable correlates replies via correlation_id REQ_0422
status: open
satisfies: FEAT_0044
is refined by: IMPL_0060
is implemented by: BB_0043
is verified by: TEST_0303

ZenohQueryable::try_recv shall surface the gateway-minted QueryId (= the envelope’s correlation_id) alongside the decoded request value Q. ZenohQueryable::reply(id, r) shall stamp id onto the reply envelope’s correlation_id so the gateway-side dispatcher can look up the corresponding zenoh::Query handle. The framework shall not perform this matching itself (preserves NO request/response matchin... (REQ_0290)); the matching lives inside ZenohQueryable.

Requirement: Multi-reply per query supported REQ_0423
status: open
satisfies: FEAT_0044
is refined by: IMPL_0060
is implemented by: BB_0043
is verified by: TEST_0303

ZenohQueryable::reply(id, r) shall be callable zero or more times for the same QueryId before terminate(id). Each call shall publish one reply envelope on the channel’s {name}.reply.out iceoryx2 service; the gateway shall forward each to zenoh::Query::reply on the matching handle.

Requirement: Reply stream end-of-stream framed in payload REQ_0424
status: open
satisfies: FEAT_0044
is refined by: IMPL_0060
is implemented by: BB_0043
is verified by: TEST_0303

The end of a reply stream shall be signalled by a one-byte Zenoh-private frame discriminator at the start of the reply envelope’s payload: 0x01 = data chunk (followed by codec-encoded R); 0x02 = end of stream (no body); 0x03 = timeout terminator (gateway-synthetic, no body). The framework’s ConnectorEnvelope reserved word (ConnectorEnvelope is a POD ... (REQ_0200)) shall not be repurposed for this signal. ZenohQueryable::terminate(id) shall emit a 0x02 envelope for id and free the gateway-side zenoh::Query handle.

Requirement: Query timeout sourced from options, overridable per-querier REQ_0425
status: open
satisfies: FEAT_0044
is refined by: IMPL_0060
is verified by: TEST_0307

The default per-query timeout shall be sourced from ZenohConnectorOptions::query_timeout. ZenohQuerier shall allow this default to be overridden at querier-creation time (via a builder option) or per-call (via an explicit send_with_timeout(q, timeout) method). Timeout expiry on the gateway shall emit a 0x03 terminator (per Reply stream end-of-stream ... (REQ_0424)) on the reply stream for that QueryId.

Requirement: terminate(id) finalizes the upstream zenoh::Query REQ_0426
status: open
satisfies: FEAT_0044
is refined by: IMPL_0060
is implemented by: BB_0042
is verified by: TEST_0303

When the gateway observes a 0x02 end-of-stream envelope from the queryable side (or synthesises a 0x03 timeout), it shall drop the corresponding entry from its correlation_id zenoh::Query map. Dropping the zenoh::Query handle finalizes the reply stream as observed by the upstream Zenoh peer.

Requirement: Codec applied to Q on send and to R on reply REQ_0427
status: open
satisfies: FEAT_0044
is refined by: IMPL_0060
is verified by: TEST_0303, TEST_0304

ZenohQuerier::send shall encode Q via the connector’s C: PayloadCodec into the envelope payload before SHM publish. ZenohQueryable::reply shall encode R via the same codec into envelope.payload[1..] (with byte [0] carrying the 0x01 data discriminator per Reply stream end-of-stream ... (REQ_0424)). Decoding the inbound counterpart (Q on the queryable side, R on the querier side) shall happen plugin-side in try_recv and shall surface codec failures as ConnectorError::Codec per Codec decode error variant (REQ_0214).

Requirement: Reply-side inbound saturation emits DroppedInbound REQ_0428
status: open
satisfies: FEAT_0044
is refined by: IMPL_0060
is verified by: TEST_0306

When the inbound bridge for the reply path (gateway → plugin on a querier channel) saturates, the gateway shall emit HealthEvent::DroppedInbound { count } (re-affirming Inbound bridge saturation s... (REQ_0406)) and drop the offending reply chunk. The in-flight QueryId shall be observable on the plugin side as a reply stream with fewer chunks than the upstream peer sent; no separate “partial reply” error variant is added.

Feature: Zenoh session topology and health FEAT_0045
status: open
satisfies: FEAT_0042
is refined by: ADR_0041

The Zenoh-specific session and observability surface — peer-vs- client mode configuration, scout/locator wiring, and the stack-internal reconnect posture. Health-event semantics inherit from Connection lifecycle (FEAT_0034) and re-affirm Stack-internal-reconnect co... (REQ_0235) (stack- internal reconnect emits health events without ReconnectPolicy).

Requirement: Zenoh session mode is a config knob REQ_0440
status: open
satisfies: FEAT_0045
is refined by: IMPL_0060
is implemented by: BB_0040, BB_0042
is verified by: TEST_0308, TEST_0312, TEST_0313

ZenohConnectorOptions::mode shall accept the values SessionMode::{Peer, Client, Router} and shall default to Peer. The gateway shall translate this knob into the corresponding zenoh::Config field before calling zenoh::open.

Requirement: NO ReconnectPolicy on Zenoh session loss REQ_0441
status: rejected
satisfies: FEAT_0045
is verified by: TEST_0309

The Zenoh connector shall not use ReconnectPolicy trait (REQ_0232) ReconnectPolicy on session loss. Zenoh’s own scout / reconnect machinery owns the retry; the connector merely emits HealthEvent on every observed transition between ConnectorHealth variants (mirrors Stack-internal-reconnect co... (REQ_0235) for tonic/gRPC).

Requirement: HealthEvent emitted on every Zenoh session transition REQ_0442
status: implemented
satisfies: FEAT_0045
is refined by: IMPL_0060
is implemented by: BB_0042
is verified by: TEST_0308
links outgoing: BB_0042, TEST_0308

Every transition of the Zenoh session between alive and closed states observed by the gateway (including the initial Connecting Up and any subsequent re-bringup driven by Zenoh’s own retry) shall emit one HealthEvent on the connector’s health channel (re-affirms HealthEvent emitted on ever... (REQ_0234)).

Requirement: Connect and listen locators surfaced to zenoh::Config REQ_0443
status: open
satisfies: FEAT_0045
is refined by: IMPL_0060
is verified by: TEST_0312, TEST_0313

ZenohConnectorOptions::connect and ZenohConnectorOptions::listen shall be carried to zenoh::Config verbatim before zenoh::open. Validation of locator URIs is delegated to zenoh (the connector neither parses nor canonicalises them).

Requirement: zenoh-integration cargo feature gates the real zenoh dep REQ_0444
status: implemented
satisfies: FEAT_0045
is refined by: IMPL_0060
is implemented by: BB_0040
is verified by: TEST_0310
links outgoing: BB_0040, TEST_0310

The real zenoh crate shall be an optional dependency of taktora-connector-zenoh, activated only by a default-off zenoh-integration cargo feature (mirrors taktora-connector-ethercat (BB_0030)’s bus-integration posture). Availability of MockZenohSession and the connector framework types in the default build is covered separately by MockZenohSession ships unfe... (REQ_0445).

Requirement: MockZenohSession ships unfeature-gated REQ_0445
status: implemented
satisfies: FEAT_0045
is refined by: IMPL_0060
is verified by: TEST_0302, TEST_0310
links outgoing: BB_0040, TEST_0302, TEST_0310

MockZenohSession — an in-process pub/sub + query loopback implementation of the ZenohSessionLike trait — shall ship in the default build, not gated by zenoh-integration. It exists so that the Layer-1 (pure-logic) test pyramid can exercise the full envelope ↔ session ↔ envelope hop without depending on the real zenoh crate.

Requirement: Linux, macOS, and Windows are supported host operating systems REQ_0446
status: implemented
satisfies: FEAT_0045
is refined by: IMPL_0060
is verified by: TEST_0311
links outgoing: BB_0040, TEST_0311

The Zenoh connector shall support Linux, macOS, and Windows as host operating systems for both plugin and gateway (broader than Linux raw socket required o... (REQ_0325)’s Linux-only EtherCAT posture, because Zenoh has no OS-specific socket requirement comparable to CAP_NET_RAW).

CAN (SocketCAN) reference connector

Feature: CAN (SocketCAN) reference connector FEAT_0046

A fourth concrete connector instantiating the framework’s contracts: socketcan-backed CAN plugin and gateway exchanging classical CAN and CAN-FD frames on one or more Linux SocketCAN network interfaces, with internal error-frame-driven health, ReconnectPolicy-driven bus-off recovery, and a non-Linux MockCanInterface for layer-1 tests. The gateway owns N socketcan::CanSocket / CanFdSocket instances — one per registered interface — and runs the RX/TX loops on a tokio sidecar contained inside taktora-connector-can. Linux is the only supported host OS for real I/O; the in-process mock interface is portable.

Feature: CAN frame transport (classical + FD) FEAT_0047
status: open
satisfies: FEAT_0046

The on-wire form of CAN traffic crossing the plugin↔gateway boundary. CanRouting declares per-channel CAN ID, mask, and frame kind (Classical or FD); the iceoryx2 service payload carries the raw CAN data bytes (codec-encoded plugin-side per Codec is a generic paramete... (REQ_0211)), with the gateway acting as a byte-only mover (symmetric with Inbound payload read from P... (REQ_0327) and Zenoh gateway is byte-only ... (REQ_0408)).

Feature: Multi-interface gateway and per-channel filtering FEAT_0048
status: open
satisfies: FEAT_0046

The gateway-side multiplexer: one gateway instance can own multiple Linux CAN interfaces (broader than Single MainDevice per gatew... (REQ_0312)’s single-MainDevice EtherCAT posture). Per-channel CAN ID and mask are compiled into one CAN_RAW_FILTER setsockopt per interface, recomputed when channels are added or removed.

Feature: Bus health, error frames, and reconnect FEAT_0049
status: open

The CAN-specific health surface: per-interface state aggregated into the connector’s single externally-visible ConnectorHealth, error-frame consumption driving transitions internally, and ReconnectPolicy-driven socket reopen on bus-off. Health-event semantics inherit from Connection lifecycle (FEAT_0034).

Requirement: CanConnector implements Connector REQ_0600
status: open
satisfies: FEAT_0046
is refined by: IMPL_0080
is implemented by: BB_0070, BB_0071
is verified by: TEST_0500

The connector crate shall expose CanConnector<C: PayloadCodec> that implements the Connector trait with type Routing = CanRouting and type Codec = C.

Requirement: CanRouting carries iface, can_id, mask, kind, fd_flags REQ_0601
status: open
satisfies: FEAT_0046
is refined by: IMPL_0080
is implemented by: BB_0071
is verified by: TEST_0501

The CanRouting struct shall identify one channel by Linux network interface name (iface, bounded ASCII string of IFNAMSIZ − 1 = 15 bytes), CAN identifier (can_id, with an explicit extended: bool flag distinguishing 11-bit from 29-bit IDs), kernel-style ID mask (mask: u32), frame kind (CanFrameKind::{Classical, Fd}), and FD bit-rate-switch / error-state-indicator flags (fd_flags: CanFdFlags, ignored when kind == Classical). It shall implement the Routing marker trait.

Requirement: Linux is the supported host OS for real I/O REQ_0602
status: open
satisfies: FEAT_0046
is refined by: IMPL_0080
is implemented by: BB_0070

The CAN gateway shall open SocketCAN interfaces via the Linux PF_CAN socket family, requiring the CAP_NET_RAW capability on the gateway process (mirrors Linux raw socket required o... (REQ_0325)). The plugin-side CanConnector and the MockCanInterface shall remain portable to macOS and Windows for layer-1 development.

Requirement: socketcan-integration cargo feature gates the real socketcan dep REQ_0603
status: open
satisfies: FEAT_0046
is refined by: IMPL_0080
is implemented by: BB_0070
is verified by: TEST_0511

The socketcan crate shall be an optional dependency of taktora-connector-can, activated only by a default-off socketcan-integration cargo feature (mirrors zenoh-integration cargo fea... (REQ_0444)’s zenoh-integration posture and taktora-connector-ethercat (BB_0030)’s bus-integration posture).

Requirement: MockCanInterface ships unfeature-gated REQ_0604
status: open
satisfies: FEAT_0046
is refined by: IMPL_0080
is implemented by: BB_0070, BB_0075
is verified by: TEST_0511

MockCanInterface — an in-process loopback implementation of the CanInterfaceLike trait — shall ship in the default build, not gated by socketcan-integration. It exists so that the Layer-1 (pure-logic) test pyramid can exercise the full envelope ↔ interface ↔ envelope hop without depending on the real socketcan crate or a Linux kernel CAN module (mirrors MockZenohSession ships unfe... (REQ_0445)).

Requirement: Tokio sidecar contained inside the CAN connector crate REQ_0605
status: open
satisfies: FEAT_0046
is refined by: IMPL_0080
is implemented by: BB_0070, BB_0073
is verified by: TEST_0508

The CAN gateway shall host its RX/TX tasks on a tokio runtime contained inside taktora-connector-can. Tokio shall not leak into taktora-executor’s WaitSet thread (mirrors Tokio sidecar contained ins... (REQ_0321), Tokio sidecar inside the ga... (REQ_0258), Tokio sidecar contained ins... (REQ_0403)).

Requirement: CAN bridge channels are bounded REQ_0606
status: open
satisfies: FEAT_0046
is refined by: IMPL_0080
is implemented by: BB_0073
is verified by: TEST_0509, TEST_0510

The outbound (taktora-executor → tokio) and inbound (tokio → taktora-executor) bridges between the plugin and the CAN gateway sidecar shall be bounded channels with capacities configurable via CanConnectorOptions (outbound_bridge_capacity and inbound_bridge_capacity).

Requirement: Outbound bridge saturation surfaces as BackPressure REQ_0607
status: open
satisfies: FEAT_0046
is refined by: IMPL_0080
is implemented by: BB_0073
is verified by: TEST_0509

When the outbound bridge channel is full, ChannelWriter::send shall return ConnectorError::BackPressure and the gateway shall report ConnectorHealth::Degraded.

Requirement: Inbound bridge saturation surfaces as DroppedInbound REQ_0608
status: open
satisfies: FEAT_0046
is refined by: IMPL_0080
is implemented by: BB_0073
is verified by: TEST_0510

When the inbound bridge channel is full, the gateway shall emit HealthEvent::DroppedInbound { count } and drop the offending inbound CAN frame for that callback.

Requirement: Classical CAN frames supported REQ_0610
status: open
satisfies: FEAT_0047
is refined by: IMPL_0080
is verified by: TEST_0502

For channels declared with CanFrameKind::Classical, the connector shall transport up to 8 bytes of payload per frame, with 11-bit standard or 29-bit extended identifiers (per CanRouting carries iface, c... (REQ_0601)’s extended flag). The corresponding iceoryx2 service payload capacity shall be 8 bytes plus the ConnectorEnvelope header.

Requirement: CAN-FD frames supported REQ_0611
status: open
satisfies: FEAT_0047
is refined by: IMPL_0080
is verified by: TEST_0503

For channels declared with CanFrameKind::Fd, the connector shall transport up to 64 bytes of payload per frame with the FD-specific bit-rate-switch (BRS) and error-state-indicator (ESI) flags carried in CanFdFlags. The corresponding iceoryx2 service payload capacity shall be 64 bytes plus the ConnectorEnvelope header.

Requirement: Channel payload sizing keyed on frame kind REQ_0612
status: open
satisfies: FEAT_0047
is refined by: IMPL_0080
is implemented by: BB_0071
is verified by: TEST_0502

ChannelDescriptor<CanRouting>::max_payload_size shall be derived deterministically from CanRouting::kind: 8 bytes for Classical, 64 bytes for Fd. The framework shall reject any plugin-provided override that violates this mapping with ConnectorError::Configuration.

Requirement: Outbound payload serialised to socketcan frame REQ_0613
status: open
satisfies: FEAT_0047
is refined by: ARCH_0060, IMPL_0080
is implemented by: BB_0072
is verified by: TEST_0502, TEST_0503, TEST_0512

When a plugin publishes a value through ChannelWriter::send, the gateway shall, before the next RX/TX iteration on the target interface, construct a socketcan::CanFrame (for Classical) or socketcan::CanFdFrame (for Fd) whose identifier is the channel’s CanRouting::can_id (with the extended flag honoured), whose data bytes are the codec-encoded envelope payload, whose DLC is the payload length, and — for FD — whose BRS / ESI flags are copied from CanRouting::fd_flags. The gateway shall not re-encode the payload.

Requirement: Inbound gateway is byte-only on the publish path REQ_0614
status: open
satisfies: FEAT_0047
is refined by: ARCH_0061, IMPL_0080
is implemented by: BB_0072
is verified by: TEST_0502, TEST_0512

On the inbound leg (CAN bus → plugin), the gateway shall publish the raw frame data bytes received from the SocketCAN read onto the matching channel’s inbound iceoryx2 service as a ConnectorEnvelope without invoking the channel’s codec — codec decoding is the responsibility of the plugin-side ChannelReader::try_recv (symmetric with Inbound payload read from P... (REQ_0327) and Zenoh gateway is byte-only ... (REQ_0408)).

Requirement: CAN ID extended flag preserved end-to-end REQ_0615
status: open
satisfies: FEAT_0047
is refined by: IMPL_0080
is implemented by: BB_0071
is verified by: TEST_0501

The CanRouting::can_id.extended boolean shall be preserved end-to-end between plugin and gateway: outbound, the gateway shall set the CAN_EFF_FLAG bit on the kernel-side frame iff extended is true; inbound, the gateway shall match against can_id and mask honouring the same flag distinction so that 11-bit and 29-bit IDs occupying the same numeric value are delivered to separate readers.

Requirement: Multiple interfaces per gateway REQ_0620
status: open
satisfies: FEAT_0048
is refined by: ARCH_0061, IMPL_0080
is implemented by: BB_0072
is verified by: TEST_0505

A single CanGateway instance shall be capable of owning multiple Linux SocketCAN interfaces (e.g. can0, can1, vcan0) simultaneously. The set of interfaces shall be declared at gateway construction via CanConnectorOptions::ifaces. This requirement is broader than Single MainDevice per gatew... (REQ_0312)’s single-MainDevice EtherCAT posture because SocketCAN bus saturation per interface is far lower than EtherCAT process-image throughput and multi-bus deployments are common in CAN.

Requirement: Routing identifies the interface REQ_0621
status: open
satisfies: FEAT_0048
is refined by: ARCH_0060, IMPL_0080
is implemented by: BB_0071
is verified by: TEST_0505

CanRouting::iface shall identify which gateway-owned SocketCAN interface a channel binds to. The gateway shall reject Connector::create_writer / create_reader calls referencing an interface not listed in CanConnectorOptions::ifaces with ConnectorError::Configuration.

Requirement: Per-interface filter is the union of channel masks REQ_0622
status: open
satisfies: FEAT_0048
is refined by: ARCH_0061, IMPL_0080
is implemented by: BB_0074
is verified by: TEST_0504

For each owned interface, the gateway shall compute the union of (can_id, mask, extended) tuples drawn from every currently-open inbound channel bound to that interface, and apply the result as a single setsockopt(SOL_CAN_RAW, CAN_RAW_FILTER, …) call. Frames not matching any registered filter shall be discarded by the kernel before reaching the gateway’s read loop.

Requirement: Filter recomputed on channel add/remove REQ_0623
status: open
satisfies: FEAT_0048
is refined by: IMPL_0080
is implemented by: BB_0074
is verified by: TEST_0504

The per-interface filter (per Per-interface filter is the... (REQ_0622)) shall be recomputed and re-applied whenever a ChannelReader is created or dropped. The recompute shall not require the interface to be re-opened or the bus to leave its current state.

Requirement: Inbound demux to all matching readers REQ_0624
status: open
satisfies: FEAT_0048
is refined by: ARCH_0061, IMPL_0080
is implemented by: BB_0072, BB_0074
is verified by: TEST_0505

When a CAN frame arrives on an interface, the gateway shall publish the frame’s data bytes (per Inbound gateway is byte-onl... (REQ_0614)) onto the inbound iceoryx2 service of every registered channel whose (iface, can_id, mask, extended) matches the received frame’s identifier under kernel CAN_RAW_FILTER semantics. Overlapping channel filters shall each receive their own envelope copy.

Requirement: Per-iface routing registry has stable iteration order REQ_0625
status: open
satisfies: FEAT_0048
is refined by: IMPL_0080
is implemented by: BB_0072
is verified by: TEST_0514

The gateway shall maintain a per-interface routing registry mapping each open ChannelDescriptor to its CanRouting and direction (outbound writer / inbound reader). The RX/TX loops shall iterate this registry on every frame and every send drain without per-iteration heap allocation (no Vec resize, no HashMap re-hash) — required by No heap allocation in dispatch (REQ_0060) from the steady-state posture, mirroring Per-channel routing registr... (REQ_0328).

Requirement: ConnectorHealth aggregates per-iface state via worst-of REQ_0630
status: open
satisfies: FEAT_0049
is refined by: ARCH_0062, IMPL_0080
is implemented by: BB_0072
is verified by: TEST_0507

The single externally-visible ConnectorHealth reported by CanConnector shall be the worst (least-healthy) of the per-interface sub-states held by the gateway: any interface Down shall surface as Degraded while at least one other interface remains Up, and shall surface as Down only when every owned interface is Down. Per-interface reasons shall be carried in the HealthEvent payload (e.g. DegradedReason::IfaceDown { iface: "can1" }).

Requirement: Error frames consumed internally REQ_0631
status: open
satisfies: FEAT_0049
is refined by: IMPL_0080
is implemented by: BB_0072
is verified by: TEST_0513

The gateway shall enable the CAN_ERR_FLAG error-frame reporting mode on each owned interface via setsockopt(SOL_CAN_RAW, CAN_RAW_ERR_FILTER, CAN_ERR_MASK), consume error frames inside its RX loop, and use them only to drive ConnectorHealth transitions. Error frames shall not reach any plugin-visible channel (re-affirmed by NO plugin-visible error-fra... (REQ_0643)).

Requirement: error-passive transitions to Degraded REQ_0632
status: open
satisfies: FEAT_0049
is refined by: ARCH_0062, IMPL_0080
is verified by: TEST_0507

When an interface reports an error-passive or error-warning condition via an error frame, the gateway shall transition that interface’s sub-state to Degraded with a reason identifying the interface and the kernel error class (DegradedReason::ErrorPassive { iface }).

Requirement: bus-off transitions to Down and triggers reconnect REQ_0633
status: open
satisfies: FEAT_0049
is refined by: ARCH_0062, IMPL_0080
is verified by: TEST_0506

When an interface reports a bus-off condition via an error frame, the gateway shall transition that interface’s sub-state to Down, close the underlying socket, and schedule a reopen attempt governed by the connector’s ReconnectPolicy. Once the socket is reopened, the gateway shall re-apply the per-interface filter (Per-interface filter is the... (REQ_0622)) before transitioning back through Connecting.

Requirement: ReconnectPolicy reused; ExponentialBackoff default REQ_0634
status: open
satisfies: FEAT_0049
is refined by: ARCH_0062, IMPL_0080
is verified by: TEST_0506

The CAN connector shall use the framework-level ReconnectPolicy trait (ReconnectPolicy trait (REQ_0232)) with ExponentialBackoff (ExponentialBackoff default ... (REQ_0233)) as the default implementation, configurable via CanConnectorOptions::reconnect_policy. This is the EtherCAT posture (contrast NO ReconnectPolicy on Zenoh... (REQ_0441) for Zenoh’s stack-internal posture) — SocketCAN exposes raw bus-off events and the gateway owns the reopen.

Requirement: HealthEvent emitted on every transition REQ_0635
status: open
satisfies: FEAT_0049
is refined by: ARCH_0062, IMPL_0080
is verified by: TEST_0507

Every transition between ConnectorHealth variants — including per-interface sub-state transitions that change the aggregated state per ConnectorHealth aggregates ... (REQ_0630) — shall emit one HealthEvent on the connector’s health channel (re-affirms HealthEvent emitted on ever... (REQ_0234)).

Requirement: Error frames not exposed to plugin REQ_0636
status: open
satisfies: FEAT_0049
is refined by: IMPL_0080
is verified by: TEST_0513

No ChannelReader<T> shall ever observe a CAN error frame as a Received<T> value. Error-frame visibility is confined to the gateway and surfaced exclusively through ConnectorHealth and HealthEvent. This is the project posture chosen during brainstorming over a plugin-visible error channel; reconsider only if a downstream consumer demonstrates a concrete need.


Anti-goals

The following requirements are explicitly rejected — captured for the record so that future readers see what the framework deliberately does not do, and why. Each rejected requirement :satisfies: Connector framework (FEAT_0030) to keep the umbrella’s traceability complete.

Requirement: NO request/response matching by the framework REQ_0290
status: rejected
satisfies: FEAT_0030

The framework shall not match requests to responses using ConnectorEnvelope::correlation_id. The field is a passive carrier; higher-layer code may use it for correlation, but the framework performs no inspection or matching.

Requirement: NO app↔gateway control plane REQ_0291
status: rejected
satisfies: FEAT_0030
is verified by: TEST_0153

The framework shall not introduce envelopes carrying ping, version-negotiation, or shutdown-handshake semantics across the plugin↔gateway boundary. Health and lifecycle are observed via ConnectorHealth, not negotiated through SHM control-plane envelopes.

Requirement: NO persistent outbox or durable buffering REQ_0292
status: rejected
satisfies: FEAT_0030

The framework shall not persist outbound envelopes on disk or in any durable store when the gateway is Down. ChannelWriter::send shall return Err(Down) instead. Durability is the responsibility of the broker (MQTT QoS 1/2) or an application-level outbox layered above the connector.

Requirement: NO schema/contract enforcement across the boundary REQ_0293
status: rejected
satisfies: FEAT_0030

The framework shall not verify that plugin and gateway agree on the channel’s payload type T or codec C. Mismatch surfaces only as a decode failure; the framework offers no central schema registry.

Requirement: NO protocol-portable Channel<T> REQ_0294
status: rejected
satisfies: FEAT_0030

The framework shall not offer a channel type that is portable between protocols (“write the same plugin code, swap MQTT for OPC UA without code changes”). Plugin code imports its connector’s Routing and is concrete about which protocol it targets.

Requirement: NO multi-broker / multi-tenant gateway REQ_0295
status: rejected
satisfies: FEAT_0030

A single MqttGateway instance shall connect to at most one MQTT broker. Multi-broker deployments shall instantiate multiple gateways.

Requirement: NO supervision / panic recovery REQ_0296
status: rejected
satisfies: FEAT_0030

The framework shall not catch panics from the tokio task or any protocol-stack worker. A panic shall propagate and abort the gateway process; restart policy is the host’s responsibility, matching taktora-executor’s existing posture.

Requirement: NO DBC parsing or typed signal extraction in taktora-connector-can REQ_0640
status: rejected
satisfies: FEAT_0046

The CAN connector shall not parse Vector DBC files or perform bit-/signal-level extraction from CAN payloads. The connector is a raw-frame transport; typed signal codecs are a separate concern for a future feature layered on top.

Requirement: NO ISO-TP or J1939 support in taktora-connector-can REQ_0641
status: rejected
satisfies: FEAT_0046

The CAN connector shall not implement ISO-TP (ISO 15765-2) segmentation or J1939 (PGN, transport protocol, address claim). Applications needing higher-layer CAN protocols shall either layer them above CanConnector or open a separate CAN_ISOTP / CAN_J1939 socket family connector in a follow-on spec.

Requirement: NO CAN-XL support REQ_0642
status: rejected
satisfies: FEAT_0046

The CAN connector shall not transport CAN-XL (CiA 610-1) frames. The first cut targets classical CAN and CAN-FD only; CAN-XL is deferred to a follow-on spec once the underlying socketcan crate and the Linux kernel surface stabilise.

Requirement: NO plugin-visible error-frame channel REQ_0643
status: rejected
satisfies: FEAT_0049
is verified by: TEST_0513

The CAN connector shall not expose CAN error frames as a plugin-readable ChannelReader. Error-frame consumption stays inside the gateway and surfaces only through ConnectorHealth / HealthEvent (re-affirms Error frames not exposed to... (REQ_0636)).

Requirement: NO can-restart-ms management from the gateway REQ_0644
status: rejected
satisfies: FEAT_0049

The CAN connector shall not set the kernel’s can-restart-ms netlink attribute on owned interfaces. Interface bring-up (ip link set canX up type can ) and auto-restart configuration remain a host / sysadmin concern; taktora-connector-can only opens the already-up interface.


Cross-cutting traceability

Every requirement on this page (excluding rejected anti-goals) carries a :satisfies: link to its capability-cluster feat; every cluster feat :satisfies: Connector framework (FEAT_0030). Architectural specifications (spec directives) refining these requirements are emitted in Connector framework — architecture (arc42). Verification artefacts (test directives) are emitted in Connector framework — verification.

Used filter: types(feat)

ID

Title

Status

Satisfies

FEAT_0030

Connector framework

open

FEAT_0031

Envelope transport

open

FEAT_0030

FEAT_0032

Codec abstraction

open

FEAT_0030

FEAT_0033

Connector trait and routing

open

FEAT_0030

FEAT_0034

Connection lifecycle

open

FEAT_0030

FEAT_0035

Process boundary deployments

open

FEAT_0030

FEAT_0036

MQTT reference connector

open

FEAT_0030

FEAT_0037

Host wiring and builder

open

FEAT_0030

FEAT_0041

EtherCAT reference connector

open

FEAT_0030

FEAT_0042

Zenoh reference connector

open

FEAT_0030

FEAT_0043

Zenoh pub/sub

open

FEAT_0042

FEAT_0044

Zenoh queries

open

FEAT_0042

FEAT_0045

Zenoh session topology and health

open

FEAT_0042

FEAT_0046

CAN (SocketCAN) reference connector

open

FEAT_0030

FEAT_0047

CAN frame transport (classical + FD)

open

FEAT_0046

FEAT_0048

Multi-interface gateway and per-channel filtering

open

FEAT_0046

FEAT_0049

Bus health, error frames, and reconnect

open

FEAT_0046

Used filter: types(req)

ID

Title

Status

Satisfies

REQ_0200

ConnectorEnvelope is a POD type

open

FEAT_0031

REQ_0201

Per-channel max payload size

open

FEAT_0031

REQ_0202

Sequence number monotonically increasing

open

FEAT_0031

REQ_0203

Timestamp recorded at send

open

FEAT_0031

REQ_0204

Correlation id is a passive carrier

open

FEAT_0031

REQ_0205

Zero-copy publish via iceoryx2 loan

open

FEAT_0031

REQ_0206

One iceoryx2 service per channel direction

open

FEAT_0031

REQ_0210

PayloadCodec trait

open

FEAT_0032

REQ_0211

Codec is a generic parameter on connectors

open

FEAT_0032

REQ_0212

JsonCodec is the default codec

open

FEAT_0032

REQ_0213

Codec encode error variant

open

FEAT_0032

REQ_0214

Codec decode error variant

open

FEAT_0032

REQ_0220

Connector trait

open

FEAT_0033

REQ_0221

ChannelDescriptor carries typed routing

open

FEAT_0033

REQ_0222

Routing is a marker trait with bounds

open

FEAT_0033

REQ_0223

create_writer / create_reader return concrete handles

open

FEAT_0033

REQ_0224

Connector ships its own routing struct

open

FEAT_0033

REQ_0230

ConnectorHealth state machine

open

FEAT_0034

REQ_0231

subscribe_health returns a Channel of HealthEvent

open

FEAT_0034

REQ_0232

ReconnectPolicy trait

open

FEAT_0034

REQ_0233

ExponentialBackoff default policy

open

FEAT_0034

REQ_0234

HealthEvent emitted on every transition

open

FEAT_0034

REQ_0235

Stack-internal-reconnect connectors emit health uniformly

open

FEAT_0034

REQ_0240

Same envelope contract for both deployments

open

FEAT_0035

REQ_0241

In-process gateway is a tokio task

open

FEAT_0035

REQ_0242

Separate-process gateway is a self-contained binary

open

FEAT_0035

REQ_0243

Clean exit on SIGINT / SIGTERM on both sides

open

FEAT_0035

REQ_0244

No app↔gateway control-plane envelopes

open

FEAT_0035

REQ_0250

MqttConnector implements Connector

open

FEAT_0036

REQ_0251

MqttRouting carries topic, qos, retained

open

FEAT_0036

REQ_0252

QoS 0 and 1 supported

open

FEAT_0036

REQ_0253

Retained-message publish supported

open

FEAT_0036

REQ_0254

Wildcard subscriptions supported

open

FEAT_0036

REQ_0255

Username/password authentication

open

FEAT_0036

REQ_0256

TLS is optional via cargo feature

open

FEAT_0036

REQ_0257

MQTT 3.1.1 baseline

open

FEAT_0036

REQ_0258

Tokio sidecar inside the gateway crate

open

FEAT_0036

REQ_0259

Bridge channels are bounded

open

FEAT_0036

REQ_0260

Outbound bridge saturation surfaces as BackPressure

open

FEAT_0036

REQ_0261

Inbound bridge saturation surfaces as DroppedInbound HealthEvent

open

FEAT_0036

REQ_0270

ConnectorHost builder API

open

FEAT_0037

REQ_0271

ConnectorGateway builder API

open

FEAT_0037

REQ_0272

Host registers connector items with the executor

open

FEAT_0037

REQ_0273

Optional Observer adapter for tracing

open

FEAT_0037

REQ_0290

NO request/response matching by the framework

rejected

FEAT_0030

REQ_0291

NO app↔gateway control plane

rejected

FEAT_0030

REQ_0292

NO persistent outbox or durable buffering

rejected

FEAT_0030

REQ_0293

NO schema/contract enforcement across the boundary

rejected

FEAT_0030

REQ_0294

NO protocol-portable Channel<T>

rejected

FEAT_0030

REQ_0295

NO multi-broker / multi-tenant gateway

rejected

FEAT_0030

REQ_0296

NO supervision / panic recovery

rejected

FEAT_0030

REQ_0310

EthercatConnector implements Connector

open

FEAT_0041

REQ_0311

EthercatRouting carries SubDevice and PDO addressing

open

FEAT_0041

REQ_0312

Single MainDevice per gateway instance

open

FEAT_0041

REQ_0313

Bus reaches OP before serving traffic

open

FEAT_0041

REQ_0314

Static PDO mapping per SubDevice

open

FEAT_0041

REQ_0315

PDO mapping applied during PRE-OP to SAFE-OP transition

open

FEAT_0041

REQ_0316

Cycle time configurable with millisecond resolution

open

FEAT_0041

REQ_0317

Missed cycle ticks are skipped not queued

open

FEAT_0041

REQ_0318

Distributed Clocks bring-up is opt-in

open

FEAT_0041

REQ_0319

Working-counter-based health policy

open

FEAT_0041

REQ_0320

Working-counter mismatch degrades health

open

FEAT_0041

REQ_0321

Tokio sidecar contained inside the connector crate

open

FEAT_0041

REQ_0322

Bridge channels are bounded

open

FEAT_0041

REQ_0323

Outbound bridge saturation surfaces as BackPressure

open

FEAT_0041

REQ_0324

Inbound bridge saturation surfaces as DroppedInbound HealthEvent

open

FEAT_0041

REQ_0325

Linux raw socket required on gateway host

open

FEAT_0041

REQ_0326

Outbound payload written to PDI bit slice per routing

open

FEAT_0041

REQ_0327

Inbound payload read from PDI bit slice per routing

open

FEAT_0041

REQ_0328

Per-channel routing registry on the gateway

open

FEAT_0041

REQ_0400

ZenohConnector implements Connector

open

FEAT_0043

REQ_0401

ZenohRouting carries key_expr and pub/sub QoS fields

open

FEAT_0043

REQ_0402

JsonCodec is the default codec for Zenoh

open

FEAT_0043

REQ_0403

Tokio sidecar contained inside the Zenoh connector crate

implemented

FEAT_0043

REQ_0404

Zenoh bridge channels are bounded

open

FEAT_0043

REQ_0405

Outbound bridge saturation surfaces as BackPressure

open

FEAT_0043

REQ_0406

Inbound bridge saturation surfaces as DroppedInbound

open

FEAT_0043

REQ_0407

Zenoh zero-copy publish via iceoryx2 loan

open

FEAT_0043

REQ_0408

Zenoh gateway is byte-only on the inbound publish path

open

FEAT_0043

REQ_0420

ZenohConnector exposes create_querier and create_queryable

open

FEAT_0044

REQ_0421

ZenohQuerier maps QueryId to envelope correlation_id

open

FEAT_0044

REQ_0422

ZenohQueryable correlates replies via correlation_id

open

FEAT_0044

REQ_0423

Multi-reply per query supported

open

FEAT_0044

REQ_0424

Reply stream end-of-stream framed in payload

open

FEAT_0044

REQ_0425

Query timeout sourced from options, overridable per-querier

open

FEAT_0044

REQ_0426

terminate(id) finalizes the upstream zenoh::Query

open

FEAT_0044

REQ_0427

Codec applied to Q on send and to R on reply

open

FEAT_0044

REQ_0428

Reply-side inbound saturation emits DroppedInbound

open

FEAT_0044

REQ_0440

Zenoh session mode is a config knob

open

FEAT_0045

REQ_0441

NO ReconnectPolicy on Zenoh session loss

rejected

FEAT_0045

REQ_0442

HealthEvent emitted on every Zenoh session transition

implemented

FEAT_0045

REQ_0443

Connect and listen locators surfaced to zenoh::Config

open

FEAT_0045

REQ_0444

zenoh-integration cargo feature gates the real zenoh dep

implemented

FEAT_0045

REQ_0445

MockZenohSession ships unfeature-gated

implemented

FEAT_0045

REQ_0446

Linux, macOS, and Windows are supported host operating systems

implemented

FEAT_0045

REQ_0500

Pure parse function with no I/O

open

FEAT_0051

REQ_0501

no_std + alloc compatible

open

FEAT_0051

REQ_0502

quick-xml + serde backend

open

FEAT_0051

REQ_0503

Parser does not depend on ethercrab or codegen

open

FEAT_0051

REQ_0504

IR carries identity, PDO maps, mailbox, DC, and OD

open

FEAT_0051

REQ_0505

Vendor-specific extensions captured as opaque blobs

open

FEAT_0051

REQ_0506

Parse errors carry line and column

open

FEAT_0051

REQ_0510

CodegenBackend trait shape

open

FEAT_0052

REQ_0511

Naming policy is owned by codegen, not the backend

open

FEAT_0052

REQ_0512

Revision collision handled deterministically

open

FEAT_0052

REQ_0513

Common PDO entry types deduplicated

open

FEAT_0052

REQ_0514

Emission target is proc_macro2 TokenStream

open

FEAT_0052

REQ_0520

Backend crate is the sole ethercrab dependency

open

FEAT_0053

REQ_0521

One device struct per ESI device entry

open

FEAT_0053

REQ_0522

SubDeviceIdentity const emitted per device

open

FEAT_0053

REQ_0523

PDO assignment alternatives emitted as sum type

open

FEAT_0053

REQ_0524

One PDO struct per assignment alternative

open

FEAT_0053

REQ_0525

Generated module root exposes a registry

open

FEAT_0053

REQ_0526

Generated code compiles under no_std + alloc

open

FEAT_0053

REQ_0530

EsiDevice trait shape

open

FEAT_0054

REQ_0531

EsiConfigurable trait shape for preop bring-up

open

FEAT_0054

REQ_0532

Traits live in ethercat-esi-rt, not taktora-connector

open

FEAT_0054

REQ_0533

Object dictionary emission is a default-off cargo feature

open

FEAT_0054

REQ_0534

Process image access via bitvec BitSlice

open

FEAT_0054

REQ_0540

Builder API shape

open

FEAT_0055

REQ_0541

Output written to OUT_DIR

open

FEAT_0055

REQ_0542

Cargo rerun-if directives emitted per ESI input

open

FEAT_0055

REQ_0543

Generated output passes through prettyplease

open

FEAT_0055

REQ_0550

cargo esi expand emits one device's generated code

open

FEAT_0056

REQ_0551

cargo esi list enumerates devices in a glob

open

FEAT_0056

REQ_0552

CLI shares the parser and codegen crates

open

FEAT_0056

REQ_0560

Verifier ingests ESI XML plus SII binary

open

FEAT_0057

REQ_0561

Diagnostic output names the differing field

open

FEAT_0057

REQ_0562

Verifier reuses the parser

open

FEAT_0057

REQ_0563

Verifier exits non-zero on mismatch

open

FEAT_0057

REQ_0590

NO CAN / CANopen / EDS support in this round

rejected

FEAT_0050

REQ_0591

NO proc-macro front-end

rejected

FEAT_0050

REQ_0592

NO unification of EtherCAT and CANopen runtime traits

rejected

FEAT_0050

REQ_0593

NO runtime XML parsing

rejected

FEAT_0050

REQ_0594

NO modification of taktora-connector-ethercat runtime

rejected

FEAT_0050

REQ_0595

NO automatic vendor library scraping

rejected

FEAT_0050

Safety refinements

The connector framework carries five TSRs from the SEooC safety concept (see Technical Safety Concept — TSRs):