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

77 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-006: Module Lifecycle Protocol — Validation, Boot-Count Guard, and Recovery
**Status:** Accepted
**Date:** 2026-07-31
**Deciders:** Bongbetic (solo developer)
**Source:** [Task #5](https://github.com/soubarnak/Mjolnir/issues/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).
## Related
- [ADR-001: Component Architecture](./001-component-architecture.md) — startup sequencing step 3
- [ADR-002: Netlink Protocol](./002-netlink-protocol.md) — GOV_SELECT/DESELECT/TELEM_SAMPLE commands
- [ADR-003: D-Bus API Contract](./003-dbus-api-contract.md) — AutoloadDisabled signal, ValidationStage signal added
- [ADR-004: Build Sequencing](./004-build-sequencing.md) — DKMS AUTOINSTALL, just install-km
- [Netlink Protocol Spec](../spec/netlink-protocol-spec.md) — §8.3 validation run hooks
- SRS FR-070079, FR-075, FR-076
- SRS RISK-001 (co-resident DKMS modules), RISK-005 (dev machine = test machine)