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.
147 lines
7.4 KiB
Markdown
147 lines
7.4 KiB
Markdown
# 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 and `sys.rs` bindings.
|
|
- `patches/flutter-engine/3.44.7/` — the eleven-patch series. Several are
|
|
directly relevant to Adreno/freedreno, not incidental to x86: `0002` stencil
|
|
for GL surfaces, `0003`/`0004`/`0006` DMSAA wrapped-FBO lifetime and partial
|
|
load, `0005` XRGB 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.rs` in full — PAM belongs to sessiond, which already owns it.
|
|
- `secure_session_locked()` as internal truth — becomes `ext-session-lock-v1`
|
|
served to sessiond, with facts flowing back through `SessionFacts`.
|
|
|
|
## 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).
|