Skip to content

ADR-021 is accepted for sound as built; Bluetooth and input methods are ADR-027's and ADR-028's - #484

Merged
DevomB merged 80 commits into
mainfrom
sound-adr
Oct 15, 2026
Merged

DevomB merged 80 commits into
mainfrom
sound-adr

Conversation

@DevomB

@DevomB DevomB commented Oct 13, 2026

Copy link
Copy Markdown
Owner

ADR-021 still read as proposed though the mixer it describes is built in the laptop stack (#460-#462), and it still decided Bluetooth and input methods, which ADR-027 and ADR-028 now decide differently: no Bluetooth or input method for 1.0, and an input method's virtual keyboard bound to its zone. Its SOF line said the firmware comes from the SOF project's releases, which nothing ships.

What changed:

  • ADR-021 decides sound alone and is accepted: the mixer in zone 0 as its own user in the audio group, a fixed-format socket per zone, the microphone by a gesture to one zone at a time until the zone stops or the user takes it back. It names ADR-027 for Bluetooth and ADR-028 for input methods, and keeps Intel's Sound Open Firmware as a named gap that amends ADR-012 when it comes; its cost says what an SOF laptop has meanwhile.
  • ADR-027 and ADR-028 no longer lean on ADR-021's old text.
  • The Bluetooth design's references to ADR-021 point at the laptop design (the direction) or ADR-027 (the decision).
  • The laptop design says which record decides each part, and that ADR-013 builds in only what boot needs, instead of "builds no sound".

Checked: grep finds no document naming ADR-021 as proposed or for Bluetooth or input methods; each claim in the new text has a round in the stack's sound suite (mixer-own-user, mixer-zones, record-refused-ungranted, status-shows-recording, grant-ends-with-zone, chrome-takes-microphone-back). Docs only: nothing in the image changes.

Decided under the owner's word by linux-distro-d6 (accept ADR-021 for sound as built; ADR-027/028 for the rest; SOF a named gap).

DevomB added 30 commits October 13, 2026 05:51
…or, in the zone that holds it

ADR-025 hands a zone a descriptor on the partition the user gave it, and
needs a reader there that parses the filesystem on that descriptor:
mtools, exfat-fuse and ntfs-3g take paths, and reopening a block device
by path is refused to the zone.

tools/stick is that reader, a crate of its own, since kryptikd keeps to
libc alone (ADR-010). It is fatfs 0.3.6, without chrono, behind
kryptik-stick: no arguments says what the zone was given, then list
[DIR], get PATH [DEST], put FILE [DIR]. The descriptor comes from the
zone's broker (`stick`, answered `ok ro|rw WHAT` with SCM_RIGHTS), or
--device FILE for an image. A read-only grant refuses every write
whatever fatfs asks. Nothing on either side is written over. Names from
the stick reach the terminal with control characters as `?`, and reach a
path only as one component. A tree nested past 32 directories is
refused. A put is synced to the device before it is reported. FAT's
dates come from the zone's clock as UTC, held to 1980-2107.

CI tests it and builds it for musl. The Distro workflow builds it beside
kryptikd, held to the same path check, and stage 04 installs it
(KRYPTIK_STICK_BIN, the stick step). The licence texts of fatfs,
bitflags, byteorder and log are in build/licences, and
check-rust-licences.sh reads its lock.
The stick reader parses a FAT filesystem in the zone, but nothing in zone 0
could hand it a stick: the design (removable-storage.md) has kryptikd make a
device-mapper linear mapping of the one partition the user picks and pass it
to the zone through its broker, never mounting it.

stick.rs decides which block devices may be given (a partition of a USB disk
or SD card, or such a disk with no partition table; held by nothing, mounted
nowhere, not on a disk the system's own mappings use), makes the mapping
read-only unless the user chose to write, records the grant under
/run/kryptik-stick, and on eject wipes the mapping's table so every read and
write on a descriptor the zone kept fails, then removes it once the last
closes. A zone file says `[storage] removable = false` to take no stick; the
vault's does, and the net zone never takes one.

The broker answers `stick` with the zone's descriptor and `ok ro|rw WHAT`;
the launch daemon has `sticks`, `stick-grant ZONE PART ro|rw` and
`stick-eject ZONE` for the chrome, and `info` says what a zone holds; root
has `kryptikd stick list|grant|eject`. A zone's stick goes back when the zone
stops.

Checked with unit tests on a stand-in /sys of the kernel's shape (a USB
partition named with its vendor, model, size and GPT name; a stick with no
partition table; an SD card; an NVMe disk refused; a held, mounted or
system-disk partition refused; bad names), the grant record round trip, the
mapping lookup, the zone key, and the broker parse.
…s suite plugs one in

A stick plugged in now opens a window of the chrome naming each partition
by what sysfs says of it; a letter and a zone's number give it to that zone
to read, or with w to write too, and e with the number takes it back. The
menu's d opens the same window, and its zone lines say which zone holds a
stick. kryptik-launch gains --sticks, --stick-grant and --stick-eject for it.

Eject now writes out what the zone left unsynced, then wipes the mapping's
table and drops what was cached of the stick: a wiped table alone left the
pages the zone had read readable through a descriptor it kept. A stick
pulled out without eject loses its grant the next time the sticks are
listed, which the chrome does whenever a block device comes or goes, and a
grant is refused while a program in the zone still holds the mapping of a
stick taken back from it.

zones-test.sh plugs a FAT stick into the running guest through QMP
(vm-drive.py's plug: and unplug: steps, run-ovmf.sh --usb-port) and runs
guest-tests/stick-check.sh between the plugs: zone 0 mounts and probes
nothing; vault and the net zone are refused; untrusted, given it read-only,
lists it and copies a file off whole, cannot write, and has no node for it;
another zone asking is refused; eject frees the partition at once and a
descriptor the zone kept reads only EIO; what the zone puts with a write
grant is on the image the host reads after pulling it out; a program
holding the stick when it is pulled out reads EIO and the grant ends with
no oops; and the install medium plugged in offers its ESP, which the reader
lists, and its root, which the reader refuses. chrome-sticks.sh checks the
window and the watcher against a stand-in launcher, and vm-drive.py's test
the plug steps against a stand-in QMP socket.
If lvm2's rules ship, 13-dm-disk.rules runs blkid on every device-mapper
device as it appears or changes, so a grant would have zone 0 parse the
stick's superblock as root through kryptik-stick-ZONE, which
59-kryptik-removable.rules keeps eudev from doing on the partition itself.
12-kryptik-stick.rules sets device-mapper's disable flags for those
mappings, by their sysfs name and by the name 10-dm.rules imports.

stick-check.sh's new round, after the grant: eudev holds no ID_FS_
property for the mapping or the partition.
A video call in a zone had the microphone and no camera: the kernel built no
video support. ADR-026 grants the camera as the microphone is granted.

The kernel builds Video4Linux and uvcvideo as modules, filtered to cameras
and test drivers, with no camera button input device; modprobe.d/camera.conf
keeps uvcvideo from loading by alias, and stage 05 checks both. vivid is
built as a module for the zones suite to stand in for a camera, without its
framebuffer; the hardening checker's finding on it is accepted, with why.

kryptikd's camera.rs: a zone that may hold the camera (a new [camera] use
key; vault's file says no, and the net zone never does) gets at start a
directory of its own from a tmpfs that allows device nodes, bound at
/run/kryptik/camera without nodev, /dev/video0 linking into it, and a
Landlock rule for it. A grant loads uvcvideo, binds again what an earlier
grant's end let go of, finds the first capture node in sysfs and makes it
there, owned by the zone's root, mode 0600. Its end, by the user, the
zone's stop or the screen lock, removes the node, unbinds the driver from
the device so a kept descriptor fails, and unloads uvcvideo unless
something still holds it, which the chrome then says.

The launch daemon has camera-grant and camera-revoke, kryptik-launch
--camera-grant and --camera-revoke, root kryptikd camera grant|revoke|
status. The chrome's menu has k<N> and k and a Camera line, a window says
which zone holds the camera for as long as it does, and kryptik-lock takes
the camera back as the screen locks.

Checked: camera.rs unit tests on a stand-in sysfs (the first capture node,
not a metadata one, numbered as numbers, its name shown plainly; the device
and bus to let go of; the record round trip), the zone key, and
chrome-camera.sh against a stand-in launcher. zones-test step 2c runs
guest-tests/camera-check.sh with vivid: no load by alias, no camera before a
grant, vault and net refused, the grant's node and a captured frame, the
zone's stop ending it, another zone finding none, k alone failing a kept
descriptor and unloading uvcvideo, a grant to a running zone. gui-check's
lock round: the screen locking takes it back.
A mapping ejected while its zone still held the stick is removed by
device-mapper once the zone's last descriptor closes, from a workqueue, so
the eject kryptikd runs at the zone's stop can find it and then lose it
between two dmsetup calls, and logged that as a failure. Eject now treats a
mapping that is gone as done, says whether a grant ended rather than
whether a mapping was there, and still clears a mapping left without a
grant.

stick-check.sh's rounds that look for the mapping to be gone wait up to ten
seconds for that removal instead of looking once.
The chrome opens its camera window within a second or two of a grant, and
a window that maps takes the keyboard: granted after the round's zone 0
window had it, the camera window could take it before the lock did, which
the round would read as the lock's doing. Granted first, and waited for,
the window is under the round's own.
A directory tree nested one level past MAX_DEPTH stops the copy off the
stick with the limit named, where a stick built to loop would otherwise
recurse until the zone's stack or disk ran out.
Every walk of the stick's directories, path lookup included, now goes
through one loop that stops with "the stick's filesystem is malformed"
past FAT's 65,536 entries in a directory, or past as many entries or bytes
as the volume has room for in one command. fatfs follows a cluster chain
that loops, so a directory could be endless (list grew its list until the
zone's memory ran out), and directories aliasing an ancestor multiplied
under get, past what the depth limit stops, until the zone's disk was full.
put looks for the name within the same bounds before fatfs walks the
directory to add to it.

A name holding a control character is no file name here: FAT allows none,
and the errors that name a path here carried it to the terminal. Errors
show paths as names are shown, and the marks that turn text around show as
'?' too.

The stick's row in stage 04 sits above kryptikd's comment, which is
kryptikd's.
… as kryptikd is

Stage 06's strict audit flags every static Rust binary for both, and lists
kryptikd and the proxy by name; kryptik-stick is built the same way.
…wn aliases

kmod's modprobe skips a blacklisted module's own aliases with or without
-b, so a dry run by the alias names nothing either way and could not show
the alias belongs to uvcvideo; modinfo reads it from the module.
…he camera

A zone holding the microphone or the camera has a window of the chrome's
that says so, but a window the user puts in full screen covers it, and only
dwl's bar above that window stays in sight: a zone could take a grant, wait
for full screen, and record with nothing on screen saying so.

The bar now reads, after the zone's name, "mic ZONE" and "cam ZONE" for
whichever zones hold them, on every fullscreen window, not only the holder's.
dwl learns them from two files through one inotify descriptor on its event
loop, and draws every fullscreen bar again on a change: the camera's grant
record, which kryptikd writes as root, and the microphone's holder, which
the mixer now writes into its own control directory (uid 87, 0640, group
kryptik) at its start and on every grant and revoke. dwl takes a file only if
it is a regular file of its writer's that no one else may write, and a name
only if it is a zone dwl knows; anything else marks nothing. kryptikd's
launch daemon mounts the camera's tmpfs as it starts, so the compositor's
watch is on it and not on the directory beneath.

gui-test, with vivid as the camera: untrusted fullscreen, granted the camera
and recorded as the microphone's holder, its bar reads "untrusted mic
untrusted cam untrusted" pixel for pixel; both taken back, and a record of
the microphone's holder put in place by root rather than the mixer, the bar
names the zone alone. The sound suite checks the mixer's record on the grant
and once the zone's stop has ended it.
…a directory made anew drops

An inotify watch belongs to the directory's inode: the mixer's control
directory made again, or anything mounted over a watched directory, ends
the watch, and every later grant went unmarked until dwl restarted. dwl now
also reads both records once a second, draws the fullscreen bars again only
when a holder changed, and sets both watches again each time, which keeps a
watch that is there as it is.

The gui round makes the mixer's directory anew before the records are
written, so the marks it checks come through a directory dwl did not first
watch, and puts the mixer's own back after.
…Bluetooth and input methods, and the laptop bullet rewritten
…ooth and input methods, and the laptop bullet rewritten
DevomB added 27 commits October 14, 2026 21:12
…, and the desktop suite checks it

# Conflicts:
#	docs/status.md
…ore lock-0's window opens, and the desktop suite is named beside the zones suite as loading vivid
…out round lists the sticks only once the kernel has let the stick's partition go
…deo0, as no other is granted the camera; the camera suite is named in the image's program list
…ard, so untrusted's fullscreen has ended for Alt+e to give back
…hes, a zone's sched and ioprio calls refused, proxy-calls ending at a proxy's exit
@DevomB
DevomB changed the base branch from zone-bar-marks to main October 15, 2026 12:31
@DevomB
DevomB marked this pull request as ready for review October 15, 2026 12:31
@DevomB
DevomB merged commit 4c06cbf into main Oct 15, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant