diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4785ea0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,116 @@ +# Changelog + +All notable changes to the `mxaccess` workspace are documented here. The +format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); +the workspace as a whole follows [SemVer](https://semver.org/) but the +0.0.x line is pre-release / API-unstable. + +## [Unreleased] — V1 — 2026-05-06 + +V1 is the first publishable cut. Closes M0 → M6 from +`design/60-roadmap.md`. + +### Added + +- **`mxaccess-codec`** — pure protocol codec covering `MxReferenceHandle`, + `NmxTransferEnvelope`, `NmxItemControlMessage`, `NmxWriteMessage` (scalar + + array, normal + timestamped), `NmxSecuredWrite2Message`, + `NmxSubscriptionMessage` (single + multi-record DataUpdate per F44), + `NmxReferenceRegistrationMessage`, `NmxMetadataQueryMessage`, + `NmxOperationStatusMessage`, `ObservedWriteBodyTemplate`, ASB Variant + + `AsbStatus` + `RuntimeValue`, `MxStatus`, `MxValueKind`, `MxDataType`, + `MxValue`. Counting-allocator bench harness in `benches/alloc_count.rs` + (F38) reports 1–4 allocs per write across the proven matrix, well under + the R12 < 5/write target. +- **`mxaccess-rpc`** — DCE/RPC PDU codec, NTLMv2 client + server-direction + packet-integrity verify (F2 with `subtle::ConstantTimeEq`), TCP + transport, OBJREF parser + Win32 `CoMarshalInterface` emitter (F6), + `IObjectExporter::ResolveOxid` + `ResolveOxid2` (F10), + `IRemUnknown::RemQueryInterface` + `RemAddRef`/`RemRelease` (F11). +- **`mxaccess-callback`** — RPC server hosting `INmxSvcCallback` + + `IRemUnknown` for inbound `DataReceived` / `StatusReceived` frames. +- **`mxaccess-nmx`** — `INmxService2` client (`RegisterEngine2`, + `TransferData`, `AddSubscriberEngine`, `SetHeartbeatSendInterval`, + etc.) plus auto-resolving `NmxClient::create` factory (F12, gated by + `windows-com`). +- **`mxaccess-galaxy`** — `tiberius`-backed `Resolver` + `UserResolver` + (F14, gated by `galaxy-resolver`). +- **`mxaccess-asb-nettcp`** — `[MS-NMF]` framing + `[MC-NBFX]` binary-XML + + `[MC-NBFS]` static dictionary + DH/HMAC/AES auth crypto with + constant-time `mod_exp` via `crypto-bigint::DynResidue` (F27). +- **`mxaccess-asb`** — `IASBIDataV2` client (`Connect`, `RegisterItems`, + `Read`, `Write`, `PublishWriteComplete`, `CreateSubscription`, + `AddMonitoredItems`, `Publish`, `DeleteMonitoredItems`, + `DeleteSubscription`, `Disconnect`) with canonical-XML HMAC signing + for all 13 `ConnectedRequest` shapes (F28) and DataContract + field-suffix names on the binary `MonitoredItem` body (F34). +- **`mxaccess`** — async Tokio façade exposing `Session`, `AsbSession`, + `Subscription` (`Stream>`), + `subscribe_buffered` per R2 single-sample-with-cadence-knob + semantics (F36), `recover_connection` reconnect loop (F16), recovery + events (`RecoveryEvent::Started/Recovered/Failed`), and a typed + `Error` taxonomy. Optional `metrics` feature emits per-op counters, + latency histograms, and connection-state gauges (F40). +- **`mxaccess-compat`** — `LMXProxyServer`-shaped Rust facade exposing + the 18-method `ILMXProxyServer5` surface as async fns over + `mxaccess::Session` / `AsbSession` with a `Mutex>` handle table and `Stream`-based event channels (F35). +- **Examples** — `connect-write-read.rs`, `subscribe.rs`, + `subscribe-buffered.rs`, `asb-subscribe.rs`, `multi-tag.rs`, + `recovery.rs`, `secured-write.rs`, plus diagnostic + `asb-relay.rs`. Live-probe DoD verified end-to-end against the + AVEVA install. +- **Tooling** — `cargo public-api` baselines under + `design/public-api/{crate}.txt` with CI drift check (F41). + `design/M6-bench-baseline.md` records the alloc-count baseline. + +### Changed (vs the .NET reference) + +- `NmxSubscriptionMessage::parse_data_update` accepts `record_count >= 1`; + the .NET reference hard-throws on `record_count != 1`. F44 evidence + walk against `captures/094-frida-buffered-separate-writer/` + documents the multi-record observation that drove the divergence. +- `subscribe_buffered` returns a `Stream` + (single-sample-per-event); per R2 verification the cadence is a + server-side delivery rate knob, not a multi-sample payload. + +### Known limitations + +- **F3** — cross-domain NTLM Type1/2/3 fixture is permanently + out-of-scope on the dev host (single-domain). Single-domain wire + parity is verified; cross-domain is documented but not regression- + tested. +- **F45** — recovery replay for buffered subscriptions falls through + to plain `AdviseSupervisory`, losing the `.property(buffer)` + registration. Filed as a follow-up. +- **F46** — `LmxProxy.dll!CLMXProxyServer.Suspend`/`.Activate` wire + emission was not instrumented; the compatibility-server's + client-side gating is documented but the underlying ORPC call + shape is unconfirmed. +- **R3 / R4** — `OperationComplete` trigger conditions and + completion-only byte mappings are unmapped in both the .NET + reference and the Rust port. Frame bytes are preserved verbatim + via `Session::operation_status_events()`. + +## Publish order + +Workspace crates form a dependency DAG; `cargo publish` requires +already-published deps to exist on crates.io, so the order matters. +For V1 cut: + +1. `mxaccess-codec` (no internal deps) +2. `mxaccess-rpc` (no internal deps) +3. `mxaccess-asb-nettcp` (no internal deps) +4. `mxaccess-galaxy` (depends on codec) +5. `mxaccess-callback` (depends on rpc + codec) +6. `mxaccess-asb` (depends on codec + asb-nettcp) +7. `mxaccess-nmx` (depends on codec + galaxy + rpc + callback) +8. `mxaccess` (depends on all the above) +9. `mxaccess-compat` (depends on mxaccess) + +`cargo publish --dry-run` validates each crate's metadata + tarball +in isolation; the dependent crates' dry-runs require the leaf crates +to actually exist on crates.io (the registry lookup happens regardless +of `--no-verify`). For pre-publish verification: leaf crates dry-run +in CI; dependent crates are validated by the public-api baseline + +build-test-clippy matrix.