Skip to content

Keep-branded read-only status display on the physical console #101

Description

@wksantiago

Motivation

An installed node boots to a bare kernel log dump and a keepnode login: prompt where every account is password-locked. There is no branding and no operator visibility on the installed system — the only MOTD in the repo is on the ISO (nixos/installer.nix:121-133).

If the vault fails to unlock or the mesh never forms, a person standing at the box learns nothing. They need working SSH to diagnose why SSH might not be working.

Scope

A Keep-branded, read-only status screen on the physical console. Off by default.

Displays: vault unlock state, Vaultwarden, mesh, replication lag, anti-lockout status, hostname, and the SSH command to reach the node.

Explicitly not in scope: no interactive actions, no desktop environment, no login. Physical console access currently grants nothing and must continue to grant nothing.

Technical details

Two processes split on privilege:

  • Collector — root, oneshot + 5s timer, writes an atomic world-readable snapshot to /run/keep-node-status/status.json (0644, tmpfs).
  • Renderer — unprivileged keep-status user (nologin, locked password), reads only that JSON and paints tty1. StandardInput=null, so the process holds no fd on the keyboard at all.

New files: nixos/lib/brand.nix (typographic mark, shared with the installer MOTD), nixos/status-display.nix, tests/status-display.nix. Namespace keepNode.statusDisplay.

Console backend only. A cage/foot Wayland kiosk was evaluated and rejected: foot binds ctrl+shift+n to spawning $SHELL, and a compositor adds a seat and a keyboard input consumer to a box with neither. The backend option ships as enum [ "console" ] so a framebuffer backend can be added later without a rename.

Blocker to resolve in implementation

nixos/lib/key-check.nix:48 writes the ANTI-LOCKOUT message to /dev/console — the same VT this feature owns. A repainting renderer would erase it. Anti-lockout state must be a first-class field in the snapshot and render as a full-width alarm banner.

Acceptance criteria

  • Off by default; enabling it does not alter any existing config
  • Renderer runs unprivileged with StandardInput=null, verified via /proc/<pid>/fd/0/dev/null
  • No password auth, no new listening port, no new daemon reachable from the network
  • Anti-lockout state renders as a full-width alarm and is never erased by a repaint
  • Stale snapshot replaces all values with ?? — a dead collector must never leave UNLOCKED on screen
  • Rotating heartbeat glyph distinguishes a live screen from a frozen one
  • A unit belonging to a disabled module renders n/a, not failed
  • ANSI escapes in a collected field cannot repaint or clear the screen
  • Degrades to ASCII and no-color on serial/vt100; width clamped and centred
  • Assertion refuses debugAccess + tty1 (both claim the VT via getty.autologinUser)
  • VM test asserts real frame content; pure-eval guard includes control cases
  • FROST threshold model untouched

Design notes

Status is triple-coded — glyph + word + colour — so it survives colorblindness, monochrome, and serial terminals. Glyphs are constrained to CP437/Lat2-Terminus16 coverage (× U+00D7, not U+2715, which is absent from the console font).

Mesh address and peer count default off: they are topology intel rendered to whoever is standing in front of the box.

The documented-but-unimplemented console break-glass path (claimed in README, docs/SECURITY.md, docs/deployment.md, nixos/yubikey.nix) is a separate concern and is not addressed here.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestp3Lowest Priority

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions