Files
Mjolnir/docs/adr/006-module-lifecycle-protocol.md
T
soubarnak f3938174cc docs(adr): add ADR-006 — module lifecycle protocol
Resolves #5. Covers SQLite persistence for validation records and
boot-attempt counter, 120s clean-boot threshold, modprobe.d blacklist
autoload gate, GRUB snapshot recovery entry, interactive validation via
StartValidationRun D-Bus method, and daemon-managed DKMS integration.
2026-07-31 10:28:49 +05:30

5.3 KiB
Raw Blame History

ADR-006: Module Lifecycle Protocol — Validation, Boot-Count Guard, and Recovery

Status: Accepted
Date: 2026-07-31
Deciders: Bongbetic (solo developer)
Source: Task #5

Context

mjolnir-km is an out-of-tree kernel module on the sole reference machine (MSI Thin A15 B7UCX), co-resident with msi-ec. It implements a custom cpuidle governor and telemetry sampler (FR-070, FR-071). Three safety FRs govern its lifecycle: FR-077 (no autoload until validated), FR-078 (boot-count guard — 2 unclean boots disable autoload), and FR-079 (known-good kernel entry preserved as recovery path). The daemon owns the module lifecycle (ADR-001 step 3), and DKMS handles rebuild (ADR-004 AUTOINSTALL=yes).

Decision

Persistence: SQLite

Validation records and boot-attempt counters live in the daemon's SQLite store (ADR-001 step 6 opens before step 3 loads the module — no phase mismatch). Two tables:

  • module_validation(kernel_version TEXT, module_version TEXT, validated_at INTEGER, validation_method TEXT). One row per validated kernel.
  • boot_attempt(boot_id TEXT PK, started_at INTEGER, clean INTEGER, autoload_enabled INTEGER). boot_id from /proc/sys/kernel/random/boot_id. Written on daemon startup before module load. DAU purge retains last 10 boots.

Clean-boot threshold: 120 seconds

Boot is clean when the daemon shuts down gracefully with the module loaded for ≥120 seconds. A kernel module governor fault typically manifests within 3060s of selection — 120s provides 4× polling cycles of observed clean operation. Aligns with systemd TimeoutStartSec=90s. Covers the suspend/resume validation cycle in FR-077.

Autoload gate: modprobe.d blacklist

/etc/modprobe.d/mjolnir-km-autoload.conf containing blacklist mjolnir-km. Managed by the daemon:

  • File exists → auto-load blocked (post-install default; after boot-count guard trips).
  • File removed → auto-load permitted (after validation run or admin re-enables).

Explicit modprobe mjolnir-km by the daemon bypasses the blacklist (it's alias-resolution-only). No DKMS POST_INSTALL script — the daemon owns the policy. DKMS only builds; on first boot of a new kernel, the daemon detects no validation record and writes the blacklist before attempting load.

Known-good kernel entry: GRUB snapshot

just install-km creates a /etc/grub.d/40_mjolnir_recovery script that copies the current default GRUB entry (pre-Mjolnir kernel command line) as a new entry named "Mjolnir-Free (recovery)" with modprobe.blacklist=mjolnir-km appended. Re-runs on every update-grub, re-deriving the latest non-Mjolnir entry. Recovery notes at /usr/share/doc/mjolnir/RECOVERY.md reference it by name.

Validation run: interactive admin flow

FR-077 validation steps (load, governor register/deregister, suspend/resume, clean unload) are sequenced by the daemon via a new StartValidationRun D-Bus method (PolKit-guarded). The suspend/resume step requires admin acknowledgment — not automatable without user consent. The daemon emits ValidationStage signals:

  1. Admin invokes mjolnirctl validate or GTK "Validate Module".
  2. Daemon loads module, sends GOV_SELECT → verifies, sends TELEM_SAMPLE → verifies, sends GOV_DESELECT → verifies.
  3. Daemon emits ValidationStage("suspend_required"). Admin suspends manually.
  4. Post-resume: daemon verifies boot_id unchanged, re-runs GOV_SELECT → TELEM_SAMPLE → GOV_DESELECT.
  5. Daemon unloads module, persists validation record, removes modprobe.d blacklist.
  6. Emits ValidationStage("complete").

On failure at any step: daemon unloads module, keeps blacklist, emits ValidationStage("failed", reason).

User notification

Two channels for boot-count guard trip (FR-078):

  • D-Bus AutoloadDisabled("boot_count_guard") signal → GTK desktop notification (ADR-003).
  • CLI: mjolnirctl status shows autoload: disabled (reason: 2 consecutive unclean boots).

Both direct admin to run mjolnirctl validate to re-validate.

Consequences

  • Positive: Daemon owns the full lifecycle — no split-authority between DKMS hooks, systemd, and the daemon.
  • Positive: modprobe.d blacklist is a one-file gate, trivial to inspect and to override in recovery.
  • Positive: Validation is interactive — the admin sees each step and owns the suspend decision.
  • Negative: Validation requires the admin to be present and to manually suspend — cannot be fully automated.
  • Negative: The 120s clean-boot threshold means a developer who insmod/rmmods in <120s gets an unclean boot recorded; acceptable since developer boots won't trip the guard (it takes 2 consecutive unclean boots with autoload enabled, and dev boots don't have autoload set).