Maintenance window
maintenance
A maintenance window quiesces one node's subtree: new work targeting it is
deferred (or admitted through the window's allow lists) until the window
ends. The JSON:API resource type is maintenance_window (verified in
serializeMaintenanceWindow), with the window row's id as the resource id
and a links.self of /api<node path>/maintenance.
The v2 OpenAPI document declares this resource's schema; the resource-field coverage gate holds this page to it.
Endpoints (node-scoped ones ride the node's …/maintenance RBAC rule path —
read for status, update for every write — 404-on-deny):
GET <node>/maintenance— the node's own not-ended window, or the inactive shape{"data": null, "meta": {"active": false}}.POST <node>/maintenance— start (201);POST <node>/maintenance/end— end (200).POST <node>/maintenance/schedule(201),/cancel,/extend— the scheduled-window lifecycle.GET /api/maintenance— the estate listing: every active and scheduled window the caller can read;?ended=trueadds ended ones, optionally bounded by?since=<RFC3339>.GET /api/maintenance/{window_id}/deferred_firings— the window's parked trigger firings, original event order.POST /api/projects/{project_code}/triggers/{id}/firings/{firing_id}/discard— prune one parked firing before release (only adeferredfiring discards).
Attributes
All timestamps are strings in 2006-01-02T15:04:05.000000Z (UTC), or null.
| Field | Type | Meaning |
|---|---|---|
active | bool | Window has started and not ended (started_at set, ended_at null). |
scheduled | bool | Window awaits its auto-start (started_at null, ended_at null). |
reason | string | The operator's required reason, from start/schedule. |
until | string/null | Declared intent only on a manually started window — the window never auto-closes on it — but it bounds deferral expiry at release. Null when not declared. Extend updates it on a manual window. |
scheduled_start | string/null | A scheduled window's declared from — when the engine auto-starts it. Null on manually started windows. |
hard_end | string/null | A scheduled window's declared to — a hard end the engine enforces (unlike the advisory until). Null on manual windows. Extend updates it on a scheduled window. |
auto_extend_secs | int/null | Per-increment extension the engine applies while the window's declared work is unfinished. Null unless scheduled with it. |
max_extension_secs | int/null | The fail-open cap on total auto-extension; defaults to 7200 (2h) when auto-extension was asked for without a cap. Null when auto-extension is not set. |
auto_end | bool | The engine ends the window when its stamped declared work completes. |
runs | string array | The declared workflow codes the engine creates at auto-start (as the scheduling operator). Always present; [] when none. |
allow | object | The window's declared maintenance work — {"workflows": [], "actions": [], "users": []}, each always present as a string array. Any one list matching a create (workflow code, change action, requesting username) admits it through the quiesced subtree. |
started_by | string/null | Username the start (or auto-start) is attributed to. Null while scheduled, and when the starting principal no longer resolves. |
started_at | string/null | When the window became active. Null while scheduled. |
ended_by | string/null | Username that ended the window. Null until ended. |
ended_at | string/null | When the window ended. Null until ended. |
deferred_count | int | The window's live parked-firing count (trigger firings with status deferred parked by this window) — computed per read via an indexed subselect. |
Window state vocabulary
There is no single state string; the lifecycle is read from the two
booleans and ended_at, exactly as derived in scanMaintenanceWindow:
- scheduled —
started_atnull andended_atnull (scheduled: true). - active —
started_atset andended_atnull (active: true). - ended —
ended_atset (both booleans false). Ended windows appear only in the estate listing with?ended=true.
A node holds at most one not-ended window (a partial unique index is the race-safe backstop); starting or scheduling over one is a named 422, as is ending when none is active. Cancelling requires the window id and only removes a window that has not started; extending refuses anything that would shorten the window.
What changes it
Start/End emit api:nodes:maintenance_started / _ended, Schedule emits
api:nodes:maintenance_scheduled, Extend api:nodes:maintenance_extended
— ordinary triggerable events. Release of deferred firings rides the
maintenance_ended event; a discarded firing settles suppressed, keeping
its deferred_window_id as history.
Related pages
opschain docs maintenance-windows— the gate, deferral and release narrative.