Files
mxaccess/design
Joseph Doherty 34d477819b
rust / build / test / clippy / fmt (push) Has been cancelled
[F28] mxaccess-asb: canonical XML signing for all 8 remaining ops
Closes F28. The 5 [XmlSerializerFormat] ops landed in commit f14580e
(2026-05-05); this commit closes out the remaining 8 ConnectedRequest
shapes, eliminating the legacy NBFX-bytes signing fallback from every
`client::*` op.

Two deliverables:

1. Extended `MxAsbClient.Probe --dump-signed-xml` (.NET probe) to
   emit deterministic canonical-XML output for ReadRequest,
   WriteBasicRequest, PublishWriteCompleteRequest,
   CreateSubscriptionRequest, DeleteSubscriptionRequest,
   AddMonitoredItemsRequest, DeleteMonitoredItemsRequest,
   PublishRequest. Saved 8 fixtures at
   rust/crates/mxaccess-asb/tests/fixtures/signed-xml/*.xml. Pinned
   field values for reproducibility:
     - SubscriptionId = 0x1234_5678_9abc_def0
     - MaxQueueSize = 100, SampleInterval = 1000
     - WriteHandle = 0xDEAD_BEEF
     - WriteValue = Variant.FromInt32(42)
     - MonitoredItem with the existing sample-item shape

2. Ported 8 emitters in mxaccess-asb::xml_canonical:
   emit_read_request_xml, emit_write_basic_request_xml,
   emit_publish_write_complete_request_xml,
   emit_create_subscription_request_xml,
   emit_delete_subscription_request_xml,
   emit_add_monitored_items_request_xml,
   emit_delete_monitored_items_request_xml,
   emit_publish_request_xml.

   New helpers consolidate XmlSerializer's per-namespace shapes:
     - emit_invensys_text — primitive int/long fields in the parent
       urn:invensys.schemas namespace (no xmlns redeclaration).
     - emit_write_value — <Values> wrapper inlining
       Value (Variant), Status (default AsbStatus), Comment (xsi:nil).
     - emit_monitored_item — <Items> wrapper inlining
       Item, SampleInterval, ValueDeadband, UserData, Buffered.
     - emit_inline_item_identity — ItemIdentity rendered as a child
       of MonitoredItem (single xmlns redeclaration on the wrapper,
       children inherit).
     - emit_inline_text + emit_inline_optional_string —
       no-redeclaration variants of emit_iom_text +
       emit_iom_optional_string.
     - emit_idata_variant — Variant's Type/Length/Payload children
       in the http://asb.contracts.idata.data/20111111 namespace
       (Payload self-closes with xsi:nil when Length=0).
     - emit_iom_default_variant — wrapper for ValueDeadband / UserData
       (default-shape Variant in iom:2 namespace).

   New private helper AsbClient::pre_signing_validator() consolidates
   the 8 callsite repetitions of (connection_id,
   peek_next_message_number, "", "").

Wired into client::* — every send_signed_envelope[_one_way] call now
passes Some(&xml) for xml_for_signing. The 8 ops affected: read,
write, publish_write_complete, delete_monitored_items,
create_subscription, add_monitored_items, publish,
delete_subscription (plus their _once retry-loop variants).

8 new fixture-comparison tests (mxaccess-asb 87 → 95). Each emitter
byte-equal vs the .NET fixture on the first try — no iteration
needed. Workspace clippy clean.

Live verification: `cargo run -p mxaccess --example asb-subscribe`
returns TestChildObject.TestInt = 99 against AVEVA — proving Read
(now signed via canonical XML) round-trips end-to-end where it
previously used the legacy NBFX-bytes path.

The remaining 7 ops are wire-tested at fixture-byte-equality only;
live exercise is gated on the F33 follow-on capture for
subscribe-flow ops, but the canonical XML matches the .NET reference
byte-for-byte, so the HMAC will match by construction once the
session is in a state to issue those ops.

design/followups.md:
  - F28 moved to Resolved with the full two-step audit trail.
  - F18 M5 status block rewritten — all sub-followups (F26 stream,
    F28, F29, F32, F33) now closed. M5 DoD bullets 1+2+3+4 all green.
  - tests/fixtures/signed-xml/README.md updated to list the 8 new
    fixtures + their pinned input values.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 02:13:16 -04:00
..

design/ — Rust port architectural plan

This folder is the design contract for the Rust replacement of AVEVA/Wonderware MXAccess. It is the gap between the .NET reference in src/ and the Rust crates that will be written under a sibling rust/ workspace (per CLAUDE.md).

The folder is structured as a small set of focused documents. Read in order; each builds on the previous.

File Purpose
00-overview.md Mission, two-layer goal, architectural principles, non-goals
10-raw-layer.md Byte-accurate raw MXAccess layer (codec + transport + session)
20-async-layer.md Idiomatic Tokio async layer on top of the raw layer
30-crate-topology.md Cargo workspace, crates, dependencies, build/test commands
40-protocol-invariants.md Bill of materials: IIDs, opnums, envelope/handle bytes
50-error-model.md MxStatus, error types, panic/cancellation policy
60-roadmap.md Milestones M0..M6, validation strategy
70-risks-and-open-questions.md Parity gaps, unproven flows, cross-platform constraints
dependencies.md Cross- and within-milestone parallelism map; agent budget per phase
review.md Adversarial review log (BLOCKER/MAJOR/MINOR/NIT findings, all resolved)
prompt.md /loop driver prompt for autonomous M2M6 execution
followups.md Open / resolved deferred work items; auto-triaged by prompt.md Step 0 (created on first /loop run if missing)

The design is grounded in the .NET reference at src/ and the protocol artifacts in docs/, analysis/, and captures/. Do not introduce protocol behavior in these documents that is not already proven in the reference. When adding a new claim about wire format, cite either:

  • a .cs file path in src/MxNativeCodec/, src/MxNativeClient/, or src/MxAsbClient/, or
  • a docs/*.md spec file, or
  • a captures/0NN-frida-* directory or analysis/frida/*.tsv row.

This folder is documentation, not code. When the Rust workspace is created, the design here is the contract it must satisfy. When evidence in captures/ invalidates a design decision here, update the design first, then the code.

Reading order

  • New contributor: 00 → 30 → 10 → 40 → 20 → 50 → 60 → 70.
  • Protocol question: 40 first, then the relevant section of 10.
  • API question: 20 first, then 50.
  • Planning a milestone: 60 first, cross-reference 70 for blockers.
  • Scheduling concurrent work: dependencies.md for the per-phase parallelism map.
  • Driving M2M6 autonomously via /loop: prompt.md (and the followups.md triage log it maintains).