Skip to content

Verified: EtherNet/IP

Each claim the EtherNet/IP page makes, with the tests that prove it in the last green CI run: 9 of 43 verified, 14 with no test yet. The claims live in docs/claims/ethernet-ip.yaml.

Last green CI run 16 Oct 2026, 14:48 UTC on c7618d3 · all runtime claims

EIP-001 verified

An `eip` driver without `host:` or without `manifest:` is rejected at project load (so `naut check` fails, not `naut run`).

where the docs say so · The loader's buildDriver is given a readable manifest, so a missing host or manifest key is the only reason left to refuse; the error must name the key, and the complete config must build.

EIP-002 partly verified

`New` never dials: it decodes the manifest, rejects bad scan-class configuration and partitions bindings into scan classes offline, so check and build pass with no controller.

where the docs say so · Offline validation is asserted for scan-class errors only; that New opens no socket, strict manifest decoding (a misspelled key is an error) and duplicate-name rejection have no eip test.

  • pass TestScanClassErrors in eip

EIP-003 partly verified

`naut eip browse` prints one line per tag with its name, type name and array dimensions, including program-scope tags as `Program:<prog>.<tag>`.

where the docs say so · Program-scope names and the presence of the strings REAL/BOOL are asserted; array dimensions in the output are not, and the eip/logix test checks the client's symbol list, not printed lines.

  • pass TestLogixEmulateBrowseAndImport in cmd/naut
  • pass TestBrowseAgainstEmulator in eip/logix

EIP-004 verified

`naut eip import` always writes `eip_types.st`; with `--format yaml` it also writes `eip_manifest.yaml` and `tags/eip.yaml`, with `--format go` it writes `eip_manifest.go`, and a missing `--out` directory is created.

where the docs say so

  • pass TestEIPImportCreatesOutDir in cmd/naut
  • pass TestEIPImportGoFormat in cmd/naut
  • pass TestLogixEmulateBrowseAndImport in cmd/naut

EIP-005 partly verified

The import selects device tags by `--tags` globs, excludes module I/O such as `Local:1:I` by default, and marks `--writable` matches as writable outputs.

where the docs say so · The default module-I/O exclusion and the writable mark are asserted; selection by `--tags` globs is never exercised (no test sets tag patterns), nor that an explicit pattern brings module I/O back in.

EIP-006 verified

The generated tag file gives output role to `--writable` matches and input otherwise, names the UDT in `type:` for struct bindings only, and says in its header that it is generated.

where the docs say so

EIP-007 verified

`--tags-skip` leaves hand-declared tags out of the tag file, and a skip pattern that matches nothing is an error rather than a silent regeneration of a hand-written tag.

where the docs say so

EIP-008 partly verified

`naut eip tags` re-derives just the tag file from the committed manifest with no controller.

where the docs say so · The generator function is tested; the `naut eip tags` subcommand itself (flags, -o, -skip) has no test.

EIP-009 no test yet

At every connect the driver validates the manifest against the live controller (missing tag, changed template type, lost UDT member) and drift fails the connection listing every problem rather than mis-decoding.

where the docs say so · No test drives a drifted controller against a manifest.

EIP-010 verified

UDTs become real ST `TYPE` structs a program can use, with the controller's member names verbatim and nested UDTs emitted dependencies-first so the block compiles top to bottom.

where the docs say so

EIP-011 partly verified

A Logix string template (`DINT LEN` plus `SINT[] DATA`) becomes the type `STRING`, not a struct; array members become `ARRAY[0..n-1]`; `STIME`/`DATE`/`TIME_OF_DAY` map to `DINT` and `DATE_AND_TIME` to `LINT`.

where the docs say so · The STRING template is asserted; array member bounds and the STIME/DATE/TIME_OF_DAY/DATE_AND_TIME mappings are not.

EIP-013 partly verified

Tag names are sanitized into identifiers (`Program:MainProgram.Motor` becomes `MainProgram_Motor`, other punctuation becomes `_`, runs collapse, a leading digit gets a `Tag_` prefix, a collision gets `_2`) while the device path stays in the manifest's `device:`.

where the docs say so · The program-scope case and the `device:` path are asserted; the leading-digit prefix, run collapsing and `_2` collision rule are not.

EIP-014 no test yet

What cannot be imported (multi-dimensional arrays, members with an undecoded type code, templates that could not be uploaded) is reported as `skipped:` lines with a reason and never reaches the manifest.

where the docs say so · The emulator-side skip list is tested (EIP-024); the importer's `skipped:` output has no test.

EIP-015 partly verified

A binding's scan class comes from the manifest's `scanclass`, overridden by `tag-classes:` globs matched against either the nautilus tag name or the device path; unassigned bindings are the `default` class at `scan-rate`.

where the docs say so · A glob matching only the device path (`Program:MainProgram.*` for Motor) and the default class are asserted; a glob matching only the nautilus name is not distinguished (every other binding's name equals its device path), and the manifest `scanclass` key and "later assignments win" are not asserted.

  • pass TestScanClasses in eip

EIP-016 verified

Naming a scan class without defining its rate is a load error.

where the docs say so

  • pass TestScanClassErrors in eip

EIP-017 verified

The reserved class `none` catalogs a tag without polling it: it never appears in the input set or snapshot, and stays a valid write target.

where the docs say so

  • pass TestScanClasses in eip

EIP-018 verified

Each class polls at its own rate, and every class is polled once immediately on connect so a slow class does not delay the first snapshot.

where the docs say so

  • pass TestScanClasses in eip

EIP-019 partly verified

Elementary scalars are batched into Multiple Service Packets sized to the negotiated 500-byte connection, so a class of many scalars costs a handful of round trips.

where the docs say so · Batched reads return correct values, with a missing tag failing alone; the 500-byte sizing and the round-trip count are not asserted.

EIP-020 partly verified

Structs, strings and arrays are read whole with one Read Tag for the root and decoded into the manifest type.

where the docs say so · Read Tag Fragmented fallback for large values is not asserted; strings and arrays through the driver are not read in a test.

  • pass TestReadStructRoot in eip/logix
  • pass TestDriverPollAndWrite in eip

EIP-021 partly verified

When the controller refuses a whole-struct read (privilege violation), that binding drops to leaf mode for the session: members are read individually and the struct is assembled client-side in manifest field order.

where the docs say so · With struct roots refused, both struct bindings drop to member-wise reads (Health().LeafMode = 2) and the struct is assembled in field order. That leaf mode sticks for the session is not asserted: a driver that retried the whole-struct read every poll would report the same.

  • pass TestDriverLeafModeFallback in eip

EIP-022 no test yet

In leaf mode a member the controller never serves is detected by its CIP status, logged once, dropped from the poll set and assembled as the IEC zero; a member failing for any other reason holds the binding's last good value, so a partial struct is never published.

where the docs say so · The emulator only refuses whole-struct roots; per-member never-serve and transient failures are not simulated.

EIP-023 no test yet

`/api/drivers` counts the bindings in leaf mode as `leaf structs`.

where the docs say so · The status mapper test builds health without leaf counts.

EIP-024 no test yet

The eip driver reports no per-tag quality of its own: before the first successful poll every input reads Stale (read error), and after a connection drops the last snapshot keeps being served as Good.

where the docs say so · Neither the pre-first-poll Stale nor the post-drop Good-with-held values is asserted by any test.

EIP-025 no test yet

A dotted path resolves to its root tag, since a UDT arrives whole.

where the docs say so · No test.

EIP-026 partly verified

A `writable` binding is a `role: output` tag; output changes reach the controller, and a scalar goes as one CIP Write Tag with its Logix type code.

where the docs say so · Tests only check the value lands in the emulator store; that a scalar goes as one CIP Write Tag and with its Logix type code is not asserted.

  • pass TestDriverPollAndWrite in eip
  • pass TestRuntimeRoundTrip in eip
  • pass TestLogixEmulateDriverRoundTrip in cmd/naut

EIP-027 partly verified

A struct output is written member by member symbolically (`Device.Member.Sub`), nested structs recursing.

where the docs say so · The member values of a nested struct landing in the emulator are asserted; that they go as per-member symbolic writes (rather than one write of the root) is not observed.

  • pass TestDriverPollAndWrite in eip

EIP-028 no test yet

An unchanged output value never reaches the wire twice.

where the docs say so · No test counts writes on the wire (the Modbus driver has one).

EIP-029 no test yet

Strings and array members inside a struct are not written, and a top-level STRING or array binding is not a writable target.

where the docs say so · Listed under Not supported; nothing asserts the refusal.

EIP-030 partly verified

There is no baseline rule: the first scan's output values, including an output's `init:`, are written to the controller.

where the docs say so · The round trip calls Scan once and waits for Batch_Ack on the emulator, so the first scan's output being written is asserted; that an output's `init:` reaches the controller is not.

  • pass TestLogixEmulateDriverRoundTrip in cmd/naut

EIP-031 no test yet

A write that fails because the connection broke is requeued (latest value per tag) and retried after reconnect; a write the controller refuses on a live connection is logged, dropped for that flush, and goes again when the value changes.

where the docs say so · No test breaks the connection or refuses a write.

EIP-032 no test yet

A write kicks the poll loop awake rather than waiting out the next interval.

where the docs say so · Latency of a write against a slow scan rate is not tested.

EIP-033 no test yet

The driver has no write gate: nothing inside it suppresses a command (a standby replica instead skips scans).

where the docs say so · An absence; the standby-skips-scans behaviour is a runtime property not tested here.

EIP-034 verified

`/api/drivers` reports Kind `ethernet-ip` with state connected, connecting, error (with `lastError`) or degraded (connected while every poll fails).

where the docs say so

EIP-035 no test yet

The status row is named by host with `Detail: "<host> · slot N"`, carries the listed metrics, and reconnects back off from 1s to about 30s, resetting on success.

where the docs say so · The state test does not assert detail, metrics or the backoff schedule.

EIP-036 partly verified

`naut logix emulate` serves a ControlLogix from an L5X export: controller tags become symbols, program tags are served as `Program:<prog>.<tag>`, initial values come from the export, and aliases, multi-dimensional arrays and types without a public shape (`MESSAGE`, `AXIS_*`) are skipped and reported.

where the docs say so · Program tags, export initial values and the alias / MESSAGE / AXIS-typed member skips are asserted; skipping a multi-dimensional array is not.

  • pass TestLogixEmulateBrowseAndImport in cmd/naut
  • pass TestLogixEmulateVarietyValues in cmd/naut

EIP-037 partly verified

`--values` seeds values by client-style path (objects seed members, arrays elements, strings a STRING tag), `--surface` serves a hand-written surface, and bad usage (no source, both sources, bad listen address, unknown seed tag, missing file) exits non-zero.

where the docs say so · Only array seeding is exercised (`Trend`); object-member and STRING seeds are not. `--surface` serving is not exercised, only its usage errors; port `0` and `--name` are not asserted.

  • pass TestLogixEmulateVarietyValues in cmd/naut
  • pass TestLogixEmulateUsage in cmd/naut

EIP-038 partly verified

`--ramp` drifts every numeric leaf, leaves BOOLs alone, and a leaf a client writes stops drifting and keeps the write.

where the docs say so · Only a REAL leaf is asserted to drift; that integer leaves drift is not. The client write is simulated by writing the emulator's tag store directly rather than over CIP.

EIP-039 partly verified

Client writes land in the emulator, so a project's outputs read back on the next poll, and the whole browse, import, poll, write path runs against it.

where the docs say so · Writes landing in the emulator store and the import, poll, write path are asserted; no test reads a written output back through the driver's next poll.

  • pass TestLogixEmulateDriverRoundTrip in cmd/naut
  • pass TestRuntimeRoundTrip in eip

EIP-040 partly verified

The emulator serves Forward_Open, symbolic Read/Write Tag (plain and fragmented), Multiple Service Packet batches, tag-list upload, Template services, and the Identity and Program Name objects, which both nautilus's client and pycomm3 speak.

where the docs say so · Fragmented transfer, the Identity object and pycomm3 compatibility are not tested (no pycomm3 job in CI).

EIP-041 partly verified

The emulator's `DenyStructRoots` switch refuses struct roots the way a real controller refuses an AOI backing tag, which is how leaf mode is tested.

where the docs say so · DenyStructRoots is what puts the driver in leaf mode (LeafMode 2 with it, 0 without it in TestRuntimeRoundTrip). That the refusal is a privilege violation, as a real controller's is, is not asserted: the driver drops to leaf mode on any non-fatal struct read error.

  • pass TestDriverLeafModeFallback in eip
  • pass TestRuntimeRoundTrip in eip

EIP-042 no test yet

`naut test` never opens a socket: whatever `driver:` configures, every acceptance test substitutes the in-memory loopback driver, and `given:` writes struct bindings field by field using the generated `type:`.

where the docs say so · Exercised by example projects (batch-skid) but no test asserts the driver substitution or the field-by-field struct `given:`.

EIP-043 no test yet

Non-Logix CIP devices, implicit Class 1 I/O, PCCC/SLC/PLC-5/Micro800, multi-dimensional arrays, and writing strings or arrays are not supported.

where the docs say so · Absences; the importer skipping multi-dimensional arrays is not tested.

EIP-044 partly verified

`WithScanRate`, `WithScanClass` and `WithTagClass` configure polling from Go, and `ScanClasses()` returns the resolved groups.

where the docs say so · WithScanClass, WithTagClass and ScanClasses are asserted; WithScanRate's value is not observable (the 250ms default would pass the same 5s wait).

  • pass TestScanClasses in eip