Skip to content

Finit 5: udev/eudev replacement - #491

Open
troglobit wants to merge 45 commits into
masterfrom
devman
Open

Finit 5: udev/eudev replacement#491
troglobit wants to merge 45 commits into
masterfrom
devman

Conversation

@troglobit

@troglobit troglobit commented May 18, 2026

Copy link
Copy Markdown
Collaborator

The changes to Finit's keventd on this branch, in conjunction with libudev-zero, should prove to be a good-enough replacement for many systems.

We might adopt libudev-zero to the project in case that's needed, but hopefully we can bring it some new fresh blood instead 🧛

Important

This branch is still an active work in progress, and it may even be dropped and its feature set be moved to the next branch, which is the canonical branch for all Finit 5 work.

@aanderse

Copy link
Copy Markdown
Contributor

@troglobit this is amazing! this code looks like it is pretty close to providing a full replacement for udev, including udisks support! thanks for your work on this 🙇‍♂️

if you get to a point where you think this is good enough i'm more than happy to test it out, just let me know!

@troglobit

Copy link
Copy Markdown
Collaborator Author

@troglobit this is amazing! this code looks like it is pretty close to providing a full replacement for udev, including udisks support! thanks for your work on this 🙇‍♂️

Glad to hear you like it! 😊

if you get to a point where you think this is good enough i'm more than happy to test it out, just let me know!

It should be fairly complete, except for libudev, which some packages need. You can use libudev-zero as a stand-in though. That's how I've been testing in Infix OS.

I should put something like this in the docs to help:

libudev-zero is a daemonless libudev.so.1 replacement. It reads device properties directly from /sys and /run/udev/data/ without requiring a daemon. Pairs with finit-keventd, which produce /run/udev/data/ records and rebroadcasts uevents on the netlink group libudev-zero monitors.

I'll probably do some commit housekeeping before I port it to the next branch where the D-Bus stuff is. After that I'll have a stab at either a new file format, or the zones idea I had.

@aanderse

Copy link
Copy Markdown
Contributor

It reads device properties directly from /sys and /run/udev/data/

actually libudev-zero doesn't actually read data from /run/udev/data... yet 🤞

there is a fork which added this functionality, but it doesn't exist upstream. i hope upstream would welcome a PR implementing this. obviously this is an important step for us.

You can use libudev-zero as a stand-in though. That's how I've been testing in Infix OS.

i would love to hear more about your user cases and testing! finix makes extensive use of libudev-zero and it works really well for us 👍

@aanderse

Copy link
Copy Markdown
Contributor

i don't have the udev binaries like fido_id in my PATH - this seems to result in a bunch of zombie processes. i don't specifically need these binaries, so would it be fine for keventd to gracefully ignore when a RUN program doesn't exist?

image

@aanderse

Copy link
Copy Markdown
Contributor

i booted into a graphical environment with keventd! greetd, my graphical login manager, initially failed because /dev/dri/... didn't exist, but then restarted and was fine. udev used to deal with this problem via its settle task, but that is discouraged these days - applications are expected to have udev support and subscribe... or something, i guess 🙂

do you have any suggestions for keventd? finit isn't defacto, applications aren't built to expect keventd 😅 so do we need a kevent settle command, or...? any suggestions, thoughts, etc... are greatly appreciated, as well as your experiences so far 🙇‍♂️

@troglobit

Copy link
Copy Markdown
Collaborator Author

Three good points!

libudev-zero: you are right, I was mislead by "a common friend" 😅 I'll fix the docs. The Gardenhouse fork that adds /run/udev/data/ support looks interesting; I hope it lands upstream.

Zombies from missing helpers: bug. I'll make sure to refactor to handle this better, thanks for taking the time to test and report!

Regarding settle: I think we have a really good abstraction with conditions in Finit, so my idea has been to to declare device requirements in the service file. For example:

service <dev/dri/card0> /usr/bin/greetd -- Login manager

Good point about settle being discouraged, I had missed that and just tried to build something that wouldn't require it. Honestly, I didn't quite understand how it worked anyway. Investigating it a bit more it seems they've deprecated it because it races and slows (serializes) the boot process.

Unknowingly I went my own merry way that turned out to be right. E.g., I've added <dev/eth0> support, in addition to the <net/eth0> support we already have. The former fires on interface existing while the latter on link up. I plan to add two more condition classes for hardware without a /dev node, like a switch core or sensor, but they'll follow sort of the udev way:

  • class/<subsystem>/<name>: fires when a sysfs class device appears
  • bind/<driver>: fires when a driver binds to its device

For your greetd case, <dev/dri/card0> should already work once you wrap it as a Finit service (minus bugs).

One more thing about keventd's design: it folds the common udev helpers (path_id, usb_id, blkid, input_id, net_id, hwdb, kmod) into the daemon as compiled-in builtins. Less fork/exec at boot, single binary to audit. Rules that call /lib/udev/; first check the path, and if missing fall back to the matching builtin. So you can drop your own /lib/udev/foo to override a builtin, and missing helpers no-op (after the fix above) instead of failing loud. Since I have no fido device to test with, so I'll leave fido_id as an exercise to the reader.

@aanderse

Copy link
Copy Markdown
Contributor

Regarding settle: I think we have a really good abstraction with conditions in Finit, so my idea has been to to declare device requirements in the service file.

i think you have a very concrete way to solve my problem, i understood this and maybe should have elaborated... what happens if my graphics device flip flops between card0 and card1? is there a more general way to depend on something coming up? i need to do more research into how udev handles this... 🤔

plan to add two more condition classes for hardware without a /dev node

very cool!

One more thing about keventd's design:

ahh! so this is a consistent design in keventd... wonderful! i had noticed it for device ownership and permission assignment, though not for these common helpers - very cool! i think i will go through the code again a bit deeper... all sorts of wonderful things exist in there 😃

thanks @troglobit!

@troglobit

Copy link
Copy Markdown
Collaborator Author

Regarding settle: I think we have a really good abstraction with conditions in Finit, so my idea has been to to declare device requirements in the service file.

i think you have a very concrete way to solve my problem, i understood this and maybe should have elaborated... what happens if my graphics device flip flops between card0 and card1? is there a more general way to depend on something coming up? i need to do more research into how udev handles this... 🤔

Yeah that sounds like you should match on a devpath or similar and create a symlink in an udev rule. We do similar things for gps devices that can be plugged into any USB port, or the other way around provide usb-to-ttl converters with a fixed device name based on the USB port they are plugged into.

One more thing about keventd's design:
ahh! so this is a consistent design in keventd... wonderful! i had noticed it for device ownership and permission assignment, though not for these common helpers - very cool! i think i will go through the code again a bit deeper... all sorts of wonderful things exist in there 😃
thanks @troglobit!

Glad you like it! ☺️

@aanderse

aanderse commented May 29, 2026

Copy link
Copy Markdown
Contributor

still running keventd... it is great 👍 i played around with libudev-zero source a bit... got it to read tags from /run/udev/data... that was neat when paired with pam_uaccess

one question: did you want keventd to do pid notification protocol, or none is fine because what does "ready" even mean for keventd?

@troglobit

Copy link
Copy Markdown
Collaborator Author

still running keventd... it is great 👍 i played around with libudev-zero source a bit... got it to read tags from /run/udev/data... that was neat when paired with pam_uaccess

Wow, by now you've probably logged more miles than me on it 😅 Any chance you can share the libudev-zero patch? I'd love to pull it into my Infix branch.

one question: did you want keventd to do pid notification protocol, or none is fine because what does "ready" even mean for keventd?

Good question, the idea was to provide a reliable hand-over point "/dev is populated, persistent symlinks are live". However, I realized that this had not yet been put into code, so the version you have created the pidfile after coldplug had triggered the kernel to re-emit events, but before keventd had drained them. The changes I just pushed aim to fix that.

In Infix I lean on Finit conditions to set up "barriers". Most services depend on <pid/syslog>, which can be provided by BusyBox syslogd + klogd or my sysklogd project, and syslogd itself depends on <service/keventd/ready>. That chain serializes execution for brief periods at boot, which on Infix is partly there to make sure no logs leak to the console and get lost from log files.

Separately, I've added a keventd -S one-shot command, similar to udevadm settle that just polls /sys/kernel/uevent_seqnum and exits when it's quiet. Mostly for legacy boot scripts that can't be wired to conditions, but might be handy for your testing too.

@aanderse

Copy link
Copy Markdown
Contributor

Any chance you can share the libudev-zero patch?

sure! i only added tag filtering from the udev database... i think if someone were to fill in more udev function stubs there is better ways to structure this code though... 😅

did you implement any other udev functions?


i'll try the new version and report back - thanks!

@troglobit

Copy link
Copy Markdown
Collaborator Author

Any chance you can share the libudev-zero patch?
sure! i only added tag filtering from the udev database... i think if someone were to fill in more udev function stubs there is better ways to structure this code though... 😅

Thanks 👍

did you implement any other udev functions?

Nothing more than the two new condition classes I mentioned before, 5a86ff8 covers that.

i'll try the new version and report back - thanks!

Much appreciated!

@aanderse

aanderse commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

still running... no problems, loving it 😄

please please please merge this before 5.0 because this is the perfect device manager for finix! 🎉

@troglobit

Copy link
Copy Markdown
Collaborator Author

still running... no problems, loving it 😄

please please please merge this before 5.0 because this is the perfect device manager for finix! 🎉

Wow, that's high praise, thank you! 🙇‍♂️

Actually, 5.0 might be closer than you think ... Swedish Midsummer is just around the corner, and with that my vacation. Although a lot of time this year will go to painting and renovations (house), I will need some stimulating code sessions as well. So hang in there! 😅

@aanderse

Copy link
Copy Markdown
Contributor

i was trying to run my system by linking against the gardendevd fork of libudev-zero and realized that in udevdb.c udevdb_devid() keys net devices as +net:<sysname> - libudev expects n<ifindex>

this means net devices are never found in /run/udev/data

my theory is that if keventd follows what the other implementations do then it might be possible to run networkmanager on keventd!

@troglobit

Copy link
Copy Markdown
Collaborator Author

i was trying to run my system by linking against the gardendevd fork of libudev-zero and realized that in udevdb.c udevdb_devid() keys net devices as +net:<sysname> - libudev expects n<ifindex>

this means net devices are never found in /run/udev/data

Should be fixed now. The branch is rebased on the new master with new .conf syntax.

my theory is that if keventd follows what the other implementations do then it might be possible to run networkmanager on keventd!

Interesting! 🤔💡

@aanderse

Copy link
Copy Markdown
Contributor

i found what i think are a few more inconsistencies between /run/udev with keventd vs udev... i will try to provide some additional details at some point

what is your plan for merging this branch? considering that nothing currently uses /run/udev i think you can safely merge this as-is and state that /run/udev data is currently "experimental"... especially since nothing will make use of it yet

does that seem reasonable?

@troglobit

Copy link
Copy Markdown
Collaborator Author

i found what i think are a few more inconsistencies between /run/udev with keventd vs udev... i will try to provide some additional details at some point

That would be great, thank you!

what is your plan for merging this branch? considering that nothing currently uses /run/udev i think you can safely merge this as-is and state that /run/udev data is currently "experimental"... especially since nothing will make use of it yet

does that seem reasonable?

Yeah it does. However, I'll rebase this branch on master as soon as I've finalized and merged the d-bus support I'm currently working on. So it'll unfortunately take a bit more time.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Evolve keventd from a power_supply-only monitor into a full device
manager capable of replacing mdev/mdevd on embedded systems.  This
is the first step towards Finit v5.0 where keventd absorbs devmon.

New capabilities:

 - Parse all uevent actions (add, remove, change, bind, unbind)
 - Create and remove /dev nodes with subsystem-aware permissions
 - Create persistent symlinks in /dev/disk/by-{id,path} and
   /dev/input/by-{id,path}, tracked for cleanup on device removal
 - Load firmware from /lib/firmware/ via the sysfs loading protocol
 - Spawn modprobe for MODALIAS events (async, non-blocking)
 - Coldplug support via -c flag (walks /sys/devices to replay events)
 - Set dev/* conditions for Finit's service dependency system

The original power_supply monitoring and sys/pwr/ac condition are
preserved.

New files: keventd.h (structures/API), uevent.c (all device logic).
The receive buffer is increased to 8K with a 1MB socket buffer to
reduce event loss during coldplug bursts.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Rewrite doc/keventd.md from a 14-line stub into comprehensive
documentation covering all features of the new unified keventd:
device node creation, persistent symlinks, firmware loading,
module loading, coldplug, conditions, and command-line usage.

Update doc/conditions.md to list keventd as the primary provider
of dev/* and sys/pwr/* conditions, with devmon as fallback when
an external device manager is used instead.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
After keventd processes a uevent (creating device nodes, loading
modules, etc.), rebroadcast the original event to netlink group
0x4 so that libudev-zero consumers -- graphical applications,
Wayland/X11 compositors, libinput, and anything else using libudev
to monitor device hotplug -- can receive device events.

Rebroadcast is enabled by default.  Use -g to override the target
netlink group mask, or -G to disable rebroadcast entirely.  Bit 0
(kernel group) is always masked out to prevent feedback loops.

Ref: #451 (comment)
See: https://github.com/illiliti/libudev-zero

Suggested-by: Aaron Andersen <aaron@fosslib.net>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
When started with -c (the default when keventd is the device manager),
gate the pidfile on the kernel's uevent_seqnum having been stable for
200ms.  Up to now ready signaling with the pidfile was done right after
coldplug() triggered the kernel to re-emit events, but before uev_run()
had drained any of them, so <pid/keventd> really only meant "listening
on netlink".

With the gate, services that depend on <pid/keventd> can now assume /dev
is populated and persistent symlinks are live.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
keventd links libblkid, which finit itself does not, but sysroot.mk
only copied the libraries finit links.  Inside the sysroot keventd
then fails to start:

    Service keventd[18] died (exit status: 127)

Collect libraries from finit and everything installed under
libexec/finit/ instead.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
A service with a <class/net/eth0>, or any other keventd-provided,
condition is never started when the device appears.  keventd asserts
the condition file, but devmon only watches /dev, so no cond_update()
ever reaches affected services.  Reload made it worse:
devmon_reconf() clears any registered dev/ condition without a /dev
node behind it.

Watch the dev/, class/, and driver/ condition directories, like the
sys and usr plugins do for their namespaces, and treat an existing
condition file as device presence in devmon_reconf().

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The unified keventd has no automated coverage, only the devmon
fallback is exercised by the test suite.  Network interfaces are
the one device class an unprivileged test can hotplug: the sandbox
has its own network namespace, so 'ip link add' makes the kernel
emit genuine uevents.

Verify keventd readiness, <class/net/IFNAME> driving a service --
and <dev/IFNAME> NOT asserted, interfaces are not device nodes --
libudev-compatible n<ifindex> keying in /run/udev/data, conditions
surviving initctl reload, and cleanup on interface remove.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Once it gives up it forks the post:script and reports that PID as the
service's, so a slay still waiting for the service to come back killed
the script instead, and crashing.sh lost the /tmp/post it checks for.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The legacy INIT_CMD_SUSPEND is refused in runlevel S, 0, and 6, the
bus method suspended unconditionally, even mid-shutdown.  Add the
same guard, replying WrongRunlevel like the reboot family.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The bus method called sm_runlevel() unconditionally.  In runlevel 0
and 6 that aborts an in-flight shutdown, which INIT_CMD_RUNLVL
refuses with a warning, and during bootstrap it switches immediately
where the legacy path defers via cfglevel to the end of runlevel S.

Port both.  A bad runlevel argument still returns InvalidArgs, where
the legacy protocol acks silently: a typed interface rejects garbage.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Manager1.Signal silently skipped stopped services, so the same
command gave different exit codes depending on transport: the legacy
INIT_CMD_SIGNAL path fails when the service is not running.  Mirror
the legacy behavior.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Service1 Start/Stop/Restart discarded the action return value, their
Manager1 twins map it to org.finit.Error.Failed.  Cond1 Set/Clear
replied success even when the condition symlink operation failed,
where legacy initctl exits 73.  Verify the resulting condition state
with cond_get() rather than the noupdate return values, which report
no-change, not failure, and would reject an idempotent re-set.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
initctl -t N reboot arms an emergency bypass timer over the legacy
socket, but the bus methods took no argument, so the timeout was
silently dropped whenever D-Bus was up.

Reboot, Halt, and Poweroff now take a timeout in seconds, 0 for
none, armed via the same shutdown_bypass() the legacy path uses.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Regression tests for the recent handler fixes: bogus SetRunlevel is
InvalidArgs, Signal on a stopped service is Failed, and the reboot
family declares the timeout argument.  Reboot cannot be invoked
without taking down the sandbox, so the latter is asserted via
introspection.  New call-u and call-su modes in dbus-auth-client.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The D-Bus methods carried byte-for-byte copies of api.c's static
start/stop/restart helpers.  Promote them to service.c alongside
service_reload(), which already serves both callers, and reduce both
sides to svc_parse_jobstr-style adapters.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
All three org.finit signals were invisible to generated proxies, and
org.freedesktop.DBus was missing from the standard interfaces even
though Hello, AddMatch, and RemoveMatch are answered.

Add a link_signal_t table to the vtable, emitted like methods and
properties, declare the Manager1 and Cond1 signals, and complete the
static XML with PropertiesChanged and org.freedesktop.DBus.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The path encoding in libink/path.c was untested for identities with
separators; dbus-service.sh only used the bare keventd identity.
Declare a dhcp-client:eth1 service and verify the escaped path, that
the object introspects, and that Identity round-trips.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
initctl_CPPFLAGS is only assigned under the DBUS conditional, but an
automake per-target variable exists even when its conditional is
false, so a --disable-dbus build dropped AM_CPPFLAGS entirely:

    util.c:319:16: error: invalid use of undefined type 'struct FTW'

Assign the base flags unconditionally and append under DBUS.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The D-Bus support is default-enabled, so the HAVE_DBUS paths only
bit-rot silently without this: the leg caught initctl failing to
build with --disable-dbus on its first local run.  Also asserts the
binaries carry no bus references and smoke-runs one non-dbus test.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
dbus.md was orphaned: not in dist_docs_DATA, not linked from the
user guide.  Wire it into the doc dist and link it from the index
features list, features.md, initctl.md, and plugins.md, where the
dbus.so plugin entry now disambiguates the external system bus from
the built-in org.finit API.

Document the 64-peer cap, the supported AddMatch keys, the busconfig
policy file, the legacy-parity edge semantics with the deliberate
SetRunlevel InvalidArgs divergence, the reload-signal behavior of
Service1.Reload, and the reboot family timeout.  Refresh the stale
initctl.md usage paste, add monitor and the D-Bus transport to
initctl(8), add /run/finit/bus to the filesystem layout, and flatten
the ChangeLog D-Bus entry to house style.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Device seqnums are 64-bit; the writer and reader stopped at u32.
Adds t/x/d to the skip path so a{sv} consumers tolerate them.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The park machinery was welded to broker uid resolution; a handler
that cannot answer yet, like a device-settle call waiting for the
event queue to drain, had no way to defer.  link_call_park() holds
the request, link_call_resume() re-runs the handler with
link_call_resumed() reading true, and the expire sweep remains the
backstop for a resume that never comes.

Resume also no longer drops a local caller's kernel group set in the
privileged re-check: group source now keys on broker-ness.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
coldplug_trigger(action, subsystem-glob) replays events for a subset
of devices, the D-Bus Trigger method needs both knobs; coldplug() is
now the ("add", NULL) case.  nftw() has no user cookie, so the
parameters ride in file statics.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
udevdb_read_parent() contained the general capability, keyed from
sysfs when no uevent is in hand.  Split it out as
udevdb_read_devpath() for the D-Bus Info method; parent lookup
becomes a wrapper.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
The udev parity gaps that were blocked on IPC -- settle, trigger,
info, queue introspection, runtime rule reload -- become bus methods.
keventd serves its own socket the way Finit serves /run/finit/bus:
brokerless, libink, one socket per daemon, no forwarding between the
two.

  Settle(u) -> b      parked until the queue drains or the timeout
                      passes; true when settled
  Trigger(s, s)       replay events, action + subsystem glob
  Info(s) -> a{ss}    /run/udev/data properties for a devpath
  RulesReload() -> u  re-read rules dirs, returns rule count
  QueueEmpty (b), SeqnumProcessed (t) properties
  DeviceProcessed (ss) signal after each fully handled event

The queue state is the highest kernel seqnum keventd has handled,
baselined at startup, against /sys/kernel/uevent_seqnum.  keventd -S
now asks the running daemon first and falls back to seqnum polling.
In passive mode Trigger and RulesReload refuse.  Adds
link_call_connection() for the park bookkeeping.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Introspection, queue-state properties, immediate and bus-first
settle, Info by devpath, Trigger(add, net) observed via the
DeviceProcessed signal, and RulesReload -- driven by dummy interface
hotplug like keventd.sh.  New call-ss and call-u client modes, and
getprop learns the b and t variants.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
initctl reload freezes conditions to the old generation and each
owner re-asserts.  Finit's own providers do this in-process; an
external provider whose conditions are generation files, rather than
the oneshot symlinks keventd uses, has no way to know the moment.
Emit Manager1.ConfigReloaded when reconfiguration completes.

keventd needs no subscriber: its conditions are symlinks to the
reconf marker itself, so they read the current generation by
construction and never flux, which the device bus test now pins
down.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Three copies of read-first-line-from-a-file (sysfs_read_file, a
static sysfs_read, fgetline), two condition emitters symlinking the
same reconf marker (cond_emit, sys_cond), two seqnum-stable loops,
and five hand-rolled logit() prototypes, none with a printf
attribute, so no format string was ever checked.

Keep one of each: cond_emit() is exported and mkpaths its parents,
which also retires init_dev_condition_dir() and the sys/ directory
priming loop; logit() moves to keventd.h with the format attribute;
the stability test becomes seqnum_stable(), shared by the coldplug
gate and settle; kev_now_ms() serves dbus.c too.  rule_ctx_free()
and kev_seq_baseline() had one caller each in their own files, now
static.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Half the daemon was missing: -p, -r, -S, -t from SYNOPSIS and
OPTIONS, class/ and driver/ conditions, SIGHUP rules reload, the
rules engine and its directories, /run/udev/data, and the D-Bus
socket in FILES.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
@troglobit
troglobit marked this pull request as ready for review August 16, 2026 20:21
@troglobit troglobit changed the title WIP: udev/eudev replacement for Finit 5 Finit 5: udev/eudev replacement Aug 17, 2026
@troglobit

Copy link
Copy Markdown
Collaborator Author

Remaining work before merge:

  • Test on real¹ hardware
  • Verify D-Bus interface

¹) meaning a real monster

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.

2 participants