Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/souveraine-components/sessiond.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

111 lines
5.7 KiB
Markdown

# souveraine-sessiond
User-tier session authority daemon. Owns the ext-session-lock-v1 surface
and the unlock verb — the one thing in the system that can release the
session lock. The shell is a *client* of this authority, not the authority
itself.
Source: `src/bin/souveraine-sessiond.rs`, `src/sessiond/{protocol,server,lock,draw,auth}.rs`.
Companion design: `docs/session-authority-boot-order.md` (Phase C),
`docs/SESSION-AUTHORITY-DOCTRINE.md` §11.
## What it owns
- The ext-session-lock-v1 client that holds the session locked at boot,
before the shell exists, and after the shell dies.
- The PAM conversation for unlock — hand-rolled libpam FFI (`auth.rs`),
no wrapper crate (fewer deps for on-device builds).
- A Unix socket at `$XDG_RUNTIME_DIR/souveraine/sessiond.sock`, same-user
today (`0600`), machined-shaped JSON-lines protocol.
## The zero-gap handoff (load-bearing)
A lock client that drops its Wayland connection **without unlocking** leaves
the compositor holding the session locked. With Hyprland's
`misc:allow_session_lock_restore`, the next client inherits that locked
state. sessiond uses this for two transitions, both fail-closed:
1. **Boot handoff.** sessiond takes the lock first (splash dissolves onto a
locked panel), quickshell starts ~3s later, the bridge calls
`shell_ready`, sessiond drops its connection, the shell's lock inherits
— no unlocked instant between them.
2. **Crash retake.** The bridge connection is the shell's heartbeat. EOF on
that connection = shell dead. sessiond retakes the lock
(`allow_session_lock_restore`) and renders its own spartan PIN surface.
A shell crash while locked is no longer an ssh-only recovery.
The retake is self-correcting: if sessiond tries to lock against a *live*
shell lock, the compositor yields Finished/Denied — no deadlock.
## What crosses the boundary
JSON-lines over the socket (`protocol.rs`). Request ops: `status`,
`shell_ready`, `locked_ack`, `lock`. Phases: `holding`, `awaiting_shell_lock`,
`released`, `idle`. The `shell_ready` connection *is* the heartbeat; its EOF
is the retake trigger. Responses are guarded `ok`/`reason` shaped, same
convention as machined.
Unlock is never an RPC verb. The only path to unlock is the PAM
conversation on sessiond's own lock surface.
## Today's security posture
- **Lock-before-shell.** No desktop surface (NM dialog, bar, background)
can precede the lock at boot — the boot fail-open hole that motivated
this daemon.
- **Lock-past-shell-death.** Shell crash while locked retakes to a PIN, not
a locked brick.
- **PAM is the unlock path.** `auth include login` (same credentials as
console login), unprivileged via `unix_chkpwd`. No bypass verb.
- **Fail-closed default.** Default behavior on start is to lock; `--no-initial-lock`
is the explicit opt-out for dev.
Sessiond is still packaged as a user unit with no `User=` directive. It
therefore runs as the human graphical-session owner. That ordering closes the
boot visibility hole but does not close ownership of the lockscreen. The
target principal is `souveraine-session`, with only the Wayland lock, PAM,
Ambient, and Revealable reach described in
`../../../souveraine/saf/authority/01-session.md`.
## Audit gaps (toward the RedFlag bar)
sessiond is the component that most directly maps onto RedFlag's "capability
token over a resolved closure, signed by an authority, verified by the
executor" shape (SESSION-AUTHORITY-DOCTRINE §10), because *unlock is
exactly the kind of verb that should require a token*. Today it does not:
- **`shell_ready` has no caller attestation.** Any same-uid process that
can reach the socket can call `shell_ready` and induce a handoff, or call
`lock`. RedFlag's analog: the privileged executor verifies the token's
signature, `AgentID`, `Operation`, expiry, and closure hash *before*
acting — it does not trust "a process asked." sessiond trusts the
heartbeat's mere existence. The fix shape: the shell presents a
machined-signed capability token (operation=`session_handoff`, expiry
short, closure = the shell binary hash) that sessiond verifies against
the pinned machine pubkey before honoring `shell_ready`.
- **Inhibit / step-up verbs (§6/§10) are stubs.** sessiond is the natural
home for the `souveraine-stepup` PAM service and the portal Inhibit
backend, and for verifying capability tokens on session verbs. These are
named in the doctrine but not implemented; the socket protocol reserves
the space.
- **`SO_PEERCRED` not yet read.** machined logs uid/gid/pid on every
request; sessiond does not yet. Same-user-only today (`server.rs`
comment: "widens to a group grant deliberately when agents get accounts").
Per-agent UIDs and worker processes turn this into kernel-backed caller
identity — the substrate of the capability gate. The account, admission,
and health contract is
`../../../souveraine/saf/identity/02-agent-principal.md`; creating accounts
without moving execution out of the human-owned server is not sufficient.
- **Binary integrity.** As with the other two and RedFlag SEC-022: no
self/peer hash check. DAC is the standing guarantee. When the capability
token lands, the token's closure should carry sessiond's *own* expected
hash so a swapped binary cannot present a valid old token — RedFlag's
`ClosureEntry.SHA256` shape pointed inward.
- **eBPF (KERN-001 shape).** The lock gate's kernel-level stop is
ext-session-lock-v1 itself (a Wayland protocol the compositor enforces),
which is already a kernel-adjacent guarantee stronger than RedFlag's
eBPF-scaffold-for-execve. So sessiond's "defense-in-depth kernel stop"
is largely already present via the compositor; the gap is the
*token gate* on top, not the kernel enforcement underneath.
See [audit-status.md](audit-status.md) for priority.