# Handoff — PON Go iOS

Context for the next session/developer. Pairs with [`README.md`](README.md) and
[`MCMS_API.md`](MCMS_API.md).

## Last session (2026-05-31)

Landed on `main`: a multi-agent code-review pass (correctness / concurrency /
security / cleanup — see [`CHANGELOG.md`](CHANGELOG.md)), then removal of the
self-signed TLS option (system trust only, parity with the Android client).

> ⚠️ **Verify on the next Mac build.** The code-review fixes were confirmed
> building on the Mac, but the **self-signed-TLS removal was merged to `main`
> un-compiled** — the Linux dev box that made the change has no Swift toolchain.
> First thing next session: build `main` and confirm it compiles. It's a small
> change (removed `ServerTrustPolicy.allowSelfSignedForHost` + the Settings
> toggle; the trust `switch` stays exhaustive), so it should be clean.

TLS consequence: **system trust only** now — a self-signed appliance (the lab box
`mcms.lab.svorka.net`) is unreachable until its CA is installed on the device
(MDM/configuration profile), or it gets a CA-trusted cert. `CHANGELOG.md` is
written to port these changes to the Android side. Merged branches
`code-review-fixes` and `remove-self-signed-tls` can be deleted.

## Status

Feature-complete and (per off-device typecheck + the user's Xcode builds)
compiling. Screens: Login, Settings, Dashboard (counts, total OLT traffic,
Health with tappable drill-downs, ONU-state breakdown, detailed-stats +
auto-refresh toggles), ONU list + detail (Overview with status/firmware+update/
optical-FEC/UNI/traffic, CPE DHCP, Config raw-JSON tree, Alarms, Logs; reset +
firmware behind hardened confirms), OLT list + detail (status/uptime/traffic/
environment/uplink/registration buckets, reset).

## Architecture

- `AppEnvironment` (`@MainActor @Observable`) owns the active `MCMSConnection`,
  persists the non-secret `APIConfiguration` to UserDefaults, keeps credentials in
  the Keychain, and routes `RootView` (Settings → Login → main `TabView`).
- Networking core: `APIConfiguration` → `SessionStore`/`APIClient` → repositories.
  Mongo documents are kept as a dynamic `JSONValue` (`APIEnvelope.swift`) and read
  via typed accessors on `MongoDocument` structs (`DeviceModels.swift`) — type the
  fields you use, leave the rest raw.
- View models are `@MainActor @Observable` classes with a `LoadPhase` + `load()`.

## API gotchas (handled in code — don't regress)

- **Single-encode URLs.** `APIClient.makeRequest` builds the path with
  `URLComponents.path` (encodes once, keeps `:` in MAC ids raw); `MCMSEndpoint`
  keeps ids RAW. `appendingPathComponent` double-encoded MAC colons → MCMS
  "invalid id". (Verified empirically.)
- **401 bounces to login; 403 does NOT** — `APIError.requiresReauthentication` is
  401-only (403 is permission/CSRF; re-login won't help). The `onAuthExpired` hook
  flows `APIClient → MCMSConnection → AppEnvironment`.
- **Writes return `{"status":"success"}` with no `data`** — `decodeEnvelope`
  returns the type's empty value instead of throwing (so login + all writes work).
- **ONU registration is on OLT-STATE buckets** (`OLTRepository.registrationMap`),
  not ONU-STATE. Parent OLT is `OLT.MAC Address`. Friendly names are `ONU.Name` /
  `OLT.Name` (NOT `Netconf.Name`/`NETCONF.Name`, which default to the id), fetched
  via `projection`.
- **CPE DHCP** = web helper `GET /api/cpe/onu/<id>/` (versionless, bare
  `[code, cpe…]` array — `APIClient.getRaw`).
- **Firmware** = Procedure 7 (`FirmwareUpgrade.apply`, unit-tested against a real
  config): GET ONU-CFG → write filename/version into the **inactive** bank → full-
  doc PUT. Inactive-bank only; still test on a spare ONU before production.
- **Timestamps** are UTC `yyyy-MM-dd HH:mm:ss[.SSSSSS]` with a literal space
  (`MongoTimestamp`, not ISO8601). Uptime = state `Time` − `Online Time`.
- Everything is `/api/v1` except `/v3/tasks/configs/` (unused). Cookies may be
  `__Host-`-prefixed (`SessionStore` matches either). `/api` is auto-appended to
  the base URL in Settings.

## Known real-world notes

- One lab box (`mcms.lab.svorka.net`) once sent an illegal `Upgrade: h2` header
  over HTTP/2 that iOS rejects (`-1005`). Fix is server-side (Apache `Protocols
  http/1.1`) or an HTTP/1.1 reverse proxy; iOS has no supported "force HTTP/1.1".
- The base URL must include `/api`; Settings normalises this.

## Verify loop

`swiftc -typecheck` against the macOS SDK for the core + view models (see README).
Tricky SwiftUI bits: compile isolated snippets sans iOS-only modifiers. No iOS SDK
on the dev machine, so full builds are the user's Xcode step.

## Repo split

Recommended: consolidate the flat sources + `Assets.xcassets` into the Xcode
project and git-init that as the iOS repo (with `docs/MCMS_API.md` + the PDFs).
The included `.gitignore` covers Xcode/Swift build artifacts. The Android app is a
separate repo (`../mcms_android`), self-contained.

## Possible next steps

OLT bulk firmware (Procedure 8 task); Swift Charts for PM history; live optical on
the list rows; richer alarm handling; widen test coverage of the firmware PUT
against more ONU-CFG shapes.
