The body tool now speaks the usbHands IPC; the gates stay as refusals, and what still binds is the authority seam itself, not her verbs.
252 lines
13 KiB
Markdown
252 lines
13 KiB
Markdown
# TASK 62 — full USB KVM: the phone becomes the glass and the hands
|
|
|
|
**Status: the base composite and pkgrel 5 phone install are green; USB Hands
|
|
and usb-signaller pkgrel 6 are source-only; cable acceptance and authority
|
|
enforcement remain open.** Opened 2026-08-07. Souveraine Gitea run 1237 built
|
|
the receiver; Pixel3Arch run 1243 built, signed and published the full device
|
|
archive. Those runs predate the persistent HID controller described below.
|
|
This is not a completed task until that source is built and landed and the
|
|
landing order and authority section below are accepted on hardware.
|
|
|
|
This task owns the one-cable composite from mode request through rollback and
|
|
the first hardware acceptance. It does not own phone-as-host power or Type-C
|
|
negotiation; TASK-58 does, and remains open.
|
|
|
|
## Contract
|
|
|
|
The attached computer is the host. It sends a GUD display into the Pixel 3.
|
|
The phone draws that display in a Wayland surface and returns keyboard,
|
|
pointer and touch through boot-protocol HID. NCM keeps a control lane and smoo
|
|
offers host-backed storage through FunctionFS/ublk.
|
|
|
|
One mode change owns all four functions:
|
|
|
|
```
|
|
kvm_mode = ffs.gud + ffs.smoo + ncm.usb0 + hid.keyboard + hid.pointer
|
|
```
|
|
|
|
`usb-signaller` prepares both FunctionFS mounts, starts both responders, waits
|
|
for both readiness signals, then binds the UDC. Any failure tears the partial
|
|
mode down and restores the previously working USB posture. The shell never
|
|
writes configfs: it asks sessiond, which records an `Action::UsbMode` and calls
|
|
the system D-Bus mechanism.
|
|
|
|
## Where the work lives
|
|
|
|
| Layer | Canonical path | State |
|
|
|---|---|---|
|
|
| Receiver + display + HID | `souveraine/src/bin/souveraine-usb-kvm.rs` | built ARM64 |
|
|
| Session authority | `souveraine/src/sessiond/{protocol,device_state,server}.rs` | route + audit built; authorization is **not finished** |
|
|
| Human surface | `souveraine/surfaces/quickshell/` | installed base leaves; conditional USB Hands controller exists only in current source |
|
|
| Souveraine package | `souveraine/packaging/arch/PKGBUILD.prebuilt` | receiver included on aarch64 |
|
|
| Gadget mechanism | `usb-signaller/` | full composite installed; persistent `usb-hid-inject stream` exists in package source only |
|
|
| Device packages | `Pixel3Arch/pkgs/{usb-signaller,smoo,blueline-usb-gadget}` | signed pkgrel 5 archive installed; usb-signaller pkgrel 6 is declared but unbuilt |
|
|
|
|
The separate local `souveraine-usb` repo is a safety copy from the first
|
|
layout attempt, not a source of record. Do not build another package from it.
|
|
|
|
The HID branch deliberately separates the mechanism from its human surface.
|
|
`USB control link` composes or releases the keyboard, pointer and NCM
|
|
functions. `Agent hands` now joins the existing Face surface to that wire when
|
|
the screen is unlocked: the lower room exposes the current agent's text and
|
|
voice paths, new/resume thread controls, a trackpad and scroll strip, explicit
|
|
clicks, a summoned host keyboard with quick keys, and a held
|
|
Ctrl+Alt+Delete. It remains one Souveraine conversation rather than a compact
|
|
controller app beside her.
|
|
|
|
`HidController.qml` owns one `usb-hid-inject stream` process while Hands is
|
|
joined. It batches pointer motion, queues text/key/click reports, consumes the
|
|
helper's `ready` and per-command `ok`, writes `release` on close, stops the
|
|
helper when `UsbState` raises `busy` for a mode switch, and stops on secure
|
|
lock. The helper validates each `type` command before its first report so a
|
|
rejected character cannot leave half that command on the host. The page never
|
|
opens `/dev/hidg*`, and the controller never writes configfs.
|
|
|
|
That is the **current source shape, not landed evidence**. Pixel3Arch now
|
|
declares `usb-signaller 0.3.1-6` with the stream helper, and the Souveraine
|
|
phone deploy path now stages tracked `assets/face` into
|
|
`~/.souveraine/face` before switching QML. Neither change has a Gitea package
|
|
result, phone install/deploy, or motion-on-glass proof yet.
|
|
|
|
## Verification already earned
|
|
|
|
- Receiver unit tests: RGB565 dirty-rect conversion and HID usage mapping.
|
|
- Receiver strict Clippy: clean.
|
|
- ArchDev full release build: ARM64 PIE, 3,893,576 bytes,
|
|
SHA-256 `1fb58409cb1923599bcbcf229d1d2c1a61d4d7b2143387f21e7c6043ec44622d`.
|
|
- ArchDev sessiond build: ARM64 PIE,
|
|
SHA-256 `46046a53231b4f838fb47d8a8e75a51c6c8471ea3ccd2bc43bbf7ced2e38841a`.
|
|
- Mode scripts parse, the source/package copies match, and the Casey-owned
|
|
runtime directory, environment file and `/dev/hidg*` nodes are reachable by
|
|
the unprivileged receiver.
|
|
|
|
These earned results cover the receiver, composite and pkgrel 5 one-shot HID
|
|
helper. They do **not** cover pkgrel 6, the persistent stream protocol,
|
|
`HidController.qml`, the joined Face controls or the new face-asset deployment
|
|
path.
|
|
|
|
`gud-gadget` has no declared licence. Casey explicitly accepted that risk for
|
|
this private build. Do not publish its binary into a public archive until that
|
|
is resolved.
|
|
|
|
## Not finished — Session Authority
|
|
|
|
Routing a request through sessiond is not the same thing as making sessiond the
|
|
sole authority. Today the shape is correct but the gate is incomplete:
|
|
|
|
- `usb` is an ambient, read-only projection and can stay that way;
|
|
- `set_usb_mode` is named and audited, but currently admits every supported
|
|
device-role mode;
|
|
- `attached_identity` and `probe_owner` are stable fields whose values are
|
|
still `null` — federation and the probe lease registry do not feed them yet;
|
|
- the held-power surface is intentionally available while locked, so HID/KVM
|
|
changes currently have no credential or known-peer gate;
|
|
- USB Hands itself requires an unlocked screen and a deliberate `Agent hands`
|
|
join before its same-session IPC verbs can inject. That is a useful local
|
|
guard, not a substitute for peer authority or closure of the direct D-Bus
|
|
bypass;
|
|
- usb-signaller's system D-Bus method can still be called around sessiond by
|
|
another locally permitted process. The authority is advisory until mutation
|
|
is restricted to the attested/sessiond path.
|
|
|
|
The intended rule is not "the agent cannot operate USB." The agent owns device
|
|
operation. A known federated peer may receive a scoped policy that allows the
|
|
agent to compose the port without repeatedly asking. An unknown peer, a new
|
|
export, or physical-access posture needs user step-up; `charging_only` remains
|
|
an always-available safe contraction. Add these operations to
|
|
`TRUST-BOUNDARY-MATRIX.md`, emit `not_permitted`/`refused_by_state` from real
|
|
checks, and close the direct D-Bus bypass before calling the authority seam
|
|
finished.
|
|
|
|
Sam's proposed privileged `smoo-host` D-Bus service is the host-side mirror of
|
|
this boundary, not its replacement: smoo owns export mechanics; Session
|
|
Authority owns whether this peer, export and moment are allowed.
|
|
|
|
### Agent reach landed — 2026-08-15 (`souveraine f87d912`)
|
|
|
|
The body tool now carries a `hand` verb that speaks the `usbHands` IPC directly
|
|
(quickshell remains the one owner of the gadget; the tool adds no writer). She
|
|
can ask `body hand status` before the surface is joined, and `type`/`key`/
|
|
`click`/`pointer` once it is. The intended rule above is now partly real: the
|
|
agent has the verbs and the gates — locked glass, unjoined surface, unarmed
|
|
mode — come back as refusals she can read and open, never as her own limits.
|
|
What still binds is the authority seam itself: no credential/known-peer gate on
|
|
the mode change, and the direct usb-signaller D-Bus bypass is still open.
|
|
|
|
## Management tether — one owner, hardware proof still owed
|
|
|
|
Live inspection on 2026-08-07 found and removed the last obsolete static
|
|
owner:
|
|
|
|
- `usb-gadget-net.service` was disabled/inactive but still existed unowned in
|
|
`/etc`, with an unowned `/usr/local/sbin/usb-gadget-net`; both were removed
|
|
from the phone and the stale rootfs enable link was deleted;
|
|
- usb-signaller is now the sole configfs gadget owner;
|
|
- `blueline-usb-gadget 1.0.0-3` owns `40-usb0.network` and the one-shot stale
|
|
interface flush, which ran successfully at boot;
|
|
- commit `Pixel3Arch 8c9c50f` adds that previously hand-installed package to
|
|
the Gitea publisher as `1.0.0-3`, so the fix no longer depends on this one
|
|
phone remembering it.
|
|
|
|
The phone was in `charging_only` during inspection, so this is ownership and
|
|
boot-graph proof, not a fresh SSH-over-NCM proof. Acceptance still requires a
|
|
cold boot into developer mode with exactly one live `usb*` interface holding
|
|
`172.16.42.1`, no competing link-down route, and a successful SSH connection
|
|
over the cable.
|
|
|
|
## Separate and still open — Type-C/TCPM
|
|
|
|
The full KVM composite keeps the phone in USB device role; its green build says
|
|
nothing about phone-as-host negotiation. TASK-58 owns the
|
|
`qcom-pmic-typec-smb2` port, CC/TCPM negotiation, internal SMB2 OTG regulator
|
|
and source VBUS. That work is expected to materially improve negotiation and
|
|
unlock proper C-to-C host behaviour. Do not let successful gadget-mode
|
|
acceptance close or demote it.
|
|
|
|
## Phone landing — 2026-08-07
|
|
|
|
Installed with one normal `pacman -Syu` after Casey deliberately enabled the
|
|
split-tunnel `wiufph` profile from the foreign `10.10.30.x` network:
|
|
|
|
- `souveraine r387`, `usb-signaller 0.3.1-5`, `smoo 0.0.2rc6-1`, and
|
|
`blueline-usb-gadget 1.0.0-3` are installed and own their expected files;
|
|
- the shell snapshot deploy landed revision `9ceaf61`; its USB state service,
|
|
Power Options surface and option row hash-match the repo;
|
|
- live mode `developer_mode`, data role `device`, charger online;
|
|
- advertised modes include `hid_mode` and `kvm_mode`;
|
|
- KVM and smoo responders remain inactive until a mode request.
|
|
|
|
That remains the last proven phone state. It contains the one-shot pkgrel 5
|
|
helper and the pre-Hands shell; the current pkgrel 6 package source and joined
|
|
Face controller have not been installed or deployed.
|
|
|
|
The first restart still advertised the old mode table. The package was correct;
|
|
an unowned `/etc/systemd/system/usb-signaller.service.d/local-fix.conf` forced
|
|
the service back to an unowned July 21 `/usr/local/bin/usb-signaller`. Both
|
|
stale files were removed. The running service now executes the package-owned
|
|
`/usr/bin/usb-signaller`. Neither stale file existed in the canonical rootfs.
|
|
|
|
Still true from the pre-landing inspection:
|
|
|
|
- the legacy static gadget is removed;
|
|
- `charge_control_end_threshold=99`; no start-threshold attribute.
|
|
|
|
The shell's KVM button is now enabled by live feature discovery, not a version
|
|
guess.
|
|
|
|
### First HID cable proof — 2026-08-07
|
|
|
|
With `USB HID Injection` selected, the device controller reported
|
|
`state=configured`; `/run/souveraine-usb/{keyboard,pointer}` resolved to the
|
|
live `/dev/hidg0` and `/dev/hidg1` endpoints. From the unprivileged phone
|
|
session, `usb-hid-inject type` wrote `Rook was here. The wire has hands.` into
|
|
the focused host field exactly. That closes host enumeration and keyboard
|
|
report delivery for the one-shot helper. It does not exercise
|
|
`usb-hid-inject stream`, the Face controller, pointer motion, secure attention,
|
|
or command acknowledgements. Pointer report delivery and the return to
|
|
developer USB are still owed before landing-order step 3 is complete.
|
|
|
|
## Landing order
|
|
|
|
1. **Base done; USB Hands pending.** The receiver, smoo and usb-signaller
|
|
pkgrel 5 packages are published and installed. Build, sign, publish and
|
|
install usb-signaller pkgrel 6 before exercising the persistent controller.
|
|
Keep Wi-Fi SSH alive; do not make a USB mode change the only recovery path.
|
|
2. **Base done; USB Hands pending.** `get_modes` includes
|
|
`hid_mode,kvm_mode` and the installed base files are package-owned. Deploy
|
|
the joined Face/HidController shell through its declared phone path, verify
|
|
the tracked face assets landed with it, and record the deployed revision.
|
|
3. Enter HID mode first. Confirm keyboard and pointer enumerate on the host;
|
|
then prove persistent text, trackpad, scroll, clicks, quick keys and held
|
|
Ctrl+Alt+Delete from the joined Face. Confirm a rejected character causes no
|
|
partial text within its host-input line, leaving/locking releases the
|
|
reports, and developer mode restores cleanly.
|
|
4. Enter KVM mode. Confirm host enumeration for GUD, both HIDs, NCM and smoo;
|
|
then confirm a host frame reaches the phone and touch returns to the host.
|
|
5. Pull the cable and force one responder failure. In both cases the daemon
|
|
must stop services, release FunctionFS, and leave or restore a usable mode.
|
|
6. Only after those checks make KVM a normal daily-driver choice.
|
|
|
|
No USB Hands phone mutation belongs before step 1's pkgrel 6 package exists in
|
|
the signed pipeline. No flash belongs to this acceptance path.
|
|
|
|
## Acceptance
|
|
|
|
- A host frame is visible on the phone; resize redraws without a blank buffer.
|
|
- Physical keyboard, mouse and touch in that surface control the attached host.
|
|
- NCM remains usable and smoo reaches its ready endpoint.
|
|
- Closing the surface, unplugging, or a failed responder leaves no stuck UDC,
|
|
root-only endpoint or dead management port.
|
|
- The held-power sheet reports the actual mode and returns to developer USB.
|
|
- `Agent hands` can be joined only for an unlocked, advertised HID path; its
|
|
joined Face controls work through one persistent acknowledged helper, and
|
|
leaving or locking closes that helper without a held key or pointer button.
|
|
- The deployed QML and tracked face page arrive together; no manual
|
|
`stage-face.sh` copy is needed to make the controls appear.
|
|
|
|
## Connects to
|
|
|
|
`USB/README.md` (exploration and host-mode record), TASK-30 (verb tables),
|
|
TASK-33 (adjacent charging policy), TASK-50 (the inverse hand-on-glass path),
|
|
TASK-57 (phone-as-gadget dock), TASK-58 (host-role VBUS), SAF federation
|
|
(attached identity and probe ownership).
|