# 02-environment — where this runs and how to reach it

Two machines. One runs the PLC, the other runs the SCADA. Both sit on the
`10.0.0.0/24` PoC network.

| Role | Hostname | LAN | Notes |
|---|---|---|---|
| **SCADA** — Yokogawa CI Server R1.03 | `yau-poc-cicore1` | `10.0.0.21` | Windows. Polls the PLC over Modbus TCP. |
| **PLC** — OpenPLC Runtime v4 in Docker | `yau-sls-poc-lin001` | `10.0.0.17` | Ubuntu 22.04 on Azure, public IP `20.211.144.151`. **Shared, live host** — see below. |

Also on the network: an Active Directory domain controller at `10.0.0.5`
(`yau.poc`), and roughly a dozen other Windows hosts.

## ⚠️ The Linux host is shared, live, and not ours

`yau-sls-poc-lin001` is the YAU Innovation Team's general-purpose Docker host and
VPN gateway. It runs **~28 containers** for several unrelated projects — Grafana,
InfluxDB, Node-RED, Forgejo, Authelia, Portainer and more — behind a Caddy reverse
proxy with AD + Duo MFA. It serves customer-facing demos.

**`openplc-runtime` is one container among many, and this project owns only that
one.** Rules that follow from it:

- **Never restart Caddy or Authelia** — they interrupt every other service.
- **Never put growing data on the root disk** (`/` is 62 GB). Use `/datadisk`.
- **Never publish a container port on `0.0.0.0`.** The host has a public IP.
- Everyone shares the `azureuser` login, so there is no per-person audit trail —
  **announce disruptive work.**

`YAU_Linux_Host_Onboarding.md` is the host's own brief, written by its owner
(Daniel Watson) and current as of 2026-09-01. **Read it before changing anything
on that machine.** Its §10 is a list of rules, each of which exists because
breaking it already caused an outage.

## Access

### The Linux host — SSH

```bash
ssh -i ~/.ssh/yau-sls-poc-lin001_key.pem azureuser@20.211.144.151
```

An SSH config alias makes this shorter. The working copy used during the audit:

```
Host lin001
    HostName 20.211.144.151
    User azureuser
    IdentityFile ~/.ssh/yau-sls-poc-lin001_key.pem
    IdentitiesOnly yes
```

`10.0.0.17` is **only routable from inside the VNet or over the WireGuard VPN** —
from outside, use the public IP.

### The CI Server host

No remote access route is recorded. It was reached interactively during
development. If you need one, ask.

### What you need from the host owner

| Item | What it is |
|---|---|
| `yau-sls-poc-lin001_key.pem` | SSH private key — `chmod 600` it or SSH refuses to use it |
| AD account in `HTTPS_UserAccess` | required for every web UI on the host |
| Duo enrolment | second factor for those UIs |
| A WireGuard peer | optional, but it is what makes `10.0.0.x` reachable directly |

## Ports

| Service | Host | Port | Bound to | Notes |
|---|---|---|---|---|
| OpenPLC **Modbus TCP server** | `10.0.0.17` | 502 | **`10.0.0.17` only** | The PLC is the Modbus *slave*. CI Server polls it. |
| OpenPLC **REST API** (HTTPS) | `10.0.0.17` | 8443 | **`10.0.0.17` only** | Control channel — upload, start/stop. JWT auth. No browser UI. |
| CI Server Modbus **client** | `10.0.0.21` | — | outbound | |

> [!IMPORTANT]
> **The bind address is the security control.** Modbus has no authentication or
> encryption, and 8443 is the control channel with only JWT in front of it. The
> host has a public IP, so binding these to `10.0.0.17` rather than `0.0.0.0` is
> the only thing keeping them off the internet. **Never widen it.**

**There is no host firewall.** `ufw` is inactive on `yau-sls-poc-lin001`; inbound
filtering is entirely the **Azure Network Security Group**. Opening a port means
editing the NSG in the Azure portal, not the host.

## Secrets

Live in **`secrets.local.md`**, which is git-ignored and must never be committed.
`secrets.local.md.template` shows the shape — copy it, fill it in, keep it local.

It holds the OpenPLC runtime `admin` password and CI Server credentials. The SSH
key is a file, not a line in that document, and is likewise never committed.

Host-level credentials (service admin passwords, API tokens for Grafana, Influx,
Authelia and so on) are **not this project's** — they live in the host owner's
`Linux Machine Config.txt`. Ask; do not copy them here.

## Files here

| File | What |
|---|---|
| `YAU_Linux_Host_Onboarding.md` | The Linux host's own brief — the stack, the auth model, the deployment pattern, and the rules. **Written and owned by the host owner, not by this project.** Current 2026-09-01. |
| `openplc-container.md` | How the PLC container is configured, as read from the running host |
| `openplc-compose.yml` | Verbatim copy of the live `~/openplc-compose.yml` |
| `MIGRATION.md` | Moving the PLC container to another host — the as-built record of the 2026-08-19 move, kept as a repeatable runbook |
| `secrets.local.md.template` | The shape of the git-ignored secrets file |

**Not kept here:** the host's full operations manual (`Host_Documentation.md`,
~400 lines covering ChirpStack, Forgejo, EQP licensing, the Telegraf fleet and the
rest of the platform). None of it is WRPS, it is owned elsewhere, and a stale copy
in this repo would be worse than a pointer. Ask the host owner for the current
version.

## Checking the environment is healthy

```bash
ssh lin001 "docker ps --filter name=openplc-runtime"
ssh lin001 "docker logs --tail 30 openplc-runtime"
ssh lin001 "df -h / /datadisk"          # both disks - / is only 62 GB
```

From a machine that can route to `10.0.0.17` (VNet or VPN):

```bash
python ../05-tests/verify_modbus.py --host 10.0.0.17 --port 502 --unit 1
```

From anywhere else, poll from a throwaway container on the host's own network:

```bash
ssh lin001 "docker run --rm --network openplc-net python:3.12-alpine \
  sh -c 'pip install -q pymodbus && python -c \"...\"'"
```

## Known issues

| Issue | Detail |
|---|---|
| **PLC scan overruns** | The 100 ms task overran ~100 times between 2026-08-19 and 2026-08-28 — roughly one per 4–5 days. Logged as a warning; the task runs at a reduced rate and the demo continues. Not investigated; the host is shared, so contention is a candidate. |
| **Migration artefacts left on the host** | `~/openplc-migration/` still holds `openplc-image.tar.gz` (354 MB) and `openplc-vol.tar.gz`. `MIGRATION.md` Part G says to delete them, and **the volume tarball contains the runtime's JWT secret.** |
| **The runtime image cannot be re-pulled** | It was produced by `docker commit` and exists in no registry. Deleting it loses the compiled PLC program. See `03-plc/as-built/`. |
| **No deployment toolchain** | The OpenPLC Editor lived only on the retired `dev-ubuntu` host. See `03-plc/DEPLOY.md` §0. |
