Watch
1
0
Fork
You've already forked SouveraineOS
0

docs: scope LibrePods authority path

This commit is contained in:
Fimeg 2026-08-13 15:16:14 -04:00
commit c5401f3393
2 changed files with 119 additions and 0 deletions

View file

@ -0,0 +1,118 @@
# TASK 74 — LibrePods: an admitted accessory, not a second shell
**Status:** scoped 2026-08-13; no shell/device proof yet. **Repos:**
`Fimeg/librepods` (AirPods protocol), `Fimeg/souveraine` (session authority +
Quickshell surface), `SouveraineOS` (this contract).
## The decision
LibrePods owns the Bluetooth/AACP protocol and the per-headset state. It is an
application producer, **not** a new authority component and never a second
shell. The attractive AirPods opening surface belongs in Souveraine's
Quickshell tree.
```
LibrePods protocol producer
│ admitted accessory event
sessiond — identity, policy, audit, presentation admission
│ one permitted presentation event
Quickshell — QML projection only
```
No LibrePods process writes `GlobalStates.qml` or calls Quickshell IPC. Those
paths have no caller identity today; doing so would recreate the ungated
producer problem demonstrated in TASK-41.
The existing `ii-base/DeviceConnectNotification.qml` is evidence, not the
implementation: it is a generic four-second toast and the current phone
`shell.qml` does not instantiate it. The AirPods card is a new, deliberate
surface with its own owner and lock-tier policy.
## What is real today
- `Fimeg/librepods` is a private Gitea mirror on `linux/rust`, with GitHub
retained as upstream and Gitea Actions as its build path.
- Its Rust branch opens BlueZ L2CAP/AACP, reads battery and proximity keys, and
has an initial disconnect/reconnect cleanup commit (`a629229`).
- The LE monitor reads encrypted advertising payloads, but no source or packet
fixture proves a distinct **case-open** transition. “Bluetooth connected” is
not an acceptable substitute for the opening animation trigger.
- `souveraine-sessiond` already owns the user-tier Unix socket, device state
trail, and shell-facing subscription path. It currently has no accessory
event/admission verb.
## The contract to build
### 1. Protocol truth — LibrePods
LibrePods produces a bounded `AccessoryObservation`; it does not decide what
the screen does. The first useful fields are a stable local accessory id,
connection state, left/right/case charge, charging state, and an evidence tag
for the observed AirPods transition.
Capture and redact real packet fixtures before naming an observation
`case_opened`. The fixture test proves parser behaviour without an AirPods
pairing session. Keys, IRKs and decrypted advertising payloads are secrets and
never appear in logs, fixtures, the audit trail, or the QML payload.
### 2. Admission — sessiond
Add an explicit, narrow presentation request to sessiond rather than a
general-purpose shell call. It must:
- identify the producer and ultimately require TASK-41's capability grant;
- reject an unadmitted event with `not_permitted` and record that refusal;
- record an admitted event in the existing forensic trail;
- classify the requested payload as ambient or personal before it reaches the
shell;
- never treat an accessory event as human input, a wake request, an unlock
factor, or a device-state transition.
Until TASK-41 lands, any same-uid development route is explicitly an
unattested diagnostic path, not a deployed authority claim.
### 3. Projection — Quickshell QML
The shell subscribes to sessiond's admitted event stream and owns the one
`AirPodsSurface` state. It renders the case-open card when the display is
already active; an accessory opening does not wake the panel or compete with
lock/power policy.
While `screenLockSecure` is true, default to an ambient announcement only.
Whether device name and individual battery levels are ambient is a policy call
to make explicitly before exposing them on the lock surface.
The card is allowed to be gorgeous: the case opens, buds settle, charge
arrives in sequence, and the card melts away into the ordinary Bluetooth
controls. Its animation is a projection of an admitted event, never the event
source.
## Order of work
1. Let LibrePods CI establish the current Rust lifecycle patch; add fixture
tests for the specific advertisement transition only after capture.
2. Define and test sessiond's accessory admission verb and forensic event,
tied to TASK-41's producer grant design.
3. Add the Quickshell `AirPodsSurface` and its single shell-side subscriber.
4. Package the producer and surface through the normal Gitea path; test on
actual AirPods, first unlocked and then locked.
## Acceptance
- One captured, redacted fixture proves the animation trigger.
- LibrePods cannot directly open or mutate any shell surface.
- An unadmitted producer is refused and the refusal is visible in the trail.
- One admitted event renders one QML card; reconnects cannot duplicate it.
- The card never wakes, unlocks, or exposes a Personal-class field while
locked without an explicit policy decision.
- Gitea builds the relevant code; the final behaviour is exercised on real
AirPods and marked separately from CI proof.
## Connects to
TASK-08 (device state), TASK-41 (attested producers), TASK-19 (settings and
surface policy), TASK-25 (packaged delivery),
`docs/DEVICE-STATE-MACHINE.md`, and
`docs/souveraine-components/README.md`.

View file

@ -64,6 +64,7 @@ Four that are cheap relative to what they unblock:
| # | Task | What's left |
|---|------|-------------|
| 74 | [LibrePods: an admitted accessory](74-librepods-as-an-admitted-accessory.md) | **Scoped 2026-08-13.** AirPods protocol stays in LibrePods; sessiond must admit/audit presentation events and Quickshell projects the card. First gate: capture a redacted case-open fixture — connection is not proof of opening. |
| 73 | [One spine, two skins: the self-dashboard](73-self-dashboard.md) | Designed 2026-08-11, nothing built. RedFlag Desktop (Tauri, public) + house Gatehouse (wry + QML glance, lens skeleton), spine is agent-side pacman closure resolution that retires souveraine-updater's `pkexec --noconfirm`. Name and home-repo decisions are Casey's. |
| 71 | [QmlMobileScanner as a components app candidate](71-qml-mobile-scanner-candidate.md) | Recon 2026-08-11, verdict owed. GPL-3.0 Qt Quick scanner+writer (zxing-cpp/QZXing/zint, SQLite history). Compatible inside AGPL; camera gate is TASK-46; ffmpeg backend needs a freedreno check; fontawesome-qml likely **not** needed (no FontLoader in the tree — unverified). |
| 70 | [The island: agent surfacing for the shell](70-the-island-agent-surfacing.md) | Recon 2026-08-11. "Open Agent Island" — end-4 fork (same ancestor as ii-base) with a morphing notch, Claude Code status, and permission Allow/Deny from the notch. Take the mechanisms (AgentService + bridge, 13/13 safety) onto ii-base, not the shell. Twines with TASK-59: the notch can host Annie as a morph state. Adjacent to TASK-69. |