Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/tasks/74-librepods-as-an-admitted-accessory.md
2026-08-13 21:02:31 -04:00

6.1 KiB

TASK 74 — LibrePods: an admitted accessory, not a second shell

Status: shell projection visually exercised on the laptop through an isolated development fixture, 2026-08-13; no daemon/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 Souveraine shell has an AirPodsSurface and an AccessoryPresentation holder. On 2026-08-13, an isolated qs -p fixture supplied one synthetic presentation to that same QML and produced a mapped souveraine:airpods overlay on the laptop. That proves the QML projection, placement and animation only: the fixture had no sessiond connection or authority claim. SessiondBridge accepts only the future accessory_presentation directive; no LibrePods path can trigger it today.
  • The laptop's composed QML tree was missing the newly manifested module and singleton, so reloads failed before the shell could load. That composition was repaired on 2026-08-13. AirPodsSurface now keeps Hyprland's focused output when available and falls back to the one announced screen under Membrane. This is source/config repair, not daemon or device proof.

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. Wire the visually exercised AirPodsSurface to the tested sessiond admission path once sessiond emits the admitted event.
  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.