docs: scope LibrePods authority path
This commit is contained in:
parent
47a4bfc920
commit
c5401f3393
2 changed files with 119 additions and 0 deletions
118
docs/tasks/74-librepods-as-an-admitted-accessory.md
Normal file
118
docs/tasks/74-librepods-as-an-admitted-accessory.md
Normal 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`.
|
||||
|
|
@ -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. |
|
||||
|
|
|
|||
Loading…
Reference in a new issue