diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..3f37e33 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,125 @@ +# 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-prefixed `WorkerEnvelope` protobuf 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-targets `net10.0;net48` and owns the `.proto` files (`mxaccess_gateway.proto`, `mxaccess_worker.proto`, `galaxy_repository.proto`). All other projects consume the generated types from here. Do not hand-edit anything under `Generated/`. + +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 + +```powershell +# 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`): + +```powershell +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: + +```powershell +$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//` with its own README. They all consume the shared `.proto` files in `src/MxGateway.Contracts/Protos`: + +- `clients/dotnet`: `dotnet build clients/dotnet/MxGateway.Client.sln` +- `clients/python`: `python -m pip install -e ".[dev]"; python -m pytest` +- `clients/rust`: `cargo test --workspace; cargo clippy --workspace --all-targets -- -D warnings` +- `clients/java`: `gradle test` (Java 21) +- Go client lives alongside as `mxgw-go` in the cross-language matrix + +End-to-end matrix runner (needs running gateway + worker + valid API key): + +```powershell +$env:MXGATEWAY_API_KEY = "" +powershell -ExecutionPolicy Bypass -File scripts/run-client-e2e-tests.ps1 +``` + +## Repository-Specific Conventions + +- **Build properties** (`src/Directory.Build.props`) enforce `Nullable=enable`, `TreatWarningsAsErrors=true`, latest analyzers, and `EnforceCodeStyleInBuild=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. Follow `CSharpStyleGuide.md` for gateway/worker/.NET-client code: file-scoped namespaces, `sealed` by default, `Async` suffix on Task-returning methods, MXAccess-aligned names (`MxStatusProxy`, `ServerHandle`, `ItemHandle`, `HResult`). +- **MXAccess parity is the contract.** Don't "fix" surprising MXAccess behavior (e.g., `WriteSecured` failing before a value-bearing NMX body, distinct `OperationComplete` semantics, 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 `OperationComplete` from 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, `WriteSecured` payloads, and `AuthenticateUser` credentials 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 on `localhost:3893`, base DN `dc=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 target `LMXProxyServerClass` from `C:\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` — full `MxGateway:*` options bound by `GatewayOptions` and validated at startup by `GatewayOptionsValidator`. +- `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/` and `analysis/` (observed native behavior). +- `C:\Users\dohertj2\Desktop\lmxopcua\gr` — Galaxy Repository (`ZB` SQL 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 Server `localhost`, database `ZB`, Windows Auth. + +## Authentication + +Gateway gRPC clients authenticate with an API key in metadata: `authorization: Bearer mxgw__`. 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.dll` uses an absolute `HintPath` to `C:\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 `.git` directory in the working tree. diff --git a/docs/clients-dotnet-csharp-design.md b/clients/dotnet/DotnetClientDesign.md similarity index 93% rename from docs/clients-dotnet-csharp-design.md rename to clients/dotnet/DotnetClientDesign.md index 6d5389a..dd16eb7 100644 --- a/docs/clients-dotnet-csharp-design.md +++ b/clients/dotnet/DotnetClientDesign.md @@ -6,8 +6,8 @@ Provide an idiomatic .NET 10 C# client library for MXAccess Gateway, plus a test CLI and unit tests. This client is for modern .NET callers and must not load MXAccess COM. -Follow the [C# Style Guide](./style-guides/CSharpStyleGuide.md) for -handwritten code and the [Protobuf Style Guide](./style-guides/ProtobufStyleGuide.md) +Follow the [C# Style Guide](../../docs/style-guides/CSharpStyleGuide.md) for +handwritten code and the [Protobuf Style Guide](../../docs/style-guides/ProtobufStyleGuide.md) for generated contract inputs. ## Projects @@ -211,3 +211,10 @@ MXGATEWAY_TEST_ITEM= Integration smoke should open, register, add, advise, stream for bounded time, and close. + +## Related Documentation + +- [Client Libraries Detailed Design](../../docs/ClientLibrariesDesign.md) +- [Client Proto Generation](../../docs/ClientProtoGeneration.md) +- [Client Packaging](../../docs/ClientPackaging.md) +- [C# Style Guide](../../docs/style-guides/CSharpStyleGuide.md) diff --git a/clients/dotnet/README.md b/clients/dotnet/README.md index 60f4915..98ff31e 100644 --- a/clients/dotnet/README.md +++ b/clients/dotnet/README.md @@ -164,6 +164,19 @@ foreach (GalaxyObject galaxyObject in objects) } ``` +Use `DiscoverHierarchyOptions` to request a server-side slice without pulling +the full Galaxy: + +```csharp +IReadOnlyList pumps = await repository.DiscoverHierarchyAsync( + new DiscoverHierarchyOptions + { + RootContainedPath = "Area1/Line3", + TagNameGlob = "Pump_*", + IncludeAttributes = false, + }); +``` + The CLI exposes the same operations: ```powershell @@ -230,5 +243,5 @@ dotnet run --project clients/dotnet/MxGateway.Client.Cli -- smoke --endpoint $en ## Related Documentation - [Client Packaging](../../docs/ClientPackaging.md) -- [Client Proto Generation](../../docs/client-proto-generation.md) -- [.NET Client Detailed Design](../../docs/clients-dotnet-csharp-design.md) +- [Client Proto Generation](../../docs/ClientProtoGeneration.md) +- [.NET Client Detailed Design](./DotnetClientDesign.md) diff --git a/docs/clients-golang-design.md b/clients/go/GoClientDesign.md similarity index 91% rename from docs/clients-golang-design.md rename to clients/go/GoClientDesign.md index fc1b589..f7d2682 100644 --- a/docs/clients-golang-design.md +++ b/clients/go/GoClientDesign.md @@ -6,8 +6,8 @@ Provide an idiomatic Go client module for MXAccess Gateway, plus a test CLI and unit tests. The Go client should be suitable for services and command-line automation. -Follow the [Go Style Guide](./style-guides/GoStyleGuide.md) for handwritten -code and the [Protobuf Style Guide](./style-guides/ProtobufStyleGuide.md) for +Follow the [Go Style Guide](../../docs/style-guides/GoStyleGuide.md) for handwritten +code and the [Protobuf Style Guide](../../docs/style-guides/ProtobufStyleGuide.md) for generated contract inputs. ## Module Layout @@ -176,3 +176,10 @@ MXGATEWAY_INTEGRATION=1 Integration test should run `OpenSession`, `Register`, `AddItem`, `Advise`, bounded `StreamEvents`, and `CloseSession`. + +## Related Documentation + +- [Client Libraries Detailed Design](../../docs/ClientLibrariesDesign.md) +- [Client Proto Generation](../../docs/ClientProtoGeneration.md) +- [Client Packaging](../../docs/ClientPackaging.md) +- [Go Style Guide](../../docs/style-guides/GoStyleGuide.md) diff --git a/clients/go/README.md b/clients/go/README.md index 71cb1ac..1516e45 100644 --- a/clients/go/README.md +++ b/clients/go/README.md @@ -3,7 +3,7 @@ The Go client module contains the generated MXAccess Gateway protobuf bindings, a small handwritten `mxgateway` package, and the `mxgw-go` test CLI scaffold. The module uses the shared proto inputs documented in -`../../docs/client-proto-generation.md` so gateway and client contracts stay in +`../../docs/ClientProtoGeneration.md` so gateway and client contracts stay in sync. ## Layout @@ -28,7 +28,7 @@ Run generation after the shared `.proto` files or the Go output path changes: ./generate-proto.ps1 ``` -The script uses the tool paths recorded in `../../docs/toolchain-links.md`. +The script uses the tool paths recorded in `../../docs/ToolchainLinks.md`. ## Build And Test @@ -209,5 +209,5 @@ go run ./cmd/mxgw-go smoke -endpoint $env:MXGATEWAY_ENDPOINT -plaintext -api-key ## Related Documentation - [Client Packaging](../../docs/ClientPackaging.md) -- [Client Proto Generation](../../docs/client-proto-generation.md) -- [Go Client Detailed Design](../../docs/clients-golang-design.md) +- [Client Proto Generation](../../docs/ClientProtoGeneration.md) +- [Go Client Detailed Design](./GoClientDesign.md) diff --git a/clients/go/generate-proto.ps1 b/clients/go/generate-proto.ps1 index a4d1d80..5697de9 100644 --- a/clients/go/generate-proto.ps1 +++ b/clients/go/generate-proto.ps1 @@ -9,13 +9,13 @@ $protoc = 'C:\Users\dohertj2\AppData\Local\Microsoft\WinGet\Packages\Google.Prot $goPluginPath = 'C:\Users\dohertj2\go\bin' if (-not (Test-Path $protoc)) { - throw "protoc was not found at $protoc. See docs/toolchain-links.md." + throw "protoc was not found at $protoc. See docs/ToolchainLinks.md." } foreach ($pluginName in @('protoc-gen-go.exe', 'protoc-gen-go-grpc.exe')) { $pluginPath = Join-Path $goPluginPath $pluginName if (-not (Test-Path $pluginPath)) { - throw "$pluginName was not found at $pluginPath. See docs/toolchain-links.md." + throw "$pluginName was not found at $pluginPath. See docs/ToolchainLinks.md." } } diff --git a/docs/clients-java-design.md b/clients/java/JavaClientDesign.md similarity index 92% rename from docs/clients-java-design.md rename to clients/java/JavaClientDesign.md index edab9b4..e02e547 100644 --- a/docs/clients-java-design.md +++ b/clients/java/JavaClientDesign.md @@ -5,8 +5,8 @@ Provide a Java client library for MXAccess Gateway, plus a test CLI and unit tests. The Java client should work for JVM services and operator tooling. -Follow the [Java Style Guide](./style-guides/JavaStyleGuide.md) for handwritten -code and the [Protobuf Style Guide](./style-guides/ProtobufStyleGuide.md) for +Follow the [Java Style Guide](../../docs/style-guides/JavaStyleGuide.md) for handwritten +code and the [Protobuf Style Guide](../../docs/style-guides/ProtobufStyleGuide.md) for generated contract inputs. ## Build Layout @@ -210,3 +210,10 @@ The `mxgateway-client` project generates the gateway and worker protobuf/gRPC bindings into `src/main/generated`, compiles the generated contracts, and runs JUnit 5 tests. The `mxgateway-cli` project builds a Picocli-based `mxgw-java` entry point for later command implementation. + +## Related Documentation + +- [Client Libraries Detailed Design](../../docs/ClientLibrariesDesign.md) +- [Client Proto Generation](../../docs/ClientProtoGeneration.md) +- [Client Packaging](../../docs/ClientPackaging.md) +- [Java Style Guide](../../docs/style-guides/JavaStyleGuide.md) diff --git a/clients/java/README.md b/clients/java/README.md index 0034fb2..7a9fe97 100644 --- a/clients/java/README.md +++ b/clients/java/README.md @@ -223,6 +223,6 @@ gradle :mxgateway-cli:run --args="smoke --endpoint $env:MXGATEWAY_ENDPOINT --pla ## Related Documentation - [Client Packaging](../../docs/ClientPackaging.md) -- [Client Proto Generation](../../docs/client-proto-generation.md) -- [Java Client Detailed Design](../../docs/clients-java-design.md) +- [Client Proto Generation](../../docs/ClientProtoGeneration.md) +- [Java Client Detailed Design](./JavaClientDesign.md) - [Java Style Guide](../../docs/style-guides/JavaStyleGuide.md) diff --git a/docs/clients-python-design.md b/clients/python/PythonClientDesign.md similarity index 91% rename from docs/clients-python-design.md rename to clients/python/PythonClientDesign.md index dac1660..f323f25 100644 --- a/docs/clients-python-design.md +++ b/clients/python/PythonClientDesign.md @@ -6,8 +6,8 @@ Provide an async Python client package for MXAccess Gateway, plus a test CLI and unit tests. The Python client should be useful for automation, diagnostics, and test harnesses. -Follow the [Python Style Guide](./style-guides/PythonStyleGuide.md) for -handwritten code and the [Protobuf Style Guide](./style-guides/ProtobufStyleGuide.md) +Follow the [Python Style Guide](../../docs/style-guides/PythonStyleGuide.md) for +handwritten code and the [Protobuf Style Guide](../../docs/style-guides/ProtobufStyleGuide.md) for generated contract inputs. ## Package Layout @@ -195,3 +195,10 @@ mxaccess-gateway-client Generated protobuf code should be regenerated through a documented command, not edited by hand. + +## Related Documentation + +- [Client Libraries Detailed Design](../../docs/ClientLibrariesDesign.md) +- [Client Proto Generation](../../docs/ClientProtoGeneration.md) +- [Client Packaging](../../docs/ClientPackaging.md) +- [Python Style Guide](../../docs/style-guides/PythonStyleGuide.md) diff --git a/clients/python/README.md b/clients/python/README.md index 0b84b2d..69ea1eb 100644 --- a/clients/python/README.md +++ b/clients/python/README.md @@ -3,7 +3,7 @@ The Python client package contains generated MXAccess Gateway protobuf bindings, the async `mxgateway` package, and the `mxgw-py` test CLI. The package uses the shared proto inputs documented in -`../../docs/client-proto-generation.md` so gateway and client contracts stay in +`../../docs/ClientProtoGeneration.md` so gateway and client contracts stay in sync. ## Layout @@ -31,7 +31,7 @@ changes: ``` The script uses the Python tool path recorded in -`../../docs/toolchain-links.md`. +`../../docs/ToolchainLinks.md`. ## Build And Test @@ -219,5 +219,5 @@ mxgw-py smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGAT ## Related Documentation - [Client Packaging](../../docs/ClientPackaging.md) -- [Client Proto Generation](../../docs/client-proto-generation.md) -- [Python Client Detailed Design](../../docs/clients-python-design.md) +- [Client Proto Generation](../../docs/ClientProtoGeneration.md) +- [Python Client Detailed Design](./PythonClientDesign.md) diff --git a/clients/python/generate-proto.ps1 b/clients/python/generate-proto.ps1 index fdc41ff..ffce22e 100644 --- a/clients/python/generate-proto.ps1 +++ b/clients/python/generate-proto.ps1 @@ -7,7 +7,7 @@ $outputRoot = Join-Path $PSScriptRoot 'src\mxgateway\generated' $python = 'C:\Users\dohertj2\AppData\Local\Programs\Python\Python312\python.exe' if (-not (Test-Path $python)) { - throw "Python was not found at $python. See docs/toolchain-links.md." + throw "Python was not found at $python. See docs/ToolchainLinks.md." } New-Item -ItemType Directory -Path $outputRoot -Force | Out-Null diff --git a/clients/rust/README.md b/clients/rust/README.md index 0b34955..0bcdbfb 100644 --- a/clients/rust/README.md +++ b/clients/rust/README.md @@ -4,7 +4,7 @@ The Rust client workspace contains the MXAccess Gateway client library, a test CLI, and tests for generated contract wiring plus wrapper behavior. The library uses the shared protobuf inputs documented in -`../../docs/client-proto-generation.md` so the Rust bindings compile against +`../../docs/ClientProtoGeneration.md` so the Rust bindings compile against the same public gateway and worker contracts as the server. ## Layout @@ -36,7 +36,7 @@ cargo clippy --workspace --all-targets -- -D warnings ``` The build script uses `protoc` from `PATH` or the Windows path recorded in -`../../docs/toolchain-links.md`. +`../../docs/ToolchainLinks.md`. ## Packaging @@ -184,6 +184,6 @@ cargo run -p mxgw-cli -- smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext -- ## Related Documentation - [Client Packaging](../../docs/ClientPackaging.md) -- [Client Proto Generation](../../docs/client-proto-generation.md) -- [Rust Client Detailed Design](../../docs/clients-rust-design.md) +- [Client Proto Generation](../../docs/ClientProtoGeneration.md) +- [Rust Client Detailed Design](./RustClientDesign.md) - [Rust Style Guide](../../docs/style-guides/RustStyleGuide.md) diff --git a/docs/clients-rust-design.md b/clients/rust/RustClientDesign.md similarity index 91% rename from docs/clients-rust-design.md rename to clients/rust/RustClientDesign.md index ccd5c75..070ee11 100644 --- a/docs/clients-rust-design.md +++ b/clients/rust/RustClientDesign.md @@ -5,8 +5,8 @@ Provide an async Rust client crate for MXAccess Gateway, plus a test CLI and unit tests. The Rust client should use `tonic` and `tokio`. -Follow the [Rust Style Guide](./style-guides/RustStyleGuide.md) for handwritten -code and the [Protobuf Style Guide](./style-guides/ProtobufStyleGuide.md) for +Follow the [Rust Style Guide](../../docs/style-guides/RustStyleGuide.md) for handwritten +code and the [Protobuf Style Guide](../../docs/style-guides/ProtobufStyleGuide.md) for generated contract inputs. ## Crate Layout @@ -187,3 +187,10 @@ MXGATEWAY_INTEGRATION=1 Use `tokio::test`. Run bounded smoke flow and ensure `CloseSession` is attempted with `drop` fallback docs, but do not rely on `Drop` for async close. + +## Related Documentation + +- [Client Libraries Detailed Design](../../docs/ClientLibrariesDesign.md) +- [Client Proto Generation](../../docs/ClientProtoGeneration.md) +- [Client Packaging](../../docs/ClientPackaging.md) +- [Rust Style Guide](../../docs/style-guides/RustStyleGuide.md) diff --git a/docs/Authentication.md b/docs/Authentication.md index 2732fe8..79d524f 100644 --- a/docs/Authentication.md +++ b/docs/Authentication.md @@ -91,12 +91,15 @@ return ApiKeyVerificationResult.Success(new ApiKeyIdentity( KeyId: storedKey.KeyId, KeyPrefix: storedKey.KeyPrefix, DisplayName: storedKey.DisplayName, - Scopes: storedKey.Scopes)); + Scopes: storedKey.Scopes, + Constraints: storedKey.Constraints)); ``` `ApiKeyVerificationResult` carries either an `ApiKeyIdentity` or a discriminated `ApiKeyVerificationFailure` value. The failure enum distinguishes parse errors, missing pepper, missing or revoked keys, and secret mismatch so the calling middleware can emit precise audit detail without leaking which check failed to the client. -`ApiKeyIdentity` exposes only non-secret fields (`KeyId`, `KeyPrefix`, `DisplayName`, `Scopes`) and is the type downstream authorization code consumes. +`ApiKeyIdentity` exposes only non-secret fields (`KeyId`, `KeyPrefix`, +`DisplayName`, `Scopes`, and `Constraints`) and is the type downstream +authorization code consumes. ## Storage @@ -131,7 +134,9 @@ public SqliteConnection CreateConnection() `SqliteAuthSchema` declares table names and the current schema version as constants. Three tables are involved: -- `api_keys` stores `key_id`, `key_prefix`, the `secret_hash` blob, `display_name`, serialized `scopes`, and the `created_utc`, `last_used_utc`, and `revoked_utc` timestamps. +- `api_keys` stores `key_id`, `key_prefix`, the `secret_hash` blob, + `display_name`, serialized `scopes`, optional serialized `constraints`, and + the `created_utc`, `last_used_utc`, and `revoked_utc` timestamps. - `api_key_audit` is an append-only log keyed by an autoincrement `audit_id` with `key_id`, `event_type`, `remote_address`, `created_utc`, and `details` columns. - `schema_version` carries a single row whose `version` column is matched against `SqliteAuthSchema.CurrentVersion`. @@ -150,9 +155,10 @@ public static ApiKeyRecord Read(SqliteDataReader reader) SecretHash: (byte[])reader["secret_hash"], DisplayName: reader.GetString(3), Scopes: ApiKeyScopeSerializer.Deserialize(reader.GetString(4)), - CreatedUtc: DateTimeOffset.Parse(reader.GetString(5), System.Globalization.CultureInfo.InvariantCulture), - LastUsedUtc: ReadNullableDateTimeOffset(reader, 6), - RevokedUtc: ReadNullableDateTimeOffset(reader, 7)); + Constraints: ApiKeyConstraintSerializer.Deserialize(reader.IsDBNull(5) ? null : reader.GetString(5)), + CreatedUtc: DateTimeOffset.Parse(reader.GetString(6), System.Globalization.CultureInfo.InvariantCulture), + LastUsedUtc: ReadNullableDateTimeOffset(reader, 7), + RevokedUtc: ReadNullableDateTimeOffset(reader, 8)); } ``` @@ -193,8 +199,8 @@ The supported subcommands match `ApiKeyAdminCommandKind` exactly: | Subcommand | Required options | Behaviour | |------------|------------------|-----------| | `init-db` | none | Runs the migrator and records an audit entry. | -| `create-key` | `--key-id`, `--display-name` | Generates a new secret, stores its peppered hash, and prints the assembled `mxgw__` token. | -| `list-keys` | none | Lists every stored key with its scopes and revocation state. | +| `create-key` | `--key-id`, `--display-name` | Generates a new secret, stores its peppered hash and optional constraints, and prints the assembled `mxgw__` token. | +| `list-keys` | none | Lists every stored key with its scopes, constraints, and revocation state. | | `revoke-key` | `--key-id` | Sets `revoked_utc` if the key is currently active. | | `rotate-key` | `--key-id` | Replaces the secret hash and prints the new token. | @@ -203,11 +209,18 @@ Examples: ```bash mxgateway apikey init-db mxgateway apikey create-key --key-id ops.alice --display-name "Alice (ops)" --scopes read,write +mxgateway apikey create-key --key-id area1.reader --display-name "Area 1 reader" --scopes invoke:read,metadata:read --read-subtree "Area1/*" --browse-subtree "Area1/*" mxgateway apikey list-keys --json mxgateway apikey revoke-key --key-id ops.alice mxgateway apikey rotate-key --key-id ops.alice ``` +Constraint flags are optional. `--read-subtree`, `--write-subtree`, +`--read-tag-glob`, `--write-tag-glob`, and `--browse-subtree` are repeatable. +`--max-write-classification` accepts one integer. `--read-alarm-only` and +`--read-historized-only` are boolean flags. Existing rows with null +constraints remain fully unconstrained after migration. + Key ids are restricted by the parser to ASCII letters, digits, periods, and hyphens so they remain safe to embed in the token format and in URL paths used by administrative tooling. ## Scope Serialization diff --git a/docs/Authorization.md b/docs/Authorization.md index 9265313..c212bfc 100644 --- a/docs/Authorization.md +++ b/docs/Authorization.md @@ -1,6 +1,8 @@ # Gateway gRPC Authorization -The authorization subsystem enforces per-RPC scope checks against the authenticated `ApiKeyIdentity` produced by the authentication layer, so service implementations never need to repeat permission logic. +The authorization subsystem has two layers. The gRPC interceptor enforces the +verb scope required by the RPC. Service-layer constraint checks then narrow +what an authenticated API key can browse, read, or write inside the Galaxy. ## Overview @@ -12,6 +14,8 @@ The participating types live under `src/MxGateway.Server/Security/Authorization/ - `GatewayGrpcScopeResolver` maps a request message (and, for `MxCommandRequest`, the inner `MxCommandKind`) to the scope string that must be present on the caller. - `GatewayScopes` exposes the canonical scope constants used by the resolver and any downstream consumer. - `GatewayRequestIdentityAccessor` and `IGatewayRequestIdentityAccessor` expose the verified identity to handlers and any service code that runs inside the call. +- `IConstraintEnforcer` applies optional API-key constraints against the + cached Galaxy hierarchy from service bodies. - `GrpcAuthorizationServiceCollectionExtensions` wires the components into the DI container and the gRPC pipeline. The `ApiKeyIdentity` consumed here is produced by the authentication layer; see [Authentication](./Authentication.md) for how it is built and how scopes are persisted. @@ -21,7 +25,9 @@ The `ApiKeyIdentity` consumed here is produced by the authentication layer; see Centralizing the policy in `GatewayGrpcAuthorizationInterceptor` produces three concrete benefits: 1. Every RPC defined in `MxAccessGatewayService` is covered by construction. A new RPC inherits the check the moment its request type is added to `GatewayGrpcScopeResolver`, instead of relying on each service method to remember to call an authorization helper. -2. The service class stays a thin translator between proto contracts and domain calls. RPC methods do not branch on identity or scope, which keeps the AGENTS.md guideline that gRPC handlers contain no policy. +2. Verb-scope policy stays centralized. Request-specific constraints still run + in service bodies because they need command payloads, item handles, and + Galaxy metadata that the interceptor should not inspect. 3. Authentication and authorization happen in one place, so the gRPC `Status` mapping is consistent. A failed key check always returns `Unauthenticated`, and a missing scope always returns `PermissionDenied` with the offending scope name. ## Interceptor Flow @@ -131,6 +137,43 @@ private static string ResolveCommandScope(MxCommandKind kind) Reads (`Register`, `AddItem`, `Advise`, and any other unspecified kind) fall through to `InvokeRead`, which keeps the matrix small while still separating reads from writes, secured writes, metadata lookups, event drains, and worker shutdown. +## Constraint Enforcement + +`ApiKeyIdentity.Constraints` is optional. Empty constraints preserve the +previous behavior: the key is authorized only by its verb scopes. Non-empty +constraints are stored as JSON in `api_keys.constraints` and are applied by +`IConstraintEnforcer` after the interceptor succeeds. + +Supported constraints are: + +| Constraint | Meaning | +|------------|---------| +| `read_subtrees` | Contained-path globs allowed for read/subscription commands. | +| `write_subtrees` | Contained-path globs allowed for write commands. | +| `read_tag_globs` | Tag-address globs allowed for read/subscription commands. | +| `write_tag_globs` | Tag-address globs allowed for write commands. | +| `max_write_classification` | Maximum Galaxy attribute `security_classification` a key may write. | +| `browse_subtrees` | Contained-path globs used to filter Galaxy browse results and deploy-event counts. | +| `read_alarm_only` | Read/subscription commands must target objects with alarm-bearing attributes. | +| `read_historized_only` | Read/subscription commands must target objects with historized attributes. | + +Glob matching is anchored, case-insensitive, and supports `*` and `?`. +Subtree and tag glob lists are alternatives: matching either list allows that +scope dimension. Empty lists mean unconstrained for that dimension. + +The service checks read constraints for `AddItem`, `AddItem2`, `AddItemBulk`, +`SubscribeBulk`, and `AdviseItemBulk`. It checks write constraints for +`Write`, `Write2`, `WriteSecured`, and `WriteSecured2`. Successful item +registrations are tracked per session so later item-handle commands resolve +back to the original tag address. If a constrained key presents an unknown item +handle, the gateway fails closed. + +Non-bulk constraint failures return gRPC `PermissionDenied`. Bulk read +commands preserve input order and return a failed `SubscribeResult` for each +denied item while still forwarding allowed items to the worker. Every denial +adds an `api_key_audit` entry with the key id, command kind, target, and +blocking constraint; secured values and raw credentials are never logged. + ## Scope Catalog `GatewayScopes` is the single source of truth for scope strings. Every entry is currently mapped by either the resolver or another security component: diff --git a/docs/ClientBehaviorFixtures.md b/docs/ClientBehaviorFixtures.md index e3765e4..2cf6699 100644 --- a/docs/ClientBehaviorFixtures.md +++ b/docs/ClientBehaviorFixtures.md @@ -101,6 +101,6 @@ fixtures and validate deterministic wrapper expectation files. ## Related Documentation -- [Client Proto Generation](./client-proto-generation.md) -- [Client Libraries Detailed Design](./client-libraries-design.md) +- [Client Proto Generation](./ClientProtoGeneration.md) +- [Client Libraries Detailed Design](./ClientLibrariesDesign.md) - [Protobuf Contracts](./Contracts.md) diff --git a/docs/client-libraries-design.md b/docs/ClientLibrariesDesign.md similarity index 92% rename from docs/client-libraries-design.md rename to docs/ClientLibrariesDesign.md index fc2805d..7da378c 100644 --- a/docs/client-libraries-design.md +++ b/docs/ClientLibrariesDesign.md @@ -20,15 +20,15 @@ Target client languages: Language-specific plans: -- `docs/clients-dotnet-csharp-design.md` -- `docs/clients-golang-design.md` -- `docs/clients-rust-design.md` -- `docs/clients-python-design.md` -- `docs/clients-java-design.md` +- `clients/dotnet/DotnetClientDesign.md` +- `clients/go/GoClientDesign.md` +- `clients/rust/RustClientDesign.md` +- `clients/python/PythonClientDesign.md` +- `clients/java/JavaClientDesign.md` Shared generation inputs: -- `docs/client-proto-generation.md` +- `docs/ClientProtoGeneration.md` - `docs/ClientBehaviorFixtures.md` - `docs/ClientPackaging.md` - `clients/proto/proto-inputs.json` @@ -433,3 +433,15 @@ Each client README should include: - integration test instructions, - warning that canceling a client call does not abort an in-flight MXAccess COM call. + +## Related Documentation + +- [.NET 10 C# Client Detailed Design](../clients/dotnet/DotnetClientDesign.md) +- [Go Client Detailed Design](../clients/go/GoClientDesign.md) +- [Rust Client Detailed Design](../clients/rust/RustClientDesign.md) +- [Python Client Detailed Design](../clients/python/PythonClientDesign.md) +- [Java Client Detailed Design](../clients/java/JavaClientDesign.md) +- [Client Proto Generation](./ClientProtoGeneration.md) +- [Client Behavior Fixtures](./ClientBehaviorFixtures.md) +- [Client Packaging](./ClientPackaging.md) +- [Cross-Language Smoke Matrix](./CrossLanguageSmokeMatrix.md) diff --git a/docs/ClientPackaging.md b/docs/ClientPackaging.md index bf8aec6..bb29f28 100644 --- a/docs/ClientPackaging.md +++ b/docs/ClientPackaging.md @@ -2,7 +2,7 @@ This document defines the clean-checkout commands for building, packaging, and running the official MXAccess Gateway clients. Use the tool paths and versions -in [Toolchain Links](./toolchain-links.md) when a command is missing from +in [Toolchain Links](./ToolchainLinks.md) when a command is missing from `PATH`. ## Shared Inputs @@ -254,7 +254,7 @@ does not abort an MXAccess COM call that is already executing on the worker STA. ## Related Documentation -- [Client Proto Generation](./client-proto-generation.md) -- [Client Libraries Detailed Design](./client-libraries-design.md) +- [Client Proto Generation](./ClientProtoGeneration.md) +- [Client Libraries Detailed Design](./ClientLibrariesDesign.md) - [Client Behavior Fixtures](./ClientBehaviorFixtures.md) -- [Toolchain Links](./toolchain-links.md) +- [Toolchain Links](./ToolchainLinks.md) diff --git a/docs/client-proto-generation.md b/docs/ClientProtoGeneration.md similarity index 98% rename from docs/client-proto-generation.md rename to docs/ClientProtoGeneration.md index 745b54d..c45763a 100644 --- a/docs/client-proto-generation.md +++ b/docs/ClientProtoGeneration.md @@ -199,8 +199,8 @@ scripts/validate-client-behavior-fixtures.ps1 ## Related Documentation - [Protobuf Contracts](./Contracts.md) -- [Client Libraries Detailed Design](./client-libraries-design.md) +- [Client Libraries Detailed Design](./ClientLibrariesDesign.md) - [Client Packaging](./ClientPackaging.md) - [Client Behavior Fixtures](./ClientBehaviorFixtures.md) -- [Client Libraries Implementation Plan](./implementation-plan-clients.md) +- [Client Libraries Implementation Plan](./ImplementationPlanClients.md) - [Protobuf Style Guide](./style-guides/ProtobufStyleGuide.md) diff --git a/docs/Contracts.md b/docs/Contracts.md index 0b1f8d9..cef6690 100644 --- a/docs/Contracts.md +++ b/docs/Contracts.md @@ -41,7 +41,7 @@ hand-edit generated files. Client generation inputs are published through `clients/proto/proto-inputs.json` and the descriptor set under `clients/proto/descriptors/`. See -[Client Proto Generation](./client-proto-generation.md) for language-specific +[Client Proto Generation](./ClientProtoGeneration.md) for language-specific generation inputs, output directories, and golden protobuf JSON fixtures. ## Generation @@ -73,7 +73,7 @@ powershell -ExecutionPolicy Bypass -File scripts/publish-client-proto-inputs.ps1 ## Related Documentation -- [Client Proto Generation](./client-proto-generation.md) -- [Gateway Process Detailed Design](./gateway-process-design.md) -- [MXAccess Worker Instance Detailed Design](./mxaccess-worker-instance-design.md) +- [Client Proto Generation](./ClientProtoGeneration.md) +- [Gateway Process Detailed Design](./GatewayProcessDesign.md) +- [MXAccess Worker Instance Detailed Design](./MxAccessWorkerInstanceDesign.md) - [Protobuf Style Guide](./style-guides/ProtobufStyleGuide.md) diff --git a/docs/CrossLanguageSmokeMatrix.md b/docs/CrossLanguageSmokeMatrix.md index d86206b..9dc8c3a 100644 --- a/docs/CrossLanguageSmokeMatrix.md +++ b/docs/CrossLanguageSmokeMatrix.md @@ -94,5 +94,5 @@ gateway, the installed MXAccess worker path, and provider state. ## Related Documentation - [Gateway Testing](./GatewayTesting.md) -- [Client Libraries Detailed Design](./client-libraries-design.md) -- [Client Proto Generation](./client-proto-generation.md) +- [Client Libraries Detailed Design](./ClientLibrariesDesign.md) +- [Client Proto Generation](./ClientProtoGeneration.md) diff --git a/docs/DashboardInterfaceDesign.md b/docs/DashboardInterfaceDesign.md index 1135f35..274c7fa 100644 --- a/docs/DashboardInterfaceDesign.md +++ b/docs/DashboardInterfaceDesign.md @@ -291,4 +291,4 @@ Use this checklist when applying the design to another project: ## Related Documentation -- [Gateway Dashboard Detailed Design](./gateway-dashboard-design.md) +- [Gateway Dashboard Detailed Design](./GatewayDashboardDesign.md) diff --git a/docs/design-decisions.md b/docs/DesignDecisions.md similarity index 97% rename from docs/design-decisions.md rename to docs/DesignDecisions.md index 34aa1cc..1be2afa 100644 --- a/docs/design-decisions.md +++ b/docs/DesignDecisions.md @@ -308,3 +308,11 @@ These are explicit post-v1 revisit items, not open blockers: - restricted worker service account, - production coalescing by item handle, - command batching for high-volume tag setup. + +## Related Documentation + +- [Gateway Process Detailed Design](./GatewayProcessDesign.md) +- [MXAccess Worker Instance Detailed Design](./MxAccessWorkerInstanceDesign.md) +- [Authentication](./Authentication.md) +- [Authorization](./Authorization.md) +- [Galaxy Repository](./GalaxyRepository.md) diff --git a/docs/GalaxyRepository.md b/docs/GalaxyRepository.md index 6141f2b..b9957f1 100644 --- a/docs/GalaxyRepository.md +++ b/docs/GalaxyRepository.md @@ -32,13 +32,23 @@ The service is defined in |-----|---------| | `TestConnection` | Connectivity probe. Returns `{ ok: bool }` after a `SELECT 1`. Does not throw on SQL failure — returns `ok = false`. Always hits SQL directly so it remains a true health check. | | `GetLastDeployTime` | Returns the cached `galaxy.time_of_last_deploy`. Served from the shared hierarchy cache; refreshed in the background. | -| `DiscoverHierarchy` | Returns the full deployed hierarchy plus every object's dynamic attributes. **Served from cache** — see [Hierarchy Cache](#hierarchy-cache). | +| `DiscoverHierarchy` | Returns one page of the deployed hierarchy plus each returned object's dynamic attributes. **Served from cache** — see [Hierarchy Cache](#hierarchy-cache). | | `WatchDeployEvents` | **Server-streaming.** The server emits the current state immediately on subscribe (so clients can bootstrap without waiting), then emits one event per detected deploy change. See [Deploy Notifications](#deploy-notifications). | -`DiscoverHierarchy` is intentionally a single unary RPC rather than a stream: -the row set is small (thousands of objects, low tens-of-thousands of -attributes for typical Galaxies) and clients almost always want the whole tree -at once. +`DiscoverHierarchy` is a paged unary RPC. The raw request accepts `page_size` +and `page_token`; the server defaults omitted page size to 1000 objects and +caps every page at 5000 objects. Page tokens bind to the cache sequence and the +active filter set, so changing filters between pages returns `InvalidArgument` +instead of mixing snapshots. Official high-level clients preserve the older +"return the full hierarchy" behavior by looping pages internally. + +The request can also slice the cached hierarchy without running new SQL. A +caller may choose one root (`root_gobject_id`, `root_tag_name`, or +`root_contained_path`) and may combine that with `max_depth`, category ids, +template-chain substring filters, an anchored case-insensitive tag-name glob, +alarm-only, historized-only, and `include_attributes = false` for a skeleton +tree. All filters are applied with AND semantics, and `total_object_count` +reports the post-filter count. ## Hierarchy Cache @@ -56,12 +66,14 @@ Refresh strategy is **deploy-time gated**: 3. If the deploy timestamp is unchanged, the heavy hierarchy + attributes queries are **skipped**. The cache simply marks `LastSuccessAt`. 4. If the deploy timestamp changed (or no data has loaded yet), the cache - pulls hierarchy + attributes, materializes a `DiscoverHierarchyReply` - once, replaces the entry atomically, and publishes a deploy event. + pulls hierarchy + attributes, materializes a Galaxy object list plus a + dashboard summary once, replaces the entry atomically, and publishes a + deploy event. -Materializing the reply at refresh time means subsequent `DiscoverHierarchy` -calls return a pre-built proto message — no per-request projection, no -per-request allocations beyond the gRPC serializer's frame. +Materializing objects and dashboard summaries at refresh time means subsequent +`DiscoverHierarchy` calls page over an immutable object list. The dashboard +uses the precomputed summary and does not rescan raw SQL rowsets on each +snapshot. When SQL is unreachable, the cache retains the previous data and flips `Status` to `Stale` (or `Unavailable` if no data was ever loaded). A @@ -110,7 +122,7 @@ Typical client pattern: 3. If sequence skipped a number, treat it as a dropped event and refresh. ``` -### Reply Shape +### Reply shape ```proto message GalaxyObject { @@ -139,9 +151,32 @@ message GalaxyAttribute { bool is_historized = 10; bool is_alarm = 11; } + +message DiscoverHierarchyRequest { + int32 page_size = 1; // omitted/0 uses the server default of 1000 + string page_token = 2; // opaque token returned by the previous page + oneof root { + int32 root_gobject_id = 3; + string root_tag_name = 4; + string root_contained_path = 5; + } + google.protobuf.Int32Value max_depth = 6; + repeated int32 category_ids = 7; + repeated string template_chain_contains = 8; + string tag_name_glob = 9; + optional bool include_attributes = 10; + bool alarm_bearing_only = 11; + bool historized_only = 12; +} + +message DiscoverHierarchyReply { + repeated GalaxyObject objects = 1; + string next_page_token = 2; + int32 total_object_count = 3; +} ``` -### Contained Name vs Tag Name +### Contained name vs tag name Galaxy objects carry two names. `tag_name` is globally unique and is what MXAccess expects in `AddItem`. `contained_name` is the human-readable name @@ -150,7 +185,7 @@ both: clients display `browse_name` to users and pass `tag_name` (or `full_tag_reference`) into MXAccess subscriptions. When `contained_name` is empty (top-level objects), `browse_name` falls back to `tag_name`. -### Data Types +### Data types `mx_data_type` is returned as the raw Galaxy integer rather than mapped to a language-neutral enum. The gateway makes no assumption about the client's @@ -176,7 +211,8 @@ GalaxyHierarchyRefreshService (BackgroundService) -> GalaxyRepository.GetLastDeployTimeAsync (cheap, every tick) -> GalaxyRepository.GetHierarchyAsync (only on deploy change) -> GalaxyRepository.GetAttributesAsync (only on deploy change) - -> GalaxyProtoMapper.MapObject (materialize DiscoverHierarchyReply once) + -> GalaxyProtoMapper.MapObject (materialize GalaxyObject list once) + -> DashboardGalaxySummary (precompute dashboard counts once) -> IGalaxyDeployNotifier.Publish (only on deploy change) ``` @@ -189,8 +225,9 @@ Component breakdown: recursive CTEs and pick the most-derived attribute override per object. - `GalaxyHierarchyCache` (`src/MxGateway.Server/Galaxy/GalaxyHierarchyCache.cs`) holds the most - recent immutable `GalaxyHierarchyCacheEntry` (rows + materialized proto - reply + counts + status). All gRPC clients share the same entry. + recent immutable `GalaxyHierarchyCacheEntry` (materialized objects + + precomputed dashboard summary + counts + status). All gRPC clients share the + same entry. - `GalaxyHierarchyRefreshService` (`src/MxGateway.Server/Galaxy/GalaxyHierarchyRefreshService.cs`) is a hosted `BackgroundService` that drives `RefreshAsync` on the configured @@ -220,6 +257,11 @@ Security`), but production deployments that use SQL authentication should set the override via environment variable rather than committing credentials to `appsettings.json`. +The dashboard parses this connection string and displays only non-secret +fields: server, database, integrated security, encrypt, and trust-server- +certificate. It never displays user id, password, access token, or arbitrary +unparsed connection string text. + ## Authorization All four Galaxy RPCs (including `WatchDeployEvents`) require the @@ -228,6 +270,11 @@ privilege to `MxCommandKind.GetSessionState` or `MxCommandKind.GetWorkerInfo`. The mapping lives in `GatewayGrpcScopeResolver`; see [Authorization](./Authorization.md) for the full scope catalog. +API keys can also carry `browse_subtrees` constraints. `DiscoverHierarchy` +intersects those contained-path globs with the caller's request filters. +`WatchDeployEvents` still emits deploy notifications, but its object and +attribute counts are scoped to the caller's browsable subtrees. + A request without an API key returns `Unauthenticated`. A request with a key that lacks `metadata:read` returns `PermissionDenied` with the missing scope embedded in the status detail. diff --git a/docs/GatewayConfiguration.md b/docs/GatewayConfiguration.md index d57bac5..7213e7a 100644 --- a/docs/GatewayConfiguration.md +++ b/docs/GatewayConfiguration.md @@ -35,6 +35,8 @@ paths, timeouts, queue sizes, enum values, or protocol values are invalid. "DefaultCommandTimeoutSeconds": 30, "MaxSessions": 64, "MaxPendingCommandsPerSession": 128, + "DefaultLeaseSeconds": 1800, + "LeaseSweepIntervalSeconds": 30, "AllowMultipleEventSubscribers": false }, "Events": { @@ -52,7 +54,8 @@ paths, timeouts, queue sizes, enum values, or protocol values are invalid. "ShowTagValues": false }, "Protocol": { - "WorkerProtocolVersion": 1 + "WorkerProtocolVersion": 1, + "MaxGrpcMessageBytes": 16777216 }, "Galaxy": { "ConnectionString": "Server=localhost;Database=ZB;Integrated Security=True;TrustServerCertificate=True;Encrypt=False;", @@ -107,6 +110,8 @@ to avoid accidental large allocations from malformed or oversized frames. | `MxGateway:Sessions:DefaultCommandTimeoutSeconds` | `30` | Default timeout used while the gateway waits for a worker command reply when an open-session request does not provide a positive command timeout. | | `MxGateway:Sessions:MaxSessions` | `64` | Maximum number of concurrently open gateway sessions. Session opens reserve a slot atomically before worker creation. | | `MxGateway:Sessions:MaxPendingCommandsPerSession` | `128` | Maximum number of pending worker commands for one session. Excess commands fail fast instead of queueing indefinitely. | +| `MxGateway:Sessions:DefaultLeaseSeconds` | `1800` | Initial session lease and refresh duration. Unary client activity extends the lease by this duration. | +| `MxGateway:Sessions:LeaseSweepIntervalSeconds` | `30` | Hosted monitor interval for closing expired leases. Active event-stream subscribers keep a session from expiring while the stream remains attached. | | `MxGateway:Sessions:AllowMultipleEventSubscribers` | `false` | Controls whether multiple `StreamEvents` subscribers may attach to one session. `true` is rejected until event fan-out is implemented. | All numeric session options must be greater than zero. The current event stream @@ -146,6 +151,7 @@ and `RecentSessionLimit` must be greater than or equal to zero. | Option | Default | Description | |--------|---------|-------------| | `MxGateway:Protocol:WorkerProtocolVersion` | `1` | Worker IPC protocol version expected by the gateway and worker. This must match `GatewayContractInfo.WorkerProtocolVersion`. | +| `MxGateway:Protocol:MaxGrpcMessageBytes` | `16777216` | Public gRPC max send and receive message size in bytes. The same default is used by official clients. The validator allows values from `1024` through `268435456`. | The protocol option is exposed for diagnostics and explicit deployment configuration, not for compatibility negotiation. A mismatch fails validation @@ -164,8 +170,8 @@ behavior. ## Related Documentation -- [Gateway Process Detailed Design](./gateway-process-design.md) -- [Gateway Dashboard Detailed Design](./gateway-dashboard-design.md) +- [Gateway Process Detailed Design](./GatewayProcessDesign.md) +- [Gateway Dashboard Detailed Design](./GatewayDashboardDesign.md) - [Worker Process Launcher](./WorkerProcessLauncher.md) - [Worker Frame Protocol](./WorkerFrameProtocol.md) - [Galaxy Repository Browse](./GalaxyRepository.md) diff --git a/docs/gateway-dashboard-design.md b/docs/GatewayDashboardDesign.md similarity index 96% rename from docs/gateway-dashboard-design.md rename to docs/GatewayDashboardDesign.md index cd05843..d57f4ed 100644 --- a/docs/gateway-dashboard-design.md +++ b/docs/GatewayDashboardDesign.md @@ -165,7 +165,7 @@ counts and rates instead. ## Pages -### Dashboard Home +### Dashboard home Show top-level status: @@ -184,7 +184,7 @@ Show top-level status: Use Bootstrap cards for individual metric summaries. Keep the layout compact and operational. -### Sessions Page +### Sessions page Show active and recent sessions in a table: @@ -203,7 +203,7 @@ Show active and recent sessions in a table: Rows should link to session details. -### Session Details Page +### Session details page Show: @@ -219,7 +219,7 @@ Show: For v1, details should be read-only unless an explicit admin action design is added. -### Workers Page +### Workers page Show: @@ -235,7 +235,7 @@ Show: - event queue depth, - restart/kill reason if terminal. -### Events Page +### Events page Show aggregate event diagnostics: @@ -249,7 +249,7 @@ Show aggregate event diagnostics: Do not display full tag values by default. If value display is later added, make it opt-in and redacted. -### Settings Page +### Settings page Show read-only effective configuration: @@ -390,3 +390,13 @@ The first dashboard slice implements: 11. periodic realtime refresh through Blazor Server. 12. route-mapping tests, disabled-dashboard tests, auth tests, and snapshot projection/redaction tests. + +## Related Documentation + +- [Dashboard Interface Design](./DashboardInterfaceDesign.md) +- [Gateway Process Detailed Design](./GatewayProcessDesign.md) +- [Authentication](./Authentication.md) +- [Authorization](./Authorization.md) +- [Sessions](./Sessions.md) +- [Metrics](./Metrics.md) +- [Diagnostics](./Diagnostics.md) diff --git a/docs/gateway-process-design.md b/docs/GatewayProcessDesign.md similarity index 98% rename from docs/gateway-process-design.md rename to docs/GatewayProcessDesign.md index d6f7b2e..d7ac230 100644 --- a/docs/gateway-process-design.md +++ b/docs/GatewayProcessDesign.md @@ -314,7 +314,7 @@ Faulted -> Closed ``` -### State Rules +### State rules - `Creating`: session id and in-memory state exist, but no worker has launched. - `StartingWorker`: worker process launch is in progress. @@ -432,7 +432,7 @@ Recommended size limits: - reject zero-length payloads, - reject payloads larger than configured maximum before allocation. -### Envelope Rules +### Envelope rules Every message uses `WorkerEnvelope`: @@ -489,7 +489,7 @@ and nonce, waits for `WorkerReady`, and only then exposes `Ready` state. The read loop starts after readiness so the handshake has a single owner for its ordered frames. -### Read Loop +### Read loop The read loop: @@ -505,7 +505,7 @@ The read loop: If the pipe closes while the session is not closing, fault the session. -### Write Loop +### Write loop The write loop serializes all writes to the pipe. No other code should write to the pipe directly. @@ -972,3 +972,17 @@ The first gateway slice should implement: 17. Basic structured logs. This proves the process model before the full command surface is implemented. + +## Related Documentation + +- [MXAccess Worker Instance Detailed Design](./MxAccessWorkerInstanceDesign.md) +- [Worker Frame Protocol](./WorkerFrameProtocol.md) +- [Worker Process Launcher](./WorkerProcessLauncher.md) +- [Gateway Configuration](./GatewayConfiguration.md) +- [Sessions](./Sessions.md) +- [gRPC](./Grpc.md) +- [Authentication](./Authentication.md) +- [Authorization](./Authorization.md) +- [Metrics](./Metrics.md) +- [Diagnostics](./Diagnostics.md) +- [Gateway Testing](./GatewayTesting.md) diff --git a/docs/GatewayTesting.md b/docs/GatewayTesting.md index 42338f9..f7c83ce 100644 --- a/docs/GatewayTesting.md +++ b/docs/GatewayTesting.md @@ -161,6 +161,6 @@ dotnet test src/MxGateway.Tests/MxGateway.Tests.csproj - [Cross-Language Smoke Matrix](./CrossLanguageSmokeMatrix.md) - [Parity Fixture Matrix](./ParityFixtureMatrix.md) -- [Gateway Process Design](./gateway-process-design.md) +- [Gateway Process Design](./GatewayProcessDesign.md) - [Worker Frame Protocol](./WorkerFrameProtocol.md) -- [MXAccess Worker Instance Detailed Design](./mxaccess-worker-instance-design.md) +- [MXAccess Worker Instance Detailed Design](./MxAccessWorkerInstanceDesign.md) diff --git a/docs/Grpc.md b/docs/Grpc.md index e72f45e..d0c958b 100644 --- a/docs/Grpc.md +++ b/docs/Grpc.md @@ -31,6 +31,11 @@ A second gRPC service, `GalaxyRepositoryGrpcService`, is mapped alongside it. It `MxAccessGatewayService` derives from the generated `MxAccessGateway.MxAccessGatewayBase` and implements every RPC declared in `mxaccess_gateway.proto`. The proto contract itself is documented in [Contracts](./Contracts.md); this section covers only what the server-side handler does on top of that contract. +Public gRPC send and receive message sizes are configured from +`MxGateway:Protocol:MaxGrpcMessageBytes` (default 16 MiB). Official clients use +the same default so paged Galaxy browse replies and larger MXAccess payloads +fail consistently instead of depending on language-specific gRPC defaults. + ### `OpenSession` `OpenSession` validates the request, asks `ISessionManager` to open a session under the caller's identity, and returns a reply that advertises both protocol versions and the capabilities the gateway supports. Capability strings are static because the gateway has a fixed feature set per build; clients use them as a forward-compatibility hint rather than runtime negotiation. @@ -211,7 +216,7 @@ if (!writer.TryWrite(publicEvent)) Under `FailFast` the session is faulted so subsequent commands return `FailedPrecondition`; the client must reopen. Under the default policy only the stream is dropped and the session continues to accept commands, leaving recovery to the client (typically a fresh `StreamEvents` call with an updated `AfterWorkerSequence`). Either way, the consumer side observes `StatusCode.ResourceExhausted` via the `EventQueueOverflow` mapping above. -### Cancellation and Cleanup +### Cancellation and cleanup The handler creates a linked cancellation token (`streamCts`) so that completing the consumer (client disconnect, error, or graceful end-of-stream) also cancels the producer. The `finally` block cancels the source, disposes the subscriber slot, awaits the producer (swallowing the expected cancellation), and emits `StreamDisconnected("Detached")` so dashboards see the disconnection regardless of cause. @@ -233,4 +238,4 @@ Because the interceptor runs before any handler, `MxAccessGatewayService` can sa - [Contracts](./Contracts.md) - [Sessions](./Sessions.md) - [Authorization](./Authorization.md) -- [Gateway Process Design](./gateway-process-design.md) +- [Gateway Process Design](./GatewayProcessDesign.md) diff --git a/docs/implementation-plan-clients.md b/docs/ImplementationPlanClients.md similarity index 92% rename from docs/implementation-plan-clients.md rename to docs/ImplementationPlanClients.md index 48b967c..51a1c34 100644 --- a/docs/implementation-plan-clients.md +++ b/docs/ImplementationPlanClients.md @@ -5,13 +5,13 @@ first slice is stable enough to generate contracts and run smoke tests. Primary designs: -- `docs/client-libraries-design.md` -- `docs/clients-dotnet-csharp-design.md` -- `docs/clients-golang-design.md` -- `docs/clients-rust-design.md` -- `docs/clients-python-design.md` -- `docs/clients-java-design.md` -- `docs/toolchain-links.md` +- `docs/ClientLibrariesDesign.md` +- `clients/dotnet/DotnetClientDesign.md` +- `clients/go/GoClientDesign.md` +- `clients/rust/RustClientDesign.md` +- `clients/python/PythonClientDesign.md` +- `clients/java/JavaClientDesign.md` +- `docs/ToolchainLinks.md` ## Shared Milestone: client-contracts-and-fixtures @@ -382,6 +382,15 @@ Deliverables: Acceptance criteria: - new developer can build each client from a clean checkout using - `docs/toolchain-links.md`, + `docs/ToolchainLinks.md`, - generated code command is documented for every language. + +## Related Documentation + +- [Implementation Plan Index](./ImplementationPlanIndex.md) +- [Client Libraries Detailed Design](./ClientLibrariesDesign.md) +- [Client Proto Generation](./ClientProtoGeneration.md) +- [Client Behavior Fixtures](./ClientBehaviorFixtures.md) +- [Client Packaging](./ClientPackaging.md) +- [Cross-Language Smoke Matrix](./CrossLanguageSmokeMatrix.md) diff --git a/docs/implementation-plan-gateway.md b/docs/ImplementationPlanGateway.md similarity index 94% rename from docs/implementation-plan-gateway.md rename to docs/ImplementationPlanGateway.md index 225dfdc..69e70c9 100644 --- a/docs/implementation-plan-gateway.md +++ b/docs/ImplementationPlanGateway.md @@ -6,10 +6,10 @@ streaming, metrics, dashboard, tests, and operational hooks. Primary designs: -- `docs/gateway-process-design.md` -- `docs/gateway-dashboard-design.md` -- `docs/design-decisions.md` -- `docs/toolchain-links.md` +- `docs/GatewayProcessDesign.md` +- `docs/GatewayDashboardDesign.md` +- `docs/DesignDecisions.md` +- `docs/ToolchainLinks.md` ## Milestone: gateway-foundation @@ -509,3 +509,17 @@ Acceptance criteria: - worker exits, - artifacts stay in temp directories. + +## Related Documentation + +- [Implementation Plan Index](./ImplementationPlanIndex.md) +- [Gateway Process Detailed Design](./GatewayProcessDesign.md) +- [Gateway Configuration](./GatewayConfiguration.md) +- [Sessions](./Sessions.md) +- [gRPC](./Grpc.md) +- [Authentication](./Authentication.md) +- [Authorization](./Authorization.md) +- [Gateway Dashboard Detailed Design](./GatewayDashboardDesign.md) +- [Gateway Testing](./GatewayTesting.md) +- [Metrics](./Metrics.md) +- [Diagnostics](./Diagnostics.md) diff --git a/docs/implementation-plan-index.md b/docs/ImplementationPlanIndex.md similarity index 78% rename from docs/implementation-plan-index.md rename to docs/ImplementationPlanIndex.md index 58c88d3..47f6fb4 100644 --- a/docs/implementation-plan-index.md +++ b/docs/ImplementationPlanIndex.md @@ -17,9 +17,9 @@ Implementation order: Detailed plans: -- `docs/implementation-plan-gateway.md` -- `docs/implementation-plan-mxaccess-worker.md` -- `docs/implementation-plan-clients.md` +- `docs/ImplementationPlanGateway.md` +- `docs/ImplementationPlanMxAccessWorker.md` +- `docs/ImplementationPlanClients.md` ## Gitea Milestones @@ -91,10 +91,17 @@ Every implementation issue should meet this baseline: ## Toolchain -Use `docs/toolchain-links.md` for installed compiler/runtime paths. If a new +Use `docs/ToolchainLinks.md` for installed compiler/runtime paths. If a new terminal cannot find a recently installed tool, refresh PATH: ```powershell $env:Path = [Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [Environment]::GetEnvironmentVariable('Path','User') ``` + +## Related Documentation + +- [Gateway Implementation Plan](./ImplementationPlanGateway.md) +- [MXAccess Worker Implementation Plan](./ImplementationPlanMxAccessWorker.md) +- [Client Libraries Implementation Plan](./ImplementationPlanClients.md) +- [Toolchain Links](./ToolchainLinks.md) diff --git a/docs/implementation-plan-mxaccess-worker.md b/docs/ImplementationPlanMxAccessWorker.md similarity index 94% rename from docs/implementation-plan-mxaccess-worker.md rename to docs/ImplementationPlanMxAccessWorker.md index 055176c..c97e504 100644 --- a/docs/implementation-plan-mxaccess-worker.md +++ b/docs/ImplementationPlanMxAccessWorker.md @@ -7,9 +7,9 @@ and shutdown. Primary designs: -- `docs/mxaccess-worker-instance-design.md` -- `docs/design-decisions.md` -- `docs/toolchain-links.md` +- `docs/MxAccessWorkerInstanceDesign.md` +- `docs/DesignDecisions.md` +- `docs/ToolchainLinks.md` - `C:\Users\dohertj2\Desktop\mxaccess\docs\MXAccess-Public-API.md` ## Milestone: mxaccess-worker-foundation @@ -29,7 +29,7 @@ Deliverables: - reference generated worker contracts, - reference `ArchestrA.MXAccess.dll`, - create `src/MxGateway.Worker.Tests`, -- document MSBuild command from `docs/toolchain-links.md`. +- document MSBuild command from `docs/ToolchainLinks.md`. Acceptance criteria: @@ -453,3 +453,14 @@ Acceptance criteria: - each public method has planned parity fixture or documented gap, - gateway results preserve HRESULT/status/value/event shape. + +## Related Documentation + +- [Implementation Plan Index](./ImplementationPlanIndex.md) +- [MXAccess Worker Instance Detailed Design](./MxAccessWorkerInstanceDesign.md) +- [Worker Bootstrap](./WorkerBootstrap.md) +- [Worker STA](./WorkerSta.md) +- [Worker Conversion](./WorkerConversion.md) +- [Worker Frame Protocol](./WorkerFrameProtocol.md) +- [Worker Process Launcher](./WorkerProcessLauncher.md) +- [Parity Fixture Matrix](./ParityFixtureMatrix.md) diff --git a/docs/Metrics.md b/docs/Metrics.md index 91fe6e1..81659f7 100644 --- a/docs/Metrics.md +++ b/docs/Metrics.md @@ -64,7 +64,7 @@ _eventStreamSendLatencyHistogram = _meter.CreateHistogram("mxgateway.eve | `mxgateway.commands.duration` | `method`, optional `category` | Command round-trip time. The `category` tag is added on failure so success and failure latencies stay distinguishable. | | `mxgateway.events.stream_send.duration` | `family` | Time spent writing each public event to the gRPC response stream in `MxAccessGatewayService.StreamEvents`. | -### Observable Gauges +### Observable gauges Observable gauges are pull-based; the `Meter` invokes the supplied callback whenever a listener samples it. Each callback re-acquires `_syncRoot` so the gauge value matches the snapshot taken at the same instant. @@ -201,10 +201,10 @@ metrics.RecordEventStreamSend(publicEvent.Family.ToString(), stopwatch.Elapsed); ## Dashboard Consumption -`Dashboard/DashboardSnapshotService.cs` calls `_metrics.GetSnapshot()` once per `GetSnapshot` invocation and projects it into the dashboard transport types together with the session registry view. The dashboard receives a single, internally consistent snapshot per tick rather than reading individual counters at separate times. See [Gateway Dashboard Design](./gateway-dashboard-design.md) and [Dashboard Interface Design](./DashboardInterfaceDesign.md) for the projection rules and wire format. +`Dashboard/DashboardSnapshotService.cs` calls `_metrics.GetSnapshot()` once per `GetSnapshot` invocation and projects it into the dashboard transport types together with the session registry view. The dashboard receives a single, internally consistent snapshot per tick rather than reading individual counters at separate times. See [Gateway Dashboard Design](./GatewayDashboardDesign.md) and [Dashboard Interface Design](./DashboardInterfaceDesign.md) for the projection rules and wire format. ## Related Documentation -- [Gateway Dashboard Design](./gateway-dashboard-design.md) +- [Gateway Dashboard Design](./GatewayDashboardDesign.md) - [Dashboard Interface Design](./DashboardInterfaceDesign.md) - [Sessions](./Sessions.md) diff --git a/docs/mxaccess-worker-instance-design.md b/docs/MxAccessWorkerInstanceDesign.md similarity index 98% rename from docs/mxaccess-worker-instance-design.md rename to docs/MxAccessWorkerInstanceDesign.md index 929c70d..99d88df 100644 --- a/docs/mxaccess-worker-instance-design.md +++ b/docs/MxAccessWorkerInstanceDesign.md @@ -36,7 +36,7 @@ installation: dotnet msbuild src\MxGateway.Worker\MxGateway.Worker.csproj /restore /p:Configuration=Debug /p:Platform=x86 ``` -`docs/toolchain-links.md` records the Visual Studio MSBuild executable for +`docs/ToolchainLinks.md` records the Visual Studio MSBuild executable for classic .NET Framework and COM interop builds: ```powershell @@ -836,3 +836,13 @@ The first worker slice should implement: This slice proves the worker can preserve the core MXAccess requirements: single-process isolation, STA ownership, message pumping, command execution, and event delivery. + +## Related Documentation + +- [Worker Bootstrap](./WorkerBootstrap.md) +- [Worker STA](./WorkerSta.md) +- [Worker Conversion](./WorkerConversion.md) +- [Worker Frame Protocol](./WorkerFrameProtocol.md) +- [Worker Process Launcher](./WorkerProcessLauncher.md) +- [Gateway Process Detailed Design](./GatewayProcessDesign.md) +- [Design Decisions](./DesignDecisions.md) diff --git a/docs/ParityFixtureMatrix.md b/docs/ParityFixtureMatrix.md index 6360c57..08bb1e6 100644 --- a/docs/ParityFixtureMatrix.md +++ b/docs/ParityFixtureMatrix.md @@ -98,5 +98,5 @@ normal unit tests only validate the repository fixture shape. ## Related Documentation - [Gateway Testing](./GatewayTesting.md) -- [MXAccess Worker Instance Detailed Design](./mxaccess-worker-instance-design.md) +- [MXAccess Worker Instance Detailed Design](./MxAccessWorkerInstanceDesign.md) - [Protobuf Contracts](./Contracts.md) diff --git a/docs/Sessions.md b/docs/Sessions.md index b4cbf9d..bb4696e 100644 --- a/docs/Sessions.md +++ b/docs/Sessions.md @@ -178,9 +178,9 @@ The order — fault, deregister, dispose, release slot, record metric, log, reth While `Ready`, callers reach the worker through `SessionManager.InvokeAsync` or `ReadEventsAsync`. Both delegate to `GatewaySession`, which checks the state under lock and updates `LastClientActivityAt` on every invocation. `GatewaySession` also exposes typed bulk helpers (`AddItemBulkAsync`, `SubscribeBulkAsync`, etc.) that wrap `WorkerCommand` round-trips and translate non-`Ok` `ProtocolStatus` replies into `SessionManagerException` with `SessionNotReady`. -Event streaming uses `AttachEventSubscriber` which returns a disposable lease. When `allowMultipleSubscribers` is false the second attach throws `EventSubscriberAlreadyActive`; this prevents two gRPC streams from racing on the same worker event channel. +Event streaming uses `AttachEventSubscriber` which returns a disposable lease. When `allowMultipleSubscribers` is false the second attach throws `EventSubscriberAlreadyActive`; this prevents two gRPC streams from racing on the same worker event channel. Active event subscribers keep the session lease from expiring until the stream is disposed. -`ExtendLease` and `IsLeaseExpired` cooperate with `SessionManager.CloseExpiredLeasesAsync`, which iterates a registry snapshot and closes any session whose lease has expired with `LeaseExpiredReason`. +Sessions open with `MxGateway:Sessions:DefaultLeaseSeconds` (default 1800) added to the open timestamp. Unary client activity refreshes the lease by the same duration. `ExtendLease` and `IsLeaseExpired` cooperate with `SessionManager.CloseExpiredLeasesAsync`, which iterates a registry snapshot and closes any session whose lease has expired with `LeaseExpiredReason`. `SessionLeaseMonitorHostedService` runs that sweep every `MxGateway:Sessions:LeaseSweepIntervalSeconds` seconds (default 30). ### Close @@ -266,6 +266,6 @@ The registry must be a singleton because its `ConcurrentDictionary` is the sourc ## Related Documentation -- [Gateway Process Design](./gateway-process-design.md) +- [Gateway Process Design](./GatewayProcessDesign.md) - [Gateway Configuration](./GatewayConfiguration.md) - [Worker Process Launcher](./WorkerProcessLauncher.md) diff --git a/docs/toolchain-links.md b/docs/ToolchainLinks.md similarity index 97% rename from docs/toolchain-links.md rename to docs/ToolchainLinks.md index 5c2be4a..54f17ae 100644 --- a/docs/toolchain-links.md +++ b/docs/ToolchainLinks.md @@ -170,3 +170,9 @@ These checks passed after installation: - `javac` compile of a temporary Java class. - Python imports for `grpc`, `grpc_tools`, and `pytest`. + +## Related Documentation + +- [Implementation Plan Index](./ImplementationPlanIndex.md) +- [Client Proto Generation](./ClientProtoGeneration.md) +- [Client Packaging](./ClientPackaging.md) diff --git a/docs/WorkerConversion.md b/docs/WorkerConversion.md index 24a4bc9..ece0c32 100644 --- a/docs/WorkerConversion.md +++ b/docs/WorkerConversion.md @@ -257,6 +257,6 @@ The exception is distinct from `COMException` so the worker's command pipeline c ## Related Documentation -- [MXAccess Worker Instance Design](./mxaccess-worker-instance-design.md) +- [MXAccess Worker Instance Design](./MxAccessWorkerInstanceDesign.md) - [Contracts](./Contracts.md) - [Worker STA Threading](./WorkerSta.md) diff --git a/docs/WorkerFrameProtocol.md b/docs/WorkerFrameProtocol.md index 7a14a12..88de84a 100644 --- a/docs/WorkerFrameProtocol.md +++ b/docs/WorkerFrameProtocol.md @@ -50,5 +50,5 @@ dotnet build src/MxGateway.Server/MxGateway.Server.csproj ## Related Documentation -- [Gateway Process Detailed Design](./gateway-process-design.md) +- [Gateway Process Detailed Design](./GatewayProcessDesign.md) - [Protobuf Contracts](./Contracts.md) diff --git a/docs/WorkerProcessLauncher.md b/docs/WorkerProcessLauncher.md index e6c74ba..9f41bd6 100644 --- a/docs/WorkerProcessLauncher.md +++ b/docs/WorkerProcessLauncher.md @@ -71,5 +71,5 @@ dotnet build src/MxGateway.Server/MxGateway.Server.csproj ## Related Documentation -- [Gateway Process Detailed Design](./gateway-process-design.md) +- [Gateway Process Detailed Design](./GatewayProcessDesign.md) - [Worker Frame Protocol](./WorkerFrameProtocol.md) diff --git a/docs/WorkerSta.md b/docs/WorkerSta.md index 8160074..54e9376 100644 --- a/docs/WorkerSta.md +++ b/docs/WorkerSta.md @@ -152,6 +152,6 @@ finally ## Related Documentation -- [MXAccess worker instance design](./mxaccess-worker-instance-design.md) +- [MXAccess worker instance design](./MxAccessWorkerInstanceDesign.md) - [Worker conversion](./WorkerConversion.md) - [Worker bootstrap](./WorkerBootstrap.md) diff --git a/gateway.md b/gateway.md index 0ffba5b..931e0ee 100644 --- a/gateway.md +++ b/gateway.md @@ -42,25 +42,25 @@ transport. Detailed follow-up docs: -- `docs/gateway-process-design.md` covers the .NET 10 gateway process, +- `docs/GatewayProcessDesign.md` covers the .NET 10 gateway process, session manager, worker supervision, gRPC API, event streaming, fault model, security, observability, and test strategy. - `docs/WorkerFrameProtocol.md` covers the gateway-side named-pipe frame reader/writer and `WorkerEnvelope` validation rules. - `docs/WorkerProcessLauncher.md` covers worker executable validation, process launch arguments, nonce handling, and startup cleanup behavior. -- `docs/mxaccess-worker-instance-design.md` covers each .NET Framework 4.8 x86 +- `docs/MxAccessWorkerInstanceDesign.md` covers each .NET Framework 4.8 x86 MXAccess worker instance, including STA ownership, message pumping, COM lifetime, command dispatch, event sinks, conversion, and shutdown. -- `docs/design-decisions.md` records current v1 choices, including API-key +- `docs/DesignDecisions.md` records current v1 choices, including API-key authentication in gateway-owned SQLite and the concrete installed MXAccess COM class details from `C:\Users\dohertj2\Desktop\mxaccess`. -- `docs/gateway-dashboard-design.md` covers the Blazor Server and Bootstrap +- `docs/GatewayDashboardDesign.md` covers the Blazor Server and Bootstrap dashboard for live gateway/session/worker status. -- `docs/client-libraries-design.md` covers shared design requirements for +- `docs/ClientLibrariesDesign.md` covers shared design requirements for official gRPC client libraries, test CLIs, and tests for .NET C#, Go, Rust, Python, and Java. -- `docs/implementation-plan-index.md` links the detailed implementation plans +- `docs/ImplementationPlanIndex.md` links the detailed implementation plans and recommended Gitea milestones/issues. - `docs/GalaxyRepository.md` covers the read-only Galaxy Repository browse RPCs that let clients enumerate the deployed object hierarchy and dynamic @@ -950,7 +950,7 @@ backend = mxaccess-worker ## Open Questions -Current v1 decisions are recorded in `docs/design-decisions.md`. +Current v1 decisions are recorded in `docs/DesignDecisions.md`. Resolved for v1: diff --git a/scripts/discover-testmachine-tags.ps1 b/scripts/discover-testmachine-tags.ps1 index 203a89e..29b5e28 100644 --- a/scripts/discover-testmachine-tags.ps1 +++ b/scripts/discover-testmachine-tags.ps1 @@ -36,7 +36,7 @@ if ($Attributes.Count -eq 0) { } if (-not (Test-Path -LiteralPath $SqlcmdPath)) { - throw "sqlcmd was not found at '$SqlcmdPath'. Pass -SqlcmdPath or update docs/toolchain-links.md." + throw "sqlcmd was not found at '$SqlcmdPath'. Pass -SqlcmdPath or update docs/ToolchainLinks.md." } $attributeList = ($Attributes | ForEach-Object { diff --git a/scripts/publish-client-proto-inputs.ps1 b/scripts/publish-client-proto-inputs.ps1 index 4457cda..ab2814f 100644 --- a/scripts/publish-client-proto-inputs.ps1 +++ b/scripts/publish-client-proto-inputs.ps1 @@ -22,7 +22,7 @@ function Resolve-Protoc { return $documentedPath } - throw "Could not find protoc.exe. See docs/toolchain-links.md for the documented protobuf toolchain path." + throw "Could not find protoc.exe. See docs/ToolchainLinks.md for the documented protobuf toolchain path." } function Ensure-Directory {