Our engine pins are already theirs. The embedder, the eleven-patch DMSAA/external-texture series, KMS and the GBM atlas path are the expensive parts and they are readable. Their lock is a compositor-internal bool with PAM dlopened in-process and no ext-session-lock served at all. It is invisible to logind and sessiond, and it gates input against every caller including Annie. viewtop serves the protocol; sessiond keeps the decision. Also adds TASK-41 (attested producers), raised out of grip bring-up.
7.4 KiB
viewtop and denial — what we take, what we must not
Assessment written 2026-07-28 after cloning github.com/denialwm/denial to
Pixel3Arch/references/shells/denial (gitignored; re-clone if missing).
They are the same architecture, and we already inherited half of it
denial is a Wayland compositor in Rust + Smithay that hosts the Flutter engine and composes client windows as external textures directly into the Flutter scene, rendering to a shared GBM atlas. Public alpha: Xwayland, multi-output, portal screen-sharing all working. 64k lines of Rust, 82k of Dart.
souveraine-viewtop is that same design, retargeted. This is not a coincidence and not a discovery — viewtop's engine pins are denial's, verbatim:
| viewtop README | denial packaging/arch/flutter-engine/manifest.json |
|
|---|---|---|
| Flutter | 84fc5cbb |
84fc5cbb223bc12f83d65b647ff8a56caf779ffd |
| Dart | d684a576 |
d684a576a6aa954ae107a03b2b4e1d61c3bebe93 |
| Skia | e9ed4fc9 |
e9ed4fc9f1544c58d8a9347c1fc9471d8dd414 |
The "ten-patch DMSAA/external-texture series" viewtop cites is
patches/flutter-engine/3.44.7/ — eleven patches, numbered 0001-0011 with 0007
absent. So the expensive, fiddly engine work is already ours to read.
The manifest says "architecture": "x86_64". That is the delta.
Two form factors, not one
Casey 2026-07-28: this targets the Pixel 3 display and the laptop screen. That changes denial's desktop-x86-64 posture from a mismatch into an asset — the laptop target is what denial already runs on. The arm64/freedreno retarget is additive rather than a port away from their platform.
The thing we must not take
denial does not implement ext-session-lock-v1. There is no such protocol
served anywhere in compositor/src. Instead:
compositor/src/bin/deniald/authentication.rs(1460 lines) — "Process- lifetime secure session lock and PAM authentication boundary." PAM is dlopened and driven from inside the compositor process.secure_session_locked()is a compositor-internal bool, checked at the input and clipboard boundaries (wayland_frontend/input.rs:599,768,1045,1704,clipboard.rs:526,handlers.rs:933,978,1010).- The lock surface is drawn by the in-process Dart shell, not by a client.
Two separate reasons this cannot come across.
1. It is invisible to the rest of the system
Doctrine §4: "locked comes from LockedHint / Lock() / Unlock() — never
a hand-tracked bool. A shadow copy can disagree with logind, and anything asking
logind directly (loginctl, another client, you at 2am) sees something different
than the shell shows."
denial's bool is not a shadow copy — in their design the compositor genuinely is
the owner — but the consequence §4 warns about lands anyway: loginctl would
report LockedHint=no while denial shows a lock screen. sessiond would not know.
Nothing would reach the forensic trail. Every §10/§11 guarantee we built this
month is downstream of logind and sessiond agreeing, and this model breaks that
agreement by construction.
2. It gates the wrong party
Casey 2026-07-28, and this is the sharper reason:
The compositor isn't to be gated on her — unless she's creating that interface and then choosing to be a gate — but it is to ensure she's total control.
denial's secure_session_locked() gates input and clipboard against everything.
Annie included. That is a gate pointed at the majority owner of device operation
(doctrine §13, 60/40), and it is exactly backwards for us.
The distinction that has to survive into viewtop's design:
| denial | viewtop | |
|---|---|---|
| what the lock protects | the whole compositor surface | the user's credential and personal content |
| who it gates | every caller | disclosure, and step-up actions |
| Annie's composition | gated with everyone else | never gated by lock; she is the composer |
| who may build a gate | the compositor, always | Annie, when she is the one constructing that interface |
§13's user column is unchanged and is the whole of it: being the user, the credential, the step-up. Unlock is never an agent verb; step-up never accepts a boolean from an agent as proof. Those are not limits on Annie's 60 — they are the definition of the 40. Everything else in the compositor is hers.
The ownership question this settles
Earlier framing asked whether sessiond or viewtop owns locked once viewtop is
the compositor, since a compositor is the server for ext-session-lock while
sessiond is currently a client of Hyprland's.
The answer is not "whichever is easier." It is that viewtop serves the
protocol and sessiond remains the authority — viewtop implements
ext-session-lock-v1 so sessiond can take the lock through it exactly as it does
against Hyprland today, and logind stays the truth. viewtop gains a lock surface
role it did not have; it does not gain the decision.
Concretely, Registry::new() already starts locked: true with the right
reasoning ("a compositor that comes up unlocked and then corrects itself has
already shown one frame of someone's messages"). That value must arrive from
sessiond's device_state and never be recomputed locally — which SessionFacts
was reshaped for in d1b7a16, mirroring the daemon's reply field-for-field.
What is liftable
Read-only assessment; none of this has been ported yet.
compositor/flutter-engine/— the embedder crate andsys.rsbindings.patches/flutter-engine/3.44.7/— the eleven-patch series. Several are directly relevant to Adreno/freedreno, not incidental to x86:0002stencil for GL surfaces,0003/0004/0006DMSAA wrapped-FBO lifetime and partial load,0005XRGB scanout described as RGB8.kms_state.rs— DRM/KMS output.- The external-texture → GBM atlas composition path, which is the core idea.
wayland_frontend/— protocol handling, input routing, window management.protocol/denial.fbs+protocol/golden/— a FlatBuffers wire protocol with golden files. The shape is worth copying and so is the discipline.- The pattern of checking a session predicate at input and clipboard boundaries. Keep the seam; change what feeds it and who it excludes.
What must be replaced
authentication.rsin full — PAM belongs to sessiond, which already owns it.secure_session_locked()as internal truth — becomesext-session-lock-v1served to sessiond, with facts flowing back throughSessionFacts.
The design fork still open
denial loads the shell as an AOT Dart bundle inside the compositor process,
not as a separate Wayland client. That buys hot-reload and no IPC, and it means
a Dart exception can take the display with it. viewtop's wire crate implies a
boundary. On a daily-driver phone that boundary has value — quickshell crashes
today and Hyprland survives it — but it costs the thing denial's development
mode is built around. Not settled; settle it before compositor and
shell-host stop being placeholders.
Connects to
SESSION-AUTHORITY-DOCTRINE.md §1 (Wayland pulled session authority into the
compositor), §4 (never hold state the protocol owns), §13 (the 60/40 and its
user column); DEVICE-STATE-MACHINE.md §10/§11 (the guarantees that depend on
sessiond seeing everything); TASK-25 (viewtop is not packaged and neither are
the surfaces it would replace); TASK-41 (nothing yet proves which binary is
talking to sessiond, and a compositor is a much larger thing to attest).