Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/SESSION-TRUST-ARCHITECTURE.md

5.9 KiB

Session and interaction trust model

This is the contract for the Souveraine shell on laptop and phone. It records the boundary between orchestration we own and the Linux/Wayland authorities we must consume rather than replace.

Authorities and projections

Question Authority Souveraine's role
Can this machine suspend, hibernate, power off, or reboot? logind Can* methods Query and expose the answer; execute the matching logind/systemd verb.
Has the compositor secured the display? WlSessionLock.secure Mirror it as screenLockSecure; never infer it from a button press.
Does the shell want its lock surface shown? GlobalStates.screenLocked Maintain this request state and persist it across a shell crash.
Has the user authenticated? PAM and the lock's LockContext Start a PAM conversation; only it may release WlSessionLock.
Why is automatic idle sleep suppressed? The active idle mechanism Keep a reason/cookie registry, but report only mechanisms that are actually applied.

screenLocked and screenLockSecure must stay distinct. The first closes ordinary shell surfaces immediately. The second is the only proof suitable for personal-data disclosure. A failed or delayed Wayland lock is therefore visible as lockRequested: true, locked: false, not mistaken for success.

Session IPC

All structured IPC results are JSON strings. Quickshell IPC only marshals primitive QML types, so : var silently becomes void and loses its payload.

session.lock() means “the Wayland lock was requested”; it does not claim the compositor is secure yet. session.state() exposes the subsequent locked acknowledgement and the most recent asynchronous power-action result. Power verbs return status: "started" when their command has started; a later exit is available in lastAction. Refusals always log and return a reason.

The current public inhibitor surface implements idle and sleep. It is intentionally wrong to accept shutdown, logout, or user-switch until each owns a real mechanism. The eventual contract is:

Kind Backend Status
idle Native Wayland idle inhibitor once verified on both targets; until then the tested hypridle control path. Implemented.
sleep A live logind/systemd inhibitor FD or systemd-inhibit process, held for the cookie lifetime. Implemented via SessionEvents delay-mode inhibitor.
logout, user-switch Shell policy checks at those operations, not fake logind inhibitors. Not yet implemented.

Every inhibitor must have a non-empty human reason, a cookie, and a visible holder in state(). Releasing an unknown cookie is a refusal.

Lock and suspend sequencing

The next lifecycle change is not another cached state machine. It is a logind event ingress, implemented in services/SessionEvents.qml:

  1. [done] Subscribe to the current logind session's Lock signal and request the Wayland lock on receipt. This makes external loginctl lock-session calls meaningful without assuming logind implements a screen locker.
  2. [done] Hold one delay-mode sleep inhibitor from shell startup.
  3. [done] On logind PrepareForSleep(true), request the Wayland lock and wait for WlSessionLock.secure; release the delay inhibitor only then.
  4. [done] Reacquire it after PrepareForSleep(false).

The delay budget is logind's InhibitDelayMaxUSec (commonly five seconds), so this must be timed on the Pixel 3 before it becomes a security claim. The failure policy is fail closed: if the lock is not secure before the deadline, leave a diagnostic event and do not pretend the session was locked.

Interaction capability tiers

The lock screen, media controls, and agent are one policy system, not three. Each operation and each response is labelled with a minimum tier:

Tier Requirement Examples
ambient None clock, weather, timers, transport controls, non-personal answers
personal No lock is requested and the compositor is not secure (!screenLocked && !screenLockSecure) messages, calendar, memories, fleet state, conversation history
stepUp Recent successful reauthentication sending, pushes, deletion, payments, physical access

Media metadata is ambient by default but must be configurable to personal. When the lock occurs, in-flight personal or step-up agent output is withheld or redacted; only ambient output remains visible.

Step-up authentication is a separate PamContext, using a dedicated PAM service such as souveraine-stepup. It never unlocks the session and it never accepts a boolean from an agent as proof. A successful result mints a short-lived, in-memory grant bound to the local action family. The grant must be cleared on lock, session end, PAM failure, and expiry. The exact freshness window is a deliberate policy setting, not an implementation accident.

Implemented in services/StepUpAuth.qml. The PAM service file (/etc/pam.d/souveraine-stepup) is NOT shipped by the shell — it is root-owned system configuration and must be installed/audited separately. Before enabling it, verify that one polkit agent owns the session and that no legacy desktop power manager is competing for idle or sleep policy.

The trust boundary matrix is documented in TRUST-BOUNDARY-MATRIX.md.

Required verification

  • session.capabilities() matches logind Can* on laptop and phone.
  • session.lock() reaches locked: true only after WlSessionLock.secure.
  • loginctl lock-session and suspend both take the same Wayland lock path.
  • Unsupported inhibitors refuse; each supported cookie visibly changes its mechanism and is released exactly once.
  • A lock during personal agent output hides it; a lock during an ambient timer leaves only the timer visible.
  • Step-up grants cannot survive lock, timeout, shell restart, or action-family changes. [done — StepUpAuth revokes on lock, expiry timer runs every 30s]