Skip to content

feat(genebean): extract chromium-kiosk service module#694

Merged
genebean merged 2 commits into
mainfrom
feat/kiosk-entryway-shared-module
Jul 19, 2026
Merged

feat(genebean): extract chromium-kiosk service module#694
genebean merged 2 commits into
mainfrom
feat/kiosk-entryway-shared-module

Conversation

@genebean

@genebean genebean commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Summary

  • Extracts the cage+chromium kiosk pattern duplicated between kiosk-gene-desk and kiosk-entryway into a reusable genebean.services.chromium-kiosk module (home-manager side owns the options - dashboard URL, GUI rotation, wlr-randr output, extra chromium flags; the NixOS side reads them back to configure services.cage and systemd.services.cage-tty1.wants), following the same dual-module convention already used for tailscale/flatpak.
  • Pure refactor - no behavior change for either host. Host-specific concerns (fbcon console rotation, zramSwap, user account block) intentionally stay in each host's own config, not folded into the module.
  • A future third kiosk now only needs a few option values in its home-<user>.nix, not a copy-pasted cage/chromium block.

Test plan

  • nix flake check passes for both hosts
  • Evaluated services.cage.program script content and systemd.services.cage-tty1.wants confirmed byte-for-byte identical to pre-refactor for both kiosk-gene-desk (built locally) and kiosk-entryway (derivation inspected directly - x86_64-linux, no local builder for it)
  • Full nixosConfigurations.kiosk-gene-desk toplevel build succeeds
  • pre-commit run -a passes
  • Deploy to both hosts and confirm the kiosk still displays correctly on hardware

kiosk-gene-desk and kiosk-entryway each ran an identical cage + chromium
kiosk pattern (a writeShellScript "kiosk.sh" doing wlr-randr then
exec'ing chromium, plus a systemd.services.cage-tty1.wants block), and
each host's home-gene.nix carried a near-identical
programs.chromium.commandLineArgs list - differing only in the
dashboard URL, whether the screen is rotated, one extra chromium flag
(--hide-scrollbars, kiosk-entryway only), and the wifi interface name
used in the cage-tty1 dependency (wlan0 vs wlp3s0).

Adds genebean.services.chromium-kiosk under services/ (not programs/) on
both the home-manager and NixOS sides, matching this repo's existing
split - services/ is for things that manage a services.*/systemd-unit-
backed daemon on both sides (flatpak, tailscale), which is what this
module's NixOS half does (services.cage, systemd.services.cage-tty1).
Following the same tailscale.nix/flatpak.nix convention, the
home-manager module owns the options (dashboardUrl, extraCommandLineArgs,
wlrRandrOutput, rotate) and the NixOS module reads them back via
config.home-manager.users.${username}.genebean.services.chromium-kiosk -
so a host only specifies these values once, in its home-<user>.nix, not
duplicated across two files. The wifi interface for cage-tty1.wants is
read from the already-declared networking.wireless.interfaces list
rather than becoming a third option naming the same thing.

fbcon console rotation (kiosk-gene-desk's boot.kernelParams, Pi-specific
and independent of cage's own GUI rotation per its existing comment) and
the zramSwap/users.users.${username} blocks stay host-specific - genuinely
separate concerns, not chromium/cage-specific ones this module should own.

Pure refactor, not a behavior change: verified both hosts' evaluated
services.cage.program script content and systemd.services.cage-tty1.wants
are byte-for-byte identical to before (kiosk-gene-desk built locally;
kiosk-entryway's derivation inspected directly, since it's x86_64-linux
and this workstation's linux-builder VM is aarch64-linux only).
@genebean
genebean force-pushed the feat/kiosk-entryway-shared-module branch from d1b83c4 to ad21005 Compare July 19, 2026 12:06
Two things needed fixing together, both found on hardware
(kiosk-entryway) while validating this actually works:

Upstream's cage module sets restartIfChanged = false, presumably to
avoid yanking an interactive desktop session out from under someone
mid `nixos-rebuild switch`. That caution doesn't apply to a kiosk -
without this override, a config change (e.g. a new dashboard URL)
requires a manual `systemctl restart cage-tty1` or a reboot to
actually take effect, since the switch itself silently leaves the old
chromium/cage instance running.

But turning restartIfChanged on alone made things worse, not better:
NixOS's activation reactivates TTY-related units on every `switch`
regardless of whether anything relevant changed, and (re)starts
getty@tty1.service even though it shows "disabled" - cage-tty1's
existing Conflicts=getty@tty1.service (set by upstream's cage module)
then kills it as a side effect, and nothing re-triggers it afterward
(WantedBy=graphical.target doesn't get re-evaluated once that target's
already reached), leaving the kiosk dark until a manual restart. This
happens independently of restartIfChanged and made the failure mode
worse once enabled, since now there were two competing stop/start
cycles racing in the same transaction.

Masking getty@tty1 outright - the standard practice for a TTY a
compositor owns exclusively - removes the conflict at its source
instead of just reacting to it, except getty@.service's own upstream
unit carries Alias=autovt@%i.service, so masking the getty@tty1 name
alone doesn't stop systemd resolving the same underlying unit via its
autovt@tty1 alias instead - confirmed on hardware, masking only
getty@tty1 still left autovt@tty1.service starting at the exact moment
cage-tty1 died. Both names need masking.

Validated with two consecutive `nixos-rebuild switch` runs against
kiosk-entryway (one that actually changed cage-tty1's unit, one no-op)
- cage-tty1 restarted cleanly on the first and was left completely
untouched on the second, matching expected behavior both times.
@genebean
genebean force-pushed the feat/kiosk-entryway-shared-module branch from 8b24b31 to 786481c Compare July 19, 2026 12:45
@genebean
genebean merged commit 5e95594 into main Jul 19, 2026
2 checks passed
@genebean
genebean deleted the feat/kiosk-entryway-shared-module branch July 19, 2026 12:49
genebean added a commit that referenced this pull request Jul 20, 2026
Both kiosks were ~95% duplicated below the chromium/cage layer already
extracted in PR #694. Adds two new genebean modules following established
patterns:

- genebean.kiosk-hardware (top-level meta-module, same tier as
  genebean.plasma/genebean.ports - not a program or service): the
  byte-identical hardware/networking/user/zram boilerplate both hosts
  shared, parametrized by a single wirelessInterface option.
- genebean.services.kiosk-backups: the restic disaster-recovery policy
  (paths, pre-reinstall-cleanup, kiosk-restic-full-restore wiring)
  kiosk-gene-desk already had, now shared - kiosk-gene-desk migrates to
  it, kiosk-entryway gains it for the first time. Paths are host-agnostic
  (no /persist prefix); the NixOS side detects impermanence
  (environment.persistence."/persist".enable) and prefixes automatically,
  enforced by an assertion against passing an already-prefixed path.

genebean.services.chromium-kiosk gains its own wirelessInterface option
(explicitly wired from kiosk-hardware's value at each call site) instead
of implicitly deriving from networking.wireless.interfaces, avoiding two
places setting the same value.

Also closes a real gap: kiosk-gene-desk had no fonts configured at all
despite rendering the same CJK/emoji-heavy Home Assistant dashboards as
kiosk-entryway - added to chromium-kiosk.nix itself so it applies to any
host enabling the kiosk service, not just these two.

nixpkgs.overlays (the makeModulesClosure allowMissing fix) stays per-host
rather than moving into kiosk-hardware - it helps construct `pkgs` itself,
so gating it behind a home-manager-sourced cfg.enable check creates a
genuine circular dependency (confirmed via a real "infinite recursion"
eval error when first tried).

Verified via deploy-rs on both hosts: dry-activate, real deploy, and
post-deploy health checks (system running, cage-tty1/dbus healthy, restic
timers scheduled, backup paths unchanged on kiosk-gene-desk).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018iUoDbZzHLBGu7jD3pVGz2
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