Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/tasks/28-upgrade-handover.md
Fimeg cd3d57959b docs: source health is §10; correct the TASK-26 retirement
TASK-26 had two subjects. Yesterday's note closed the proximity-authority
half and read past the other one — evidence-source health, which is what
PAF/slpi.md was actually pointing at. Built now, so both are closed.

TASK-28 gains two measured cases: the reporter/sessiond split-delivery
order, and sessiond restart putting up the fallback PIN surface.
2026-07-26 08:38:35 -04:00

5.8 KiB

TASK 28 — Handover on upgrade

Status: scoped 2026-07-25, not started. Size: design first, then one session per mechanism.

An upgrade can move authority from one component to another. If the two halves land at different times, behaviour regresses silently — the system keeps running, nothing fails, and a capability is simply gone. Today that was caught by hand. It should be caught by the packaging.

The case that motivated this (2026-07-25)

blueline-proximity-lock was demoted from an authority to a reporter: it used to call blueline-screen-toggle itself, and now it reports proximity to sessiond, which decides. Correct design — but the new script is inert unless the sessiond it reports to is new enough to act on it. The two halves ship by different routes:

  • sessiond ships in the souveraine package, via CI → pacman -Syu
  • the script lives in rootfs-overlay/, which reaches a device only at provision time (TASK-27)

Installing the script alone would have removed proximity blanking and put nothing in its place. Nothing would have errored. The phone would simply have stopped blanking in a pocket, and the next person to notice would have been Casey, in a pocket. The only reason it went well is that both halves were installed together by hand, in order, with the result checked.

A second instance, measured 2026-07-26

Two more data points, both from shipping the source-health change.

The split-delivery case fired again, and this time the failure direction is the loud one. Source health lives in sessiond (souveraine package, CI → pacman -Syu); the heartbeat that makes silence meaningful lives in blueline-proximity-lock (rootfs-overlay/, provision-time only). New sessiond with the old reporter declares proximity down every 90 s forever, because nothing heartbeats. That was avoided by deploying the reporter first, by hand, in order — which is the same "the only reason it went well" as the case above. Note the asymmetry is worth encoding: here the wrong order is noisy, in the proximity demotion it was silent. Packaging cannot rely on which.

Restarting sessiond puts up the fallback PIN surface. Measured: 07:55:00 systemctl --user restart souveraine-sessiond → 07:55:05 session lock acquired → the shell reconnected at 07:55:05 but sessiond kept the lock and ran PAM itself → one failed attempt 07:55:18, success 07:55:33, unlocked at the device. This is lock-before-shell working as designed (a session authority that restarts must not leave an unlocked gap), but it means every package upgrade that restarts the daemon locks the phone into the fallback surface, not the shell's. The handover this task is named for is exactly the missing piece: the new sessiond should take the lock from the old one and hand it to the live shell rather than re-running the crash-path surface.

Related, same shape: restarting sessiond to pick up the new binary dropped the shell's heartbeat, so shell_alive went false and the session fell back to sessiond's spartan PIN surface instead of the shell's lockscreen. Nothing re-registered it. A user who picked the phone up at that moment would have concluded the lockscreen was broken.

What is missing

  1. A capability handshake, so a reporter can refuse to demote itself. Before blueline-proximity-lock stops deciding, it should be able to ask sessiond "do you act on proximity?" and keep its old behaviour if the answer is no. The device_state IPC already exposes enough to build this (a panel_on field and a policy the daemon will admit to); what is missing is a declared capability list and the reporter checking it. Same argument applies to any future "X stops deciding, Y decides now" change.

  2. Both halves in one transaction. As long as the overlay is not a package, no dependency can express "this script needs that daemon". Packaging the overlay (TASK-27) is the prerequisite; then a normal depends=('souveraine>=0.1.r219') does the work, and pacman refuses the half-upgrade rather than performing it.

  3. Restart order and session re-registration. A souveraine upgrade should restart sessiond and cause the shell to re-establish its bridge, in that order, without the user seeing the fallback surface. Today the pacman hook enqueues marked services with no knowledge of the lock handoff that sits between them. sessiond already supports the zero-gap handoff in both directions — the upgrade path just never uses it.

  4. A post-upgrade assertion. After the transaction, something should check that the capability still exists: proximity still blanks a locked screen, the shell still owns the lock, gestures still bind. A silent regression is only silent because nothing asks.

Prior art worth reading before designing this

Android's PackageManager restarts a component's dependents on replace, and its ACTION_MY_PACKAGE_REPLACED exists for exactly this. systemd has ExecReload= and Also=/PartOf= for restart propagation, which is likely enough for the restart-order half without inventing anything.

Acceptance

  • No upgrade can install one half of an authority move. Attempting it fails the transaction with a legible reason.
  • A reporter whose consumer is too old keeps its previous behaviour and says so, once, in the journal.
  • A souveraine upgrade leaves the shell owning the lock, with no fallback surface visible and no manual restart.
  • A post-transaction check reports which device capabilities are live, so a regression is a failed check rather than a discovery weeks later.

Connects to

TASK-27 (packaging the overlay is the prerequisite), TASK-08 / TASK-15 (the device state machine is the consumer in the case above), SESSION-AUTHORITY-DOCTRINE §11 (one authority — this is how authority moves between processes without a gap).