- Rename 16 kebab-case docs to PascalCase per StyleGuide - Move per-language client design docs from docs/ to clients/<lang>/ alongside their READMEs - Add ## Related Documentation sections to 15 docs that lacked one - Fix sentence-case violations in H3 headings (StyleGuide rule) - Update cross-references in gateway.md, client READMEs, scripts, and generate-proto.ps1 helpers to follow the new paths - Add CLAUDE.md with build/test commands, the source-update verification matrix, the parity-first contract, and pointers to MXAccess and Galaxy Repository analysis sources Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
mxaccessgw is the MXAccess Gateway: a gRPC service that gives modern (.NET, Go, Rust, Python, Java) clients full MXAccess parity without forcing them to load 32-bit MXAccess COM, run x86, or own an STA message pump.
The architecture is a two-process design — read gateway.md before making structural changes:
- Gateway (
src/MxGateway.Server, .NET 10, x64): ASP.NET Core gRPC server. Owns the public API, sessions, auth, the Blazor dashboard, and the Galaxy Repository SQL browse RPCs. Never instantiates MXAccess COM directly. - Worker (
src/MxGateway.Worker, .NET Framework 4.8, x86): one process per session. Owns one MXAccess COM instance on a dedicated STA, pumps Windows messages, and converts COM events to protobuf. - IPC: gateway↔worker uses one bidirectional named pipe per worker (
mxaccess-gateway-{gatewayPid}-{sessionId}) with length-prefixedWorkerEnvelopeprotobuf frames. Gateway hosts the pipe server and launches the worker. gRPC is not used inside the worker — .NET Framework 4.8 doesn't have a first-class gRPC stack. - Contracts (
src/MxGateway.Contracts): multi-targetsnet10.0;net48and owns the.protofiles (mxaccess_gateway.proto,mxaccess_worker.proto,galaxy_repository.proto). All other projects consume the generated types from here. Do not hand-edit anything underGenerated/.
The worker must do all MXAccess COM calls on its dedicated STA thread, and the STA loop must pump Windows messages (MsgWaitForMultipleObjectsEx + PeekMessage/DispatchMessage) so MXAccess events deliver. A plain blocking queue on an STA is not enough.
Build, Test, Run
# Full solution build (gateway, worker, contracts, tests)
dotnet build src/MxGateway.sln
# Worker must be built x86 — the gateway looks for MxGateway.Worker.exe under bin\x86
dotnet build src/MxGateway.Worker/MxGateway.Worker.csproj -p:Platform=x86
# Gateway tests (no MXAccess required — uses FakeWorkerHarness)
dotnet test src/MxGateway.Tests/MxGateway.Tests.csproj
dotnet test src/MxGateway.Worker.Tests/MxGateway.Worker.Tests.csproj -p:Platform=x86
# Run gateway locally (defaults bound under MxGateway:* in src/MxGateway.Server/appsettings.json)
dotnet run --project src/MxGateway.Server/MxGateway.Server.csproj
# API-key admin CLI (same exe, "apikey" subcommand)
dotnet run --project src/MxGateway.Server/MxGateway.Server.csproj -- apikey create --display-name "dev" --scopes session,invoke,event,metadata,admin
Single test by name (xUnit --filter):
dotnet test src/MxGateway.Tests/MxGateway.Tests.csproj --filter FullyQualifiedName~GatewayEndToEndFakeWorkerSmokeTests
Live MXAccess integration tests are opt-in because they need installed MXAccess COM and live provider state:
$env:MXGATEWAY_RUN_LIVE_MXACCESS_TESTS = "1"
dotnet test src/MxGateway.IntegrationTests/MxGateway.IntegrationTests.csproj --filter FullyQualifiedName~WorkerLiveMxAccessSmokeTests
Live LDAP tests use MXGATEWAY_RUN_LIVE_LDAP_TESTS=1. See docs/GatewayTesting.md for the full opt-in matrix and LiveMxAccessFactAttribute / LiveLdapFactAttribute for the gating logic.
Clients
Each language client is in clients/<lang>/ with its own README. They all consume the shared .proto files in src/MxGateway.Contracts/Protos:
clients/dotnet:dotnet build clients/dotnet/MxGateway.Client.slnclients/python:python -m pip install -e ".[dev]"; python -m pytestclients/rust:cargo test --workspace; cargo clippy --workspace --all-targets -- -D warningsclients/java:gradle test(Java 21)- Go client lives alongside as
mxgw-goin the cross-language matrix
End-to-end matrix runner (needs running gateway + worker + valid API key):
$env:MXGATEWAY_API_KEY = "<api-key>"
powershell -ExecutionPolicy Bypass -File scripts/run-client-e2e-tests.ps1
Repository-Specific Conventions
- Build properties (
src/Directory.Build.props) enforceNullable=enable,TreatWarningsAsErrors=true, latest analyzers, andEnforceCodeStyleInBuild=true. New warnings break the build — fix them, don't suppress unless the suppression has a narrow reason. - Style guides in
docs/style-guides/are authoritative. FollowCSharpStyleGuide.mdfor gateway/worker/.NET-client code: file-scoped namespaces,sealedby default,Asyncsuffix on Task-returning methods, MXAccess-aligned names (MxStatusProxy,ServerHandle,ItemHandle,HResult). - MXAccess parity is the contract. Don't "fix" surprising MXAccess behavior (e.g.,
WriteSecuredfailing before a value-bearing NMX body, distinctOperationCompletesemantics, invalid-handle exceptions) unless the client explicitly opts into a non-parity mode. The installed MXAccess COM component is the baseline. - Don't synthesize events. The gateway forwards only events the worker emits; it never invents
OperationCompletefrom write completion or command replies. - One worker per session, one event subscriber per session (v1). Multi-subscriber fan-out and reconnectable sessions are explicitly out of scope — see
docs/DesignDecisions.md. - Gateway restart does not reattach orphan workers. The first version terminates orphaned workers on startup; do not design code paths that assume reattachment.
- No Blazor UI component libraries. Dashboard uses local Bootstrap CSS/JS only — do not introduce MudBlazor, Radzen, FluentUI, etc.
- Don't log secrets or full tag values by default. API keys, passwords,
WriteSecuredpayloads, andAuthenticateUsercredentials must never reach logs. Value logging is opt-in and redacted. - Generated code under
src/MxGateway.Contracts/Generated/,clients/*/generated*/,clients/python/src/mxgateway/generated/, etc., is build output. Don't hand-edit. To regenerate, build the contracts project (dotnet build src/MxGateway.Contracts/MxGateway.Contracts.csproj) or run the per-client generation step in that client's README. - Documentation style (
StyleGuide.md): PascalCase filenames, no marketing language, present tense, explain why not what. - Update docs in the same change as the source. When public APIs, contracts, configuration, build steps, security behavior, event shapes, value conversion, status mapping, or lifecycle rules change, the affected docs (
gateway.md,docs/, client READMEs, design docs) must change in the same commit. Don't leave stale prose describing old behavior.
Source Update Workflow
When source code changes, build and test the affected component before reporting work done. If the change crosses component boundaries, build each affected component — don't rely on a single top-level build:
| Changed area | Required verification |
|---|---|
Contracts or .proto files |
regenerate generated code, then build gateway, worker, and every generated client touched by the contract |
| Gateway server, sessions, workers, gRPC, dashboard, metrics | dotnet build src/MxGateway.Server and run affected gateway / fake-worker tests |
| Worker IPC, STA, MXAccess, conversion | dotnet build src/MxGateway.Worker -p:Platform=x86 and run worker tests |
| .NET client | dotnet build clients/dotnet/MxGateway.Client.sln and run its tests |
| Go client | gofmt, go build ./..., go test ./... from clients/go |
| Rust client | cargo fmt, cargo check --workspace, cargo test --workspace, cargo clippy --all-targets -- -D warnings from clients/rust |
| Python client | python -m pytest from clients/python |
| Java client | gradle test from clients/java |
| Integration tests | run only when MXAccess COM, provider state, and external services are available; otherwise document why skipped |
Design Sources To Consult Before Non-Trivial Changes
gateway.md— top-level architecture, command/event surface, IPC envelope, STA thread model, fault handling.glauth.md— local LDAP server (GLAuth onlocalhost:3893, base DNdc=lmxopcua,dc=local) used for dev authn. Pre-provisioned users (admin/admin123,readonly/readonly123, etc.) and the role→capability mapping live there.docs/DesignDecisions.md— v1 choices (MXAccess COM targetLMXProxyServerClassfromC:\Program Files (x86)\ArchestrA\Framework\Bin\ArchestrA.MXAccess.dll, API-key-in-SQLite auth, fail-fast event backpressure, etc.).docs/GatewayProcessDesign.md,docs/MxAccessWorkerInstanceDesign.md,docs/WorkerFrameProtocol.md,docs/WorkerProcessLauncher.md— detailed component designs.docs/GatewayConfiguration.md— fullMxGateway:*options bound byGatewayOptionsand validated at startup byGatewayOptionsValidator.docs/GatewayTesting.md— fake worker harness, live MXAccess smoke, parity matrix, cross-language smoke matrix.docs/ToolchainLinks.md— installed compiler/SDK paths on this dev box (.NET 10.0.201, Go 1.26.2, Rust 1.95, Python 3.12.10, Temurin 21, protoc 34.1, etc.).
External analysis sources referenced by design docs:
C:\Users\dohertj2\Desktop\mxaccess— MXAccess analysis project. Key files:docs/MXAccess-Public-API.md(COM class, ProgID, CLSID, method list, event signatures,MxDataType,MxStatus,MXSTATUS_PROXY),docs/MXAccess-Reverse-Engineering.md(installed runtime path, x86 COM constraints),docs/Current-Sprint-State.md(parity gaps),src/MxTraceHarness/(x86 harness using the real COM interop),captures/andanalysis/(observed native behavior).C:\Users\dohertj2\Desktop\lmxopcua\gr— Galaxy Repository (ZBSQL DB) notes. Key files:connectioninfo.md,layout.md,schema.md,queries/hierarchy.sql,queries/attributes.sql,queries/attributes_extended.sql,queries/change_detection.sql. Connection is SQL Serverlocalhost, databaseZB, Windows Auth.
Authentication
Gateway gRPC clients authenticate with an API key in metadata: authorization: Bearer mxgw_<key-id>_<secret>. Keys are stored hashed (with a peppered SHA) in a gateway-owned SQLite DB (default C:\ProgramData\MxGateway\gateway-auth.db). Scopes (session, invoke, event, metadata, admin) gate specific RPCs; missing → Unauthenticated, insufficient → PermissionDenied. The apikey subcommand on the server exe manages keys; see src/MxGateway.Server/Security/Authentication/.
Dashboard auth uses the same verifier but exchanges the API key for an HTTP-only secure cookie at /dashboard/login. Dashboard:AllowAnonymousLocalhost bypasses cookie auth on loopback when explicitly enabled.
Process / Platform Notes
- Working tree is on Windows (
C:\Users\dohertj2\Desktop\mxaccessgw). PowerShell is the native shell for tooling commands; bash is fine for git/grep/find. - The worker reference to
ArchestrA.MXAccess.dlluses an absoluteHintPathtoC:\Program Files (x86)\ArchestrA\Framework\Bin\ArchestrA.MXAccess.dll. The worker only builds where MXAccess is installed (this dev box). - The repo is not a git repository at the top level — there's no
.gitdirectory in the working tree.