# EtherGold Field Configuration Matrix

Canonical field model for the Pi-served config UI (`field_config.py` + `/EtherGold/config`).
Bench tools (`FIXTURE_POS`, `fixture_hosts.env`, witnesses) stay separate.

**EG tip:** see `EG_VERSION` in `pog422_core.py`.

## Profiles (top-level radio)

| Profile ID | Label | EtherGold role | EDGE | DyzineFrame | CHANNEL |
|---|---|---|---|---|---|
| `listen_ohd` | POG422 Listen-only TopBox (1920×1080) | `listen` | `EDGE_MODE=0` | **on (TBX)** | `both` |
| `listen_ohd_server` | POG422 Listen-only (OHD Server — no Display) | `listen` | `EDGE_MODE=0` | **off** | `both` |
| ~~`atlas_copper`~~ | **Withdrawn** (copper hop + inject not reliable enough for field) | — | — | — | — |
| `wifi_bank_bridge` | WiFi Bridge Between Native Banks | `edge` | `EDGE_MODE=1`, peer-a/b | off (signage optional) | `both` |
| `atlas_wifi_star` | Atlas WiFi Hub/Spoke (Beta) | `edge` | `EDGE_MODE=1`, hub/spoke | **on** | `both` |

## Node roles (dependent)

| Profile | Roles |
|---|---|
| `wifi_bank_bridge` | `side_a` → `EDGE_ROLE=peer-a`; `side_b` → `EDGE_ROLE=peer-b` |
| `atlas_wifi_star` | `master_hub` → hub; `slave_spoke` → spoke + **TID** required |
| others | none |

## Identity (field-facing)

`/config` shows P3 GAMESTATS values (TID, machine number, Mode) in a dashed **read-only** group. Operator fields (asset, bank, Position Number, Location, log upload) stay solid borders. Same CSS: `input:read-only` is dashed + muted; editable fields take a gold focus ring. **Grab GAMESTATS** on that panel POSTs `/EtherGold/api/identity/p3-grab` (`force` refreshes filled identity; still skips if the cabinet is in play).

| Field | Required | Notes |
|---|---|---|
| `asset_id` | yes | Free-text; typically SGVI Asset Number |
| `bank` | yes | Logical bank number (`EDGE_BANK` on star) |
| `tid` | spoke only | 4-hex POG422 tunnel key — operator spoke field, not GAMESTATS |
| `tid_hex` / TID | read-only | P3 GAMESTATS **id row, last 4-hex** (live T340+: **BD07**). This is the POG TID. Config col 1 (**50004**) is **machine number**, not TID. |
| Mode | read-only | GAMESTATS Master/Slave (`MACHINE_ROLE`). Empty TID/Mode on config/status POST `/EtherGold/api/identity/p3-grab` once in an idle window. |
| `BANK_POSITION` | operator | Identity panel **Position Number** (1..x in the bank). Not GA `FIXTURE_POS`. |
| `enrollment_path` / Location | operator | Unique Enrollment.csv Location+Path. Ads + log dest. **Apply upserts this asset’s row in Enrollment.csv** (machine move). `Enrollment.local.csv` keeps the same row after an OTA pull of an older catalog. Pi does not publish Dropbox SGVI. |
| `log_upload_enabled` | operator | Identity checkbox, default on. **curl -T** to `:51709` `{Path}/Logs/` with Pi `wput.env` Basic auth (**SGVIUpload**). Unset asset skips IIS; unknown asset → `/orphan/{asset}/`. Not rclone. Not FTP wput. |
| `FIXTURE_POS` | **never in production Apply** | GA 4-board fixture seats only. UART socket default is internal `UART_SOCKET_ID=1`. |

### Production vs GA fixture

`FIXTURE_POS=1..5` / `51..99` / hashed values like **96** are **GA bench fixture** conventions. They must not appear in Production EtherGold Apply output. Field identity is `ASSET_ID` + hostname `topbox_<asset>` (Linux user stays `topbox`). GAMESTATS supplies POG TID (id-row last 4-hex) and Master/Slave. Config col 1 is machine number, not TID. Operator supplies `BANK_POSITION`.

| Profile | Production Apply `FIXTURE_POS` |
|---|---|
| all production profiles | **omitted** (`UART_SOCKET_ID=1` only) |

Bench launchers under the GA fixture tree still set `FIXTURE_POS` explicitly. Do not convert fixture pos1–5.

## Generated env summary

### `role.env`

| Profile | `ETHERGOLD_ROLE` |
|---|---|
| listen_ohd | `listen` |
| listen_ohd_server | `listen` |
| wifi_bank_bridge / atlas_wifi_star | `edge` |
| atlas_copper | **withdrawn** (do not apply) |

### `ethergold.env` (always)

```
CHANNEL=both
POG422_UARTD=1
POG422_UARTD_INJECT=0   # listen only
POG422_UARTD_INJECT=1   # bridge / edge / topbox
UART_SOCKET_ID=1
ASSET_ID=<asset_id>
FIELD_BANK=<bank>
BANK_POSITION=<1..x>     # operator Identity panel; omit if unset
# FIXTURE_POS is not written by Production Apply
```

### `edge.env`

| Profile | Keys |
|---|---|
| listen / copper | `EDGE_MODE=0` |
| wifi_bank_bridge | `EDGE_MODE=1`, `EDGE_ROLE=peer-a\|peer-b`, uplink/game if, WiFi SSID/PSK ref |
| atlas_wifi_star | `EDGE_MODE=1`, `EDGE_TOPOLOGY=hub\|spoke`, `EDGE_BANK`, spoke TID, `EDGE_TID_UNICAST=1`, `EDGE_BANK_WLAN=1`, fleet HMAC path, hub `EDGE_SPOKE_STALE_S` (default 180; `0` = never drop) |

## OIS / machine transport (WiFi star)

| Traffic | Path | Policy |
|---|---|---|
| Machine gameplay | WireGuard + gretap (VPN) | TID-unicast when learned |
| OIS | **Same 6 GHz Wi‑Fi, not inside VPN** | Minimal HMAC (`EDGE_FLEET_HMAC_KEY`); **flood + inject**; **no RX TID filter** |
| Pipe probe | WG+gretap (`EGPIPE1` / eth tag `P`) | Bidirectional PING/PONG; **never** POG422 inject; retry until ready; record delay/RTT |

Bank-wide OIS (Pause/Resume Bank, Print Ticket, Tournament Mode, …) must reach every seat.

**Transitional tip:** `CHANNEL=both` still tags OIS onto eth/gretap today. Field target = `ois_wifi_plane` (see `ois_wifi_plane.py`). Matrix cutover: set `OIS_WIFI_PLANE=1` when WiFi OIS plane is enabled; eth OIS tag remains until that path is proven.

**Listen / TopBox vs OHD:** dual-channel listen ingests OIS for status hooks only — **never** forward OIS on progressives HTTP to Win11 OHD. **OHD means 3840×1080 Win11 overhead only.** Atlas TopBox is DyzineFrame **TBX 1920×1080** with machine-specific display hooks — not an OHD — **and** it still serves HTTP :31234 progressives (a Win11 OHD may pull that same feed). `listen_ohd_server` is the same HTTP listen path with no local DyzineFrame.

## Validation (reject)

- listen + inject
- listen + `EDGE_MODE=1`
- star + inter-seat copper expected
- wifi_bank_bridge without side_a/side_b
- atlas_wifi_star spoke without valid TID
- duplicate TID same bank (when peers known)
- field profile with `CHANNEL` ≠ `both`
- empty `asset_id` or missing `bank`

## Services / display

| Profile | `ethergold` / collector | HTTP :31234 | DyzineFrame |
|---|---|---|---|
| listen_ohd | collector | yes (progressives for local DF **and** Win11 OHD) | **on** (TBX TopBox 1920×1080: logo videos, single-game progressive, ads, fireworks, door/error — machine hooks, not OHD) |
| listen_ohd_server | collector | yes (progressives for Win11 OHD 3840×1080) | **off** (no local display) |
| atlas_copper | ethergold bridge **+ same bus listen** (`progressives.csv` for TopBox DF) | yes | on |
| wifi_bank_bridge | ethergold + edge | yes | off by default |
| atlas_wifi_star | ethergold + edge | yes | **on** (transport ≠ display) |

**Gap (closing):** `set_ethergold_role.sh edge` historically disabled DyzineFrame. Field apply uses `--display on` for Atlas star so transport and display are separate.

## Wiring expectations

| Profile | Copper | WiFi |
|---|---|---|
| listen_ohd | one HAT leg on bank; no inject | optional LAN |
| listen_ohd_server | one HAT leg on bank if a bus exists; no inject | LAN required for Win11 OHD |
| atlas_copper | eth0/eth1 daisy | none required |
| wifi_bank_bridge | local game leg + uplink; **one** Master across both sides | WG peer-a ↔ peer-b |
| atlas_wifi_star | **no** inter-seat eth; game if optional | hub ↔ spoke gretaps; OIS on 6 GHz WiFi+HMAC |

## Apply (no reboot)

- **Save** writes `field-config.json` only (draft).
- **Apply** writes `field-config.json` + `role.env` / `ethergold.env` / `edge.env`. Stamps DyzineFrame `config.ini` asset/bank/`display_mode=TBX` only when the profile has a local TopBox display (`listen_ohd`, `atlas_wifi_star`). `listen_ohd_server` does not stamp or start DyzineFrame.
- Apply does **not** reboot and does **not** restart listen/collector (`set_ethergold_role.sh` is not invoked from the UI).
- HDMI identity is in-memory in DyzineFrame until Supervisor Stop/Start (not a Pi reboot).

## Auth (config UI)

- Login required for `/EtherGold/config`, `/EtherGold/status`, and `/EtherGold/api/config*`
- Unauthenticated visit to config/status redirects to `/?next=<path>`; after Sign in, home returns to that allowlisted path
- Sign in mints a process-scoped UI session (not stored Basic). HTTP restart or Pi reboot invalidates it; the open tab watches `/health` `boot_id` and returns to login
- Default user `SGVI` / password from server-side `field-ui.auth` or env (not in client JS)
- Progressives CSV / floor-games / sync JSON stay unauthenticated for OHD Win11

## Lab-only (not in field UI)

- `CHANNEL=machine` or `CHANNEL=ois` alone
- Lab synth / debug flags
- `FIXTURE_POS` as operator input
