Chapter 5 · contracts
Interfaces and contracts
Boundaries between components: what crosses them, what stays local, and what is still illustrative.
Full catalog with producer/consumer fields: Interface catalog.
Monitoring-only boundary
Figure 6. Enrollment → first contact → first UPS reading
Takeaway: The sequence shows who sends the code, where the credential is stored, and that a denied enroll stores nothing. Endpoint names stay Proposed.
Related: Enrollment interface · Enrollment sequence
WDJB-MJHT is synthetic.
Figure 10. Configuration states — desired · pending · applied · failed
Takeaway: Pending leads separately to Applied or to Failed. A failure does not become Applied. Lost contact leaves the change Pending.
Related: Config states
Illustrative exchanges
Schemas and endpoint names below are illustrative / Proposed unless noted as Accepted product behavior.
Example — telemetry record (illustrative)
{
"schema": "mainskeeper.telemetry.reading/v1-illustrative",
"collectorId": "bw-pi-01",
"upsId": "ups-bw-rack",
"observedAt": "2026-04-14T09:12:00-07:00",
"receivedAt": "2026-04-14T09:12:02-07:00",
"powerState": "battery",
"chargePercent": { "support": "not-reported" },
"runtimeEstimateMin": { "support": "not-reported" },
"sequence": 4412
}
This is the walkthrough’s first on-battery observation, delivered while the WAN is still up. Charge and runtime are omitted on purpose. Missing measurements use unsupported or not-reported, never a fabricated 0. See the Broadwater outage walkthrough.
Example — replayed record (illustrative)
{
"observedAt": "2026-04-14T09:41:00-07:00",
"receivedAt": "2026-04-14T09:48:40-07:00",
"flags": ["received-late", "replayed"],
"powerState": "utility",
"sequence": 4488,
"note": "Return to utility. History at 09:41. Does not set recovery. The 09:12 row was already acked and is not sent again."
}
Receipt is more than two minutes after observation, during drain, so this is replay. It places the return to utility in history at 09:41 and ends the observed battery episode. It does not recover the on-battery incident, and it does not repeat the 09:13 notification. A separate fresh UPS reading after the 09:48 heartbeat does the recovery. Resolution is five stable minutes after that recovery. The replayed row keeps its original timestamp and does not overwrite the newer current state.
Example — enrollment exchange (behavior Accepted; HTTP Proposed)
# Collector mainskeeper enroll --headless # → short code WDJB-MJHT (design TTL 10 min), device polls for approval # Operator (dashboard) # approve code → site + roles + witness flag # Collector receives credential material out-of-band from URL/logs # First contact ≠ first UPS reading
Example — desired vs applied config
Desired (dashboard): heartbeat_interval = 30s Local override: heartbeat_interval = 15s Effective text: "Effective: 15 s (local override) · Desired in dashboard: 30 s" State lifecycle: desired → pending → applied | failed(reason)
Figure 11. Operational incident lifecycle
Takeaway: Acknowledgement sits beside the state path. Recovery follows fresh evidence that the condition cleared. Resolution follows five minutes stable. First evidence and grace have no fixed order.
Related: Incident lifecycle