Files
lmxopcua/docs/v2/dev-environment.md
Joseph Doherty 4903a19ec9 Add data-path ACL design (acl-design.md, closes corrections B1) + dev-environment inventory and setup plan (dev-environment.md), and remove consumer cutover from OtOpcUa v2 scope.
ACL design defines NodePermissions bitmask flags covering Browse / Read / Subscribe / HistoryRead / WriteOperate / WriteTune / WriteConfigure / AlarmRead / AlarmAcknowledge / AlarmConfirm / AlarmShelve / MethodCall plus common bundles (ReadOnly / Operator / Engineer / Admin); 6-level scope hierarchy (Cluster / Namespace / UnsArea / UnsLine / Equipment / Tag) with default-deny + additive grants and Browse-implication on ancestors; per-LDAP-group grants in a new generation-versioned NodeAcl table edited via the same draft → diff → publish → rollback boundary as every other content table; per-session permission-trie evaluator with O(depth × group-count) cost cached for the lifetime of the session and rebuilt on generation-apply or LDAP group cache expiry; cluster-create workflow seeds a default ACL set matching the v1 LmxOpcUa LDAP-role-to-permission map for v1 → v2 consumer migration parity; Admin UI ACL tab with two views (by LDAP group, by scope), bulk-grant flow, and permission simulator that lets operators preview "as user X" effective permissions across the cluster's UNS tree before publishing; explicit Deny deferred to v2.1 since verbose grants suffice at v2.0 fleet sizes; only denied OPC UA operations are audit-logged (not allowed ones — would dwarf the audit log). Schema doc gains the NodeAcl table with cross-cluster invariant enforcement and same-generation FK validation; admin-ui.md gains the ACLs tab; phase-1 doc gains Task E.9 wiring this through Stream E plus a NodeAcl entry in Task B.1's DbContext list.

Dev-environment doc inventories every external resource the v2 build needs across two tiers per decision #99 — inner-loop (in-process simulators on developer machines: SQL Server local or container, GLAuth at C:\publish\glauth\, local dev Galaxy) and integration (one dedicated Windows host with Docker Desktop on WSL2 backend so TwinCAT XAR VM can run in Hyper-V alongside containerized oitc/modbus-server, plus WSL2-hosted Snap7 and ab_server, plus OPC Foundation reference server, plus FOCAS TestStub and FaultShim) — with concrete container images, ports, default dev credentials (clearly marked dev-only since production uses Integrated Security / gMSA per decision #46), bootstrap order for both tiers, network topology diagram, test data seed locations, and operational risks (TwinCAT trial expiry automation, Docker pricing, integration host SPOF mitigation, per-developer GLAuth config sync, Aveva license scoping that keeps Galaxy tests on developer machines and off the shared host).

Removes consumer cutover (ScadaBridge / Ignition / System Platform IO) from OtOpcUa v2 scope per decision #136 — owned by a separate integration / operations team, tracked in 3-year-plan handoff §"Rollout Posture" and corrections §C5; OtOpcUa team's scope ends at Phase 5. Updates implementation/overview.md phase index to drop the "6+" row and add an explicit "OUT of v2 scope" callout; updates phase-1 and phase-2 docs to reframe cutover as integration-team-owned rather than future-phase numbered.

Decisions #129–137 added: ACL model (#129), NodeAcl generation-versioned (#130), v1-compatibility seed (#131), denied-only audit logging (#132), two-tier dev environment (#133), Docker WSL2 backend for TwinCAT VM coexistence (#134), TwinCAT VM centrally managed / Galaxy on dev machines only (#135), cutover out of v2 scope (#136), dev credentials documented openly (#137).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 11:58:33 -04:00

20 KiB
Raw Blame History

Development Environment — OtOpcUa v2

Status: DRAFT — concrete inventory + setup plan for every external resource the v2 build needs. Companion to test-data-sources.md (which catalogues the simulator/stub strategy per driver) and implementation/overview.md (which references the dev environment in entry-gate checklists).

Branch: v2 Created: 2026-04-17

Scope

Every external resource a developer needs on their machine, plus the dedicated integration host that runs the heavier simulators per CI tiering decision #99. Includes Docker container images, ports, default credentials (dev only — production overrides documented), and ownership.

Not in scope here: production deployment topology (separate doc when v2 ships), CI pipeline configuration (separate ops concern), individual developer's IDE / editor preferences.

Two Environment Tiers

Per decision #99:

Tier Purpose Where it runs Resources
PR-CI / inner-loop dev Fast, runs on minimal Windows + Linux build agents and developer laptops Each developer's machine; CI runners Pure-managed in-process simulators (NModbus, OPC Foundation reference server, FOCAS TCP stub from test project). No Docker, no VMs.
Nightly / integration CI Full driver-stack validation against real wire protocols One dedicated Windows host with Docker Desktop + Hyper-V + a TwinCAT XAR VM All Docker simulators (oitc/modbus-server, ab_server, Snap7), TwinCAT XAR VM, Galaxy.Host installer + dev Galaxy access, FOCAS TCP stub binary, FOCAS FaultShim assembly

The tier split keeps developer onboarding fast (no Docker required for first build) while concentrating the heavy simulator setup on one machine the team maintains.

Resource Inventory

A. Always-required (every developer + integration host)

Resource Purpose Type Default port Default credentials Owner
.NET 10 SDK Build all .NET 10 x64 projects OS install n/a n/a Developer
.NET Framework 4.8 SDK + targeting pack Build Driver.Galaxy.Host (Phase 2+) Windows install n/a n/a Developer
Visual Studio 2022 17.8+ or Rider 2024+ IDE (any C# IDE works; these are the supported configs) OS install n/a n/a Developer
Git Source control OS install n/a n/a Developer
PowerShell 7.4+ Compliance scripts (phase-N-compliance.ps1) OS install n/a n/a Developer
Repo clones lmxopcua (this repo), scadalink-design (UI/auth reference per memory file scadalink_reference.md), 3yearplan (handoff + corrections) Git clone n/a n/a Developer

B. Inner-loop dev (developer machines + PR-CI)

Resource Purpose Type Default port Default credentials Owner
SQL Server 2022 dev edition Central config DB; integration tests against Configuration project Local install OR Docker container mcr.microsoft.com/mssql/server:2022-latest 1433 sa / OtOpcUaDev_2026! (dev only — production uses Integrated Security or gMSA per decision #46) Developer (per machine)
GLAuth (LDAP server) Admin UI authentication tests; data-path ACL evaluation tests Local binary at C:\publish\glauth\ per existing CLAUDE.md 3893 (LDAP) / 3894 (LDAPS) Service principal: cn=admin,dc=otopcua,dc=local / OtOpcUaDev_2026!; test users defined in GLAuth config Developer (per machine)
Local dev Galaxy (Aveva System Platform) Galaxy driver tests; v1 IntegrationTests parity Existing on dev box per CLAUDE.md n/a (local COM) Windows Auth Developer (already present per project setup)

C. Integration host (one dedicated Windows machine the team shares)

Resource Purpose Type Default port Default credentials Owner
Docker Desktop for Windows Host for containerized simulators Install (Hyper-V required; not compatible with TwinCAT runtime — see TwinCAT row below for the workaround) n/a Integration host admin
oitc/modbus-server Modbus TCP simulator (per test-data-sources.md §1) Docker container 502 (Modbus TCP) n/a (no auth in protocol) Integration host admin
ab_server (libplctag binary) AB CIP + AB Legacy simulator (per test-data-sources.md §2 + §3) Native binary built from libplctag source; runs in a separate VM or host since it conflicts with Docker Desktop's Hyper-V if run on bare metal 44818 (CIP) n/a Integration host admin
Snap7 Server S7 simulator (per test-data-sources.md §4) Native binary; runs in a separate VM or in WSL2 to avoid Hyper-V conflict 102 (ISO-TCP) n/a Integration host admin
TwinCAT XAR runtime VM TwinCAT ADS testing (per test-data-sources.md §5; Beckhoff XAR cannot coexist with Hyper-V on the same OS) Hyper-V VM with Windows + TwinCAT XAR installed under 7-day renewable trial 48898 (ADS over TCP) TwinCAT default route credentials configured per Beckhoff docs Integration host admin
OPC Foundation reference server OPC UA Client driver test source (per test-data-sources.md §"OPC UA Client") Built from OPCFoundation/UA-.NETStandard ConsoleReferenceServer project 62541 (default for the reference server) Anonymous + Username (user1 / password1) per the reference server's built-in user list Integration host admin
FOCAS TCP stub (Driver.Focas.TestStub) FOCAS functional testing (per test-data-sources.md §6) Local .NET 10 console app from this repo 8193 (FOCAS) n/a Developer / integration host (run on demand)
FOCAS FaultShim (Driver.Focas.FaultShim) FOCAS native-fault injection (per test-data-sources.md §6) Test-only native DLL named Fwlib64.dll, loaded via DLL search path in the test fixture n/a (in-process) n/a Developer / integration host (test-only)

D. Cloud / external services

Resource Purpose Type Access Owner
Gitea at gitea.dohertylan.com Hosts lmxopcua, 3yearplan, scadalink-design repos HTTPS git Existing org credentials Org IT
Anthropic API (for Codex adversarial reviews) /codex:adversarial-review invocations during exit gates HTTPS via Codex companion script API key in developer's ~/.claude/... config Developer (per codex:setup skill)

Network Topology (integration host)

                 ┌────────────────────────────────────────┐
                 │   Integration Host (Windows + Docker)  │
                 │                                        │
                 │   Docker Desktop (Linux containers):   │
                 │   ┌───────────────────────────────┐    │
                 │   │  oitc/modbus-server  :502/tcp │    │
                 │   └───────────────────────────────┘    │
                 │                                        │
                 │   WSL2 (Snap7 + ab_server, separate    │
                 │   from Docker Desktop's HyperV):       │
                 │   ┌───────────────────────────────┐    │
                 │   │  snap7-server        :102/tcp │    │
                 │   │  ab_server         :44818/tcp │    │
                 │   └───────────────────────────────┘    │
                 │                                        │
                 │   Hyper-V VM (Windows + TwinCAT XAR):  │
                 │   ┌───────────────────────────────┐    │
                 │   │  TwinCAT XAR        :48898    │    │
                 │   └───────────────────────────────┘    │
                 │                                        │
                 │   Native processes:                    │
                 │   ┌───────────────────────────────┐    │
                 │   │  ConsoleReferenceServer :62541│    │
                 │   │  FOCAS TestStub          :8193│    │
                 │   └───────────────────────────────┘    │
                 │                                        │
                 │   SQL Server 2022 (local install):     │
                 │   ┌───────────────────────────────┐    │
                 │   │  OtOpcUaConfig_Test    :1433  │    │
                 │   └───────────────────────────────┘    │
                 └────────────────────────────────────────┘
                              ▲
                              │ tests connect via the host's hostname or 127.0.0.1
                              │
                 ┌────────────────────────────────────────┐
                 │   Developer / CI machine running       │
                 │   `dotnet test --filter Category=...`  │
                 └────────────────────────────────────────┘

Bootstrap Order — Inner-loop Developer Machine

Order matters because some installs have prerequisites. ~3060 min total on a fresh machine.

  1. Install .NET 10 SDK (https://dotnet.microsoft.com/) — required to build anything
  2. Install .NET Framework 4.8 SDK + targeting pack — only needed when starting Phase 2 (Galaxy.Host); skip for Phase 01 if not yet there
  3. Install Git + PowerShell 7.4+
  4. Clone repos:
    git clone https://gitea.dohertylan.com/dohertj2/lmxopcua.git
    git clone https://gitea.dohertylan.com/dohertj2/scadalink-design.git
    git clone https://gitea.dohertylan.com/dohertj2/3yearplan.git
    
  5. Install SQL Server 2022 dev edition (local install) OR start the Docker container (see Resource B):
    docker run --name otopcua-mssql -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=OtOpcUaDev_2026!" `
      -p 1433:1433 -d mcr.microsoft.com/mssql/server:2022-latest
    
  6. Install GLAuth at C:\publish\glauth\ per existing CLAUDE.md instructions; populate glauth-otopcua.cfg with the test users + groups (template in docs/v2/dev-environment-glauth-config.md — to be added in the setup task)
  7. Run dotnet restore in the lmxopcua repo
  8. Run dotnet build ZB.MOM.WW.OtOpcUa.slnx (post-Phase-0) or ZB.MOM.WW.LmxOpcUa.slnx (pre-Phase-0) — verifies the toolchain
  9. Run dotnet test with the inner-loop filter — should pass on a fresh machine

Bootstrap Order — Integration Host

Order matters more here because of Hyper-V conflicts. ~half-day on a fresh machine.

  1. Install Windows Server 2022 or Windows 11 Pro (Hyper-V capable)
  2. Enable Hyper-V + WSL2
  3. Install Docker Desktop for Windows, configure to use WSL2 backend (NOT Hyper-V backend — leaves Hyper-V free for the TwinCAT XAR VM)
  4. Set up WSL2 distro (Ubuntu 22.04 LTS) for native Linux binaries that conflict with Docker Desktop
  5. Pull / start Modbus simulator:
    docker run -d --name modbus-sim -p 502:502 -v ${PWD}/modbus-config.yaml:/server_config.yaml oitc/modbus-server
    
  6. Build + start ab_server (in WSL2):
    git clone https://github.com/libplctag/libplctag
    cd libplctag/src/tests
    make ab_server
    ./ab_server --plc=ControlLogix --port=44818  # default tags loaded from a config file
    
  7. Build + start Snap7 Server (in WSL2):
  8. Set up TwinCAT XAR VM:
    • Create a Hyper-V VM (Gen 2, Windows 11)
    • Install TwinCAT 3 XAE + XAR (download from Beckhoff, free for dev/test)
    • Activate the 7-day trial; document the rotation schedule
    • Configure ADS routes for the integration host to reach the VM
    • Deploy the test PLC project from test-data-sources.md §5 ("a tiny test project — MAIN (PLC code) + GVL")
  9. Build + start OPC Foundation reference server:
    git clone https://github.com/OPCFoundation/UA-.NETStandard
    cd UA-.NETStandard/Applications/ConsoleReferenceServer
    dotnet run --port 62541
    
  10. Install SQL Server 2022 dev edition (or run the Docker container as on developer machines)
  11. Build + run FOCAS TestStub (from this repo, post-Phase-5):
    dotnet run --project src/ZB.MOM.WW.OtOpcUa.Driver.Focas.TestStub -- --port 8193
    
  12. Verify by running dotnet test --filter Category=Integration from a developer machine pointed at the integration host

Credential Management

Dev environment defaults

The defaults in this doc are for dev environments only. They're documented here so a developer can stand up a working setup without hunting; they're not secret.

Production overrides

For any production deployment:

  • SQL Server: Integrated Security with gMSA (decision #46) — never SQL login with shared password
  • LDAP: production GLAuth or AD instance with proper service principal
  • TwinCAT: paid license (per-runtime), not the 7-day trial
  • All other services: deployment-team's credential management process; documented in deployment-guide.md (separate doc, post-v2.0)

Storage

For dev defaults:

  • SQL Server SA password: stored in each developer's local appsettings.Development.json (gitignored)
  • GLAuth bind DN/password: stored in glauth-otopcua.cfg (gitignored)
  • Docker secrets / volumes: developer-local

For production:

  • gMSA / cert-mapped principals — no passwords stored anywhere
  • Per-NodeId credentials in ClusterNodeCredential table (per decision #83)
  • Admin app uses LDAP (no SQL credential at all on the user-facing side)

Test Data Seed

Each environment needs a baseline data set so cross-developer tests are reproducible. Lives in tests/ZB.MOM.WW.OtOpcUa.IntegrationTests/SeedData/:

  • GLAuth users: test-readonly@otopcua.local (in OtOpcUaReadOnly), test-operator@otopcua.local (OtOpcUaWriteOperate + OtOpcUaAlarmAck), test-fleetadmin@otopcua.local (OtOpcUaAdmins)
  • Central config DB: a seed cluster TEST-CLUSTER-01 with 1 node + 1 namespace + 0 drivers (other tests add drivers)
  • Modbus sim: YAML config preloading the addresses from test-data-sources.md §1 (HR 09 constants, ramp at HR 100, etc.)
  • TwinCAT XAR: the test PLC project deployed; symbols match test-data-sources.md §5
  • OPC Foundation reference server: starts with built-in test address space; tests don't modify it

Seeds are idempotent (re-runnable) and gitignored where they contain credentials.

Setup Plan (executable)

Step 1 — Inner-loop dev environment (each developer, ~1 day with documentation)

Owner: developer Prerequisite: Bootstrap order steps 19 above Acceptance:

  • dotnet test ZB.MOM.WW.OtOpcUa.slnx passes
  • A test that touches the central config DB succeeds (proves SQL Server reachable)
  • A test that authenticates against GLAuth succeeds (proves LDAP reachable)

Step 2 — Integration host (one-time, ~1 week)

Owner: DevOps lead Prerequisite: dedicated Windows machine, hardware specs ≥ 8 cores / 32 GB RAM / 500 GB SSD Acceptance:

  • Each simulator (Modbus, AB, S7, TwinCAT, OPC UA reference) responds to a probe from a developer machine
  • A nightly CI job runs dotnet test --filter Category=Integration against the integration host and passes
  • Service-account permissions reviewed by security lead

Step 3 — TwinCAT XAR VM trial rotation automation (one-time, half-day)

Owner: DevOps lead Prerequisite: Step 2 complete Acceptance:

  • A scheduled task on the integration host either re-activates the 7-day trial automatically OR alerts the team 24h before expiry; cycle tested

Step 4 — Per-developer GLAuth config sync (recurring, when test users change)

Owner: developer (each) Acceptance:

  • A script in the repo (scripts/sync-glauth-dev-config.ps1) updates the local GLAuth config from a template; documented in CLAUDE.md
  • Test users defined in the template work on every developer machine

Step 5 — Docker simulator config (per-developer, ~30 min)

Owner: developer (each) Acceptance:

  • The Modbus simulator container is reachable from 127.0.0.1:502 from the developer's test runner (only needed if the developer is debugging Modbus driver work; not required for Phase 0/1)

Step 6 — Codex companion setup (per-developer, ~5 min)

Owner: developer (each) Acceptance:

  • /codex:setup skill confirms readiness; /codex:adversarial-review works against a small test diff

Operational Risks

Risk Mitigation
TwinCAT 7-day trial expires mid-CI run Step 3 automation; alert before expiry; license budget approved as fallback for production-grade pre-release validation
Docker Desktop license terms change for org use Track Docker pricing; budget approved or fall back to Podman if license becomes blocking
Integration host single point of failure Document the setup so a second host can be provisioned in <2 days; test fixtures pin to a hostname so failover changes one DNS entry
GLAuth dev config drifts between developers Sync script + template (Step 4) keep configs aligned; periodic review
Galaxy / MXAccess licensing for non-dev-machine Galaxy stays on the dev machines that already have Aveva licenses; integration host does NOT run Galaxy (Galaxy.Host integration tests run on the dev box, not the shared host)
Long-lived dev env credentials in dev appsettings.Development.json Gitignored; documented as dev-only; production never uses these

Decisions to Add to plan.md

# Decision Rationale
133 Two-tier dev environment: inner-loop (in-process simulators on developer machines) + integration (Docker / VM / native simulators on a single dedicated Windows host) Per decision #99. Concrete inventory + setup plan in dev-environment.md
134 Docker Desktop with WSL2 backend (not Hyper-V backend) on integration host so TwinCAT XAR VM can run in Hyper-V alongside Docker TwinCAT runtime cannot coexist with Hyper-V-mode Docker Desktop; WSL2 backend leaves Hyper-V free for the XAR VM. Documented operational constraint
135 TwinCAT XAR runs only in a dedicated VM on the integration host; developer machines do NOT run XAR locally The 7-day trial reactivation needs centralized management; the VM is shared infrastructure
136 Galaxy / MXAccess testing happens on developer machines that have local Aveva installs, NOT on the shared integration host Aveva licensing scoped to dev workstations; integration host doesn't carry the license. v1 IntegrationTests parity (Phase 2) runs on developer boxes.
137 Dev env credentials are documented openly in dev-environment.md; production credentials use Integrated Security / gMSA per decision #46 Dev defaults are not secrets; they're convenience. Production never uses these values