ADR 0023: Scheduled image apply is an explicit, time-bound opt-in
Status: accepted
Context
ADR 0007 made DeskOS stage image updates and never reboot on its own, which is the right default for a workstation:
a published image must not interrupt a user's session. Some organizations run unattended kiosks or lab stations where
a reboot is part of the maintenance contract, and a staged image must not wait indefinitely. bootc upgrade --apply
applies the update and reboots, so it cannot be a safe default: it must be an explicit organization decision, limited
to a local time or a window, and a missed calendar event after suspend must not become a surprise reboot.
Decision
Extend UpdatePolicy.image with an optional apply object:
image:
automatic: true
schedule: daily
requireACPower: true
apply:
enabled: true
days: [sunday]
at: "03:00" # exact time
timezone: Europe/Madrid # optional; machine local time when omitted
The window form replaces at with a window of start (HH:MM) and duration. apply.enabled: true requires
image.automatic: true; the staging service remains, and a separate deskos-image-apply.service and .timer are
added. Rules:
daysis a non-empty list of lowercase weekdays, and exactly one ofatorwindowis required;window.durationis a positive whole-minute duration that ends on the same day.timezoneis an optional IANA name, validated with Go's embeddedtime/tzdata, never the build host's zoneinfo; the generatedOnCalendar=carries it, and omitting it means the machine's local zone.apply.enabled: falsedisables a lower layer's apply policy, and other apply fields below it are reported as masked. The apply timer isPersistent=false, so a machine powered off during the window waits for the next one instead of rebooting at the next boot.- The generated service runs
deskos windowas anExecConditionbeforebootc upgrade --apply --quiet; it exits 1 outside the permitted interval, so a calendar event that elapses during suspend cannot reboot a user who wakes the machine later. The exact mode allows five minutes of grace, the window mode checks the full window, andAccuracySec=1minis written for both.
Consequences
- ADR 0007's "no generated unit reboots" remains true for every image without an explicit
applyblock; the test that rejects--applyin rendered files must keep covering the Core and example images. Core does not setapply, which stays a foundation default of stage-only. - The service does not silently kill an active session:
skipIfActiveSessionis deliberately not part of this decision, and the window is the contract. Notification is a separate decision, and this adds no public kind; the count does not change.
Endpoint interface
| Item | Name |
|---|---|
| apply service and timer | deskos-image-apply.service, deskos-image-apply.timer |
| time guard | /usr/bin/deskos window --days … --at … --grace … or --start … --duration … |
| effective plan | artifact.scheduledUpdates with kind image-apply |