Files
Joseph Doherty 7e4d713eb3 Cross-platform NegotiateAuthentication; StorageType field; docs polish
HistorianSspiClient rewritten on top of System.Net.Security.NegotiateAuthentication
in place of P/Invoke into secur32.dll's InitializeSecurityContextW. The class
keeps the same Next() / Dispose() / two-constructor surface so callers don't
change. RequiredProtectionLevel=EncryptAndSign + AllowedImpersonationLevel=
Identification produces a request-flag set equivalent to the captured native
0x2081C / 0x81C bitmasks (still preserved as constants for the existing unit
tests). Removes the only Windows P/Invoke in the production SDK; the
[SupportedOSPlatform("windows")] gating elsewhere stays in place pending a
separate sweep.

HistorianStorageType (Cyclic = 1, Delta = 2):
Captured 2026-05-04 via --write-storage-type on the harness. Delta differs
from Cyclic in three places — header byte 10 (0x02 -> 0x06), flag-block
byte 1 (0x01 -> 0x02), and 4 zero bytes inserted after StorageRate before
the FILETIME. Server persists Tag.StorageType=1/2 accordingly. Plumbed
through HistorianTagDefinition.StorageType + serializer + orchestrator + 2
new tests (golden bytes + live SQL persistence verification).

Docs polish:
CLAUDE.md no longer claims "no P/Invoke" (HistorianSspiClient is the one
allowed P/Invoke surface); updated test count to 169+; AGENTS.md Required
SDK Surface and Repository Layout brought up to date with the live state
including the write surface; handoff.md "not a git working tree" obsolete
note removed.

171/171 tests pass with the NegotiateAuthentication replacement (was 169;
+2 new tests for StorageType).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:19:37 -04:00

8.0 KiB

AGENTS.md

Mission

Build a fully managed .NET 10 replacement for AVEVA Historian's aahClientManaged / aahClient.dll stack by reverse-engineering the native binary Historian protocol.

The target is an in-process managed SDK that can replace the current .NET Framework/native sidecar used by OtOpcUa.Driver.Historian.Wonderware.

Chosen Approach

Use the reverse-engineering path from instructions.md:

  1. Decompile current\aahClientManaged.dll to understand the managed wrapper surface, connection flow, query types, data models, error handling, and native calls.
  2. Inspect current\aahClient.dll and the matching AVEVA install DLLs to map the native ABI and identify transport/framing responsibilities.
  3. Capture traffic from real Historian sessions and derive the on-the-wire protocol for the required read-only operations.
  4. Implement the protocol in a pure managed .NET 10 client.

Do not pursue the REST API implementation unless explicitly asked. Do not build a P/Invoke shim as the primary solution; it is useful only as an analysis aid.

Repository Layout

This workspace is a full Git repo (origin: gitea.dohertylan.com) with the shipping SDK under src/, tests under tests/, RE tooling under tools/, and decoded protocol notes under docs/. See CLAUDE.md for the authoritative architecture overview.

  • instructions.md - source planning document and decision record.
  • src\AVEVA.Historian.Client\ - the production managed SDK (pure .NET 10, no native AVEVA references).
  • tests\AVEVA.Historian.Client.Tests\ - unit + gated live integration tests.
  • tools\ - reverse-engineering tooling (CLI, native trace harness, WCF capture server, IL-rewrite instrumentation helper).
  • docs\reverse-engineering\ - sanitized RE evidence and decoded notes.
  • current\ - the seven DLLs the existing sidecar links against today.
  • aveva-install-x64\ and aveva-install-x86\ - full AVEVA Historian client-side DLL sets for cross-version reference.

Use current\ first because it represents the deployed sidecar dependency set. Use aveva-install-* to compare architecture-specific behavior and locate adjacent client APIs.

Required SDK Surface

The shipping public surface (all live-verified against localhost — see CLAUDE.md "Required SDK Surface" for the authoritative list and caveats):

Reads:

  • ProbeAsync
  • ReadRawAsync
  • ReadAggregateAsync
  • ReadAtTimeAsync
  • ReadEventsAsync
  • BrowseTagNamesAsync
  • GetTagMetadataAsync
  • Status helpers: GetConnectionStatusAsync, GetStoreForwardStatusAsync, GetSystemParameterAsync

Writes (added 2026-05-04 by explicit request):

  • EnsureTagAsync for analog Float / Double / Int2 / Int4 / UInt4 (with optional ApplyScaling=true for distinct MinRaw/MaxRaw and optional StorageRateMs for non-default sampling).
  • DeleteTagAsync.

AddS2 (write samples) is architecturally blocked — the server's runtime cache only ingests from configured IOServer / Application Server pipelines. Do not extend write support without an explicit new request.

Reverse-Engineering Workflow

The bulk of the original RE workflow has been executed and is now backed by docs/reverse-engineering/ evidence. The notes below are the durable process in case new captures are needed (e.g., for a new Historian version or a new write op).

1. Managed Wrapper Analysis

Use dnSpy / ILSpy / the in-repo dnlib-method CLI on current\aahClientManaged.dll.

Document:

  • Public types and methods used for connections and queries.
  • P/Invoke or native interop entry points.
  • Constructor arguments, enum values, flags, and default values.
  • Query argument structures for raw, aggregate, at-time, and event reads.
  • Returned sample/event models, quality fields, timestamp handling, and error propagation.

Sanitized notes go under docs\reverse-engineering\ (the folder exists and is the canonical home for committed RE evidence).

2. Native ABI Mapping

Inspect current\aahClient.dll and compare with:

  • aveva-install-x64\aahClient.dll
  • aveva-install-x86\aahClient.dll
  • aahClientCommon.dll
  • aahDataSetClient.dll
  • aahClientConfig.dll

Useful tools:

  • dumpbin /exports
  • Dependencies or Process Monitor
  • API Monitor
  • Detours or equivalent call hooks

Document function names, calling conventions, pointer ownership, HRESULT/error patterns, string encodings, and architecture differences.

3. Wire Capture

Capture real Historian sessions with Wireshark while running the existing Wonderware sidecar/client against a development Historian.

Capture each scenario independently:

  • Connection open/close and health probe.
  • Raw history query.
  • Aggregate query for each required retrieval mode.
  • At-time/interpolated query.
  • Event query.
  • Error cases: bad tag, empty range, invalid credentials, server offline.

For every capture, record:

  • Historian version and architecture.
  • Client DLL version and file hash.
  • Server host/port.
  • Query parameters.
  • Expected logical result set.
  • Packet capture filename.

Do not commit sensitive packet captures, credentials, server names, or customer tag names. Sanitize before adding any fixtures or notes.

4. Protocol Model

Derive and document:

  • Session handshake.
  • Authentication exchange, if present.
  • Message framing and length prefixes.
  • Message type identifiers.
  • Request/response correlation.
  • Endianness.
  • Timestamp encoding.
  • String encoding.
  • Numeric value encoding.
  • Quality/status encoding.
  • Error frame format.
  • Event payload format.

Add version notes whenever behavior differs between the installed 2020 DLLs and newer Historian versions.

5. Managed Implementation Shape

The implementation has landed and is the authoritative reference. See CLAUDE.md "Code Architecture" for the actual layout. The original abstract shape is preserved as historical context only.

Key design rule still in force: keep protocol parsing isolated from transport I/O so captured frames can be tested without a live Historian.

Testing Expectations

Start with deterministic tests around protocol encoding/decoding:

  • Golden byte fixtures for each message kind.
  • Round-trip tests for request builders.
  • Parser tests for captured and sanitized response frames.
  • Timestamp, quality, and string encoding edge cases.
  • Error frame parsing.

Add live integration tests only behind explicit configuration, such as:

  • HISTORIAN_HOST
  • HISTORIAN_PORT
  • HISTORIAN_USER
  • HISTORIAN_PASSWORD
  • HISTORIAN_TEST_TAG

Integration tests must skip cleanly when these values are not configured.

Constraints

  • Keep the final SDK managed .NET 10. The single P/Invoke surface allowed in production is HistorianSspiClient (Windows SSPI for integrated auth); do not add unrelated P/Invokes.
  • Avoid adding native runtime dependencies to the production SDK. No reference to aahClientManaged.dll / aahClient.dll from src/.
  • Avoid broad API design. Implement only the operations listed in "Required SDK Surface".
  • Treat AVEVA protocol details as version-sensitive; document assumptions in docs/reverse-engineering/.
  • Do not redistribute AVEVA binaries.
  • Do not commit credentials, proprietary captures, or customer data.
  • Do not delete or overwrite DLLs in current\ or aveva-install-*.

Definition of Done

Both the RE phase and the SDK phase are met as of 2026-05-04:

  • Managed wrapper public surface and native entry points are documented in docs/reverse-engineering/.
  • Required query flows have sanitized captures + byte-level notes; golden fixtures live under fixtures/protocol/.
  • Message framing, request/response/error layouts are decoded sufficiently for round-trip parser tests.
  • The shipping SDK implements the Required SDK Surface (reads + writes).
  • 169 unit + live integration tests pass.
  • Local consumers can replace the sidecar without aahClientManaged.dll or aahClient.dll at runtime.

Future RE work (e.g., new Historian version, additional write ops) should follow the same workflow above; new evidence updates docs/reverse-engineering/ and the relevant plan file under docs/plans/.