# ScadaBridge Env2 Docker Infrastructure

A second Docker deployment of a minimal ScadaBridge cluster topology, designed to run **concurrently with** the primary `docker/` stack so the Transport (#24) feature can be exercised end-to-end across two real environments.

See [`docs/plans/2026-05-24-second-environment-design.md`](../docs/plans/2026-05-24-second-environment-design.md) for the design rationale.

## Cluster Topology

```
              ┌───────────────────┐
              │  Traefik LB :9100 │  ◄── CLI / Browser
              │  Dashboard :8181  │
              └────────┬──────────┘
                       │ routes to active node
┌──────────────────────┼──────────────────────────────┐
│            Env2 Central Cluster                     │
│                                                     │
│  ┌─────────────────┐     ┌─────────────────┐        │
│  │ env2-central-a   │◄──►│ env2-central-b   │       │
│  │  Web UI :9101    │     │  Web UI :9102    │       │
│  │  Akka   :9111    │     │  Akka   :9112    │       │
│  └────────┬─────────┘     └─────────────────┘       │
│           │                                         │
└───────────┼─────────────────────────────────────────┘
            │ Akka.NET Remoting
            ▼
┌────────────────────┐
│  Env2 Site-X       │
│  (Env2 Site X)     │
│                    │
│  node-a ◄──► node-b│
│  Akka :9121 :9122  │
│  gRPC :9123 :9124  │
└────────────────────┘
```

## Port Allocation

Env2 host ports are the primary's ports + 100. Both stacks can run simultaneously.

| Node | Container Name | Host Web | Host Akka | Host gRPC | Internal |
|------|----------------|----------|-----------|-----------|----------|
| Traefik LB | `scadabridge-env2-traefik` | 9100 | — | — | 80 (proxy), 8080 (dashboard host:8181) |
| Central A | `scadabridge-env2-central-a` | 9101 | 9111 | — | 5000 (web), 8081 (Akka) |
| Central B | `scadabridge-env2-central-b` | 9102 | 9112 | — | 5000 (web), 8081 (Akka) |
| Site-X A | `scadabridge-env2-site-x-a` | — | 9121 | 9123 | 8082 (Akka), 8083 (gRPC) |
| Site-X B | `scadabridge-env2-site-x-b` | — | 9122 | 9124 | 8082 (Akka), 8083 (gRPC) |

## Shared Infrastructure

Env2 attaches to the existing `scadabridge-net` Docker bridge network and reuses these primary infra containers:

| Service | Container | What env2 uses it for |
|---------|-----------|-----------------------|
| MS SQL | `scadabridge-mssql` | Env2-specific databases `ScadaBridgeConfig2` / `ScadaBridgeMachineData2` |
| LDAP | `scadabridge-ldap` | Authentication (same test users) |
| SMTP | `scadabridge-smtp` | Notification capture in Mailpit (env2 emails distinguishable by `FromAddress`) |
| OPC UA | `scadabridge-opcua` | Simulated tags for site-x data connections |
| REST API | `scadabridge-restapi` | External REST API testing |

## Commands

### First-Time Setup

```bash
# 1. Make sure primary infra is up (creates scadabridge-net, scadabridge-mssql, etc.)
cd infra && docker compose up -d && cd ..

# 2. Build image + create env2 databases + deploy env2 containers
bash docker-env2/deploy.sh

# 3. Seed env2's single test site (first-time only)
bash docker-env2/seed-sites.sh
```

The seed also creates a **native alarm source demo**: a `MxAlarmDemo` template with a
`GalaxyAlarms` native alarm source bound to the shared MxGateway connection, plus a
deployed `MxAlarmDemo-1` instance on site-x. This exercises the read-only native alarm
mirror end-to-end — mirrored alarms appear live in the Central UI Debug View with severity
and condition badges. Verify the binding with:

```bash
scadabridge --url http://localhost:9100 template native-alarm-source list --template-id <id>
```

### After Code Changes

```bash
bash docker-env2/deploy.sh
```

The Docker build is shared with the primary stack — if you've just run `docker/deploy.sh`, the env2 build hits a fully cached image.

### Running Both Stacks Concurrently

```bash
bash docker/deploy.sh         # primary
bash docker-env2/deploy.sh    # env2
```

Both UIs are now reachable:
- Primary: http://localhost:9000
- Env2:    http://localhost:9100

### Teardown

```bash
bash docker-env2/teardown.sh
```

Containers stop, volumes (data + logs) preserved. To also drop the env2 databases:

```bash
docker exec scadabridge-mssql /opt/mssql-tools18/bin/sqlcmd \
    -S localhost -U sa -P 'ScadaBridge_Dev1#' -C \
    -Q "DROP DATABASE ScadaBridgeConfig2; DROP DATABASE ScadaBridgeMachineData2;"
```

### CLI Access

```bash
dotnet run --project src/ZB.MOM.WW.ScadaBridge.CLI -- \
    --url http://localhost:9100 \
    --username multi-role --password password \
    template list
```

### View Logs

```bash
docker compose -f docker-env2/docker-compose.yml logs -f
docker logs -f scadabridge-env2-central-a
```

## Test Users

Same as primary (env2 shares LDAP). See `infra/glauth/config.toml` and primary `docker/README.md`.

## Transport Testing Workflow

See [`docs/plans/2026-05-24-second-environment-verification.md`](../docs/plans/2026-05-24-second-environment-verification.md) for the manual golden-path checklist.

## What's Different from Primary

- Single site (`site-x`) instead of three (`site-a/b/c`).
- Host port range 91XX vs primary 90XX.
- Container names prefixed `scadabridge-env2-`.
- Databases `ScadaBridgeConfig2` / `ScadaBridgeMachineData2` on the **shared** `scadabridge-mssql`.
- `Transport.SourceEnvironment` = `"docker-cluster-env2"` (stamped into exported bundle manifests).
- Distinct `Security.JwtSigningKey` (sessions cannot cross envs).
