Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/SESSION-TRUST-ARCHITECTURE.md
Fimeg 341a2fe060 docs: take the lockscreen out of casey's uid
The current user unit wins the boot race but still runs as the human whose Personal key the lock is meant to evict. Record the target souveraine-session principal, its narrow Wayland/PAM/data reach, and the fact that the rich shell is presentation—not release authority.
2026-08-17 12:43:45 -04:00

105 lines
6.1 KiB
Markdown

# 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 |
| --- | --- | --- |
| Who owns the secure pre-unlock surface? | Dedicated `souveraine-session` principal + compositor lock protocol | Sessiond decides and audits; the shell renders; Casey authenticates but does not own the lock process. Target design, not current user-unit state. |
| 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]