Installer ownership journal for crash-safe repair and uninstall
Pattern for a CLI installer that adds a small set of exact entries to a shared, hand-editable settings file. Covers deny-over-allow precedence, an ownership journal that records pre-existing entries as unowned, pre-state and post-state fingerprints that resolve crash ambiguity, and a write-ahead sequence for install and uninstall.
Installer ownership journal for crash-safe repair and uninstall
Problem
An installer adds a fixed small set of exact entries (for example six permission allow rules) to a settings file a person also edits by hand. Repair and uninstall must touch only entries the installer itself added, never a person's pre-existing or independently added entries, and must respect an explicit deny. The process can crash at any point, including between the journal write and the settings write.
Invariants
- Ownership is decided once, at install time, by observation, and recorded. It is never re-derived later by matching known content, because a person can author an identical rule.
- Deny overrides allow at every step. The installer never adds, edits or removes a deny rule in any mode.
- Every settings mutation is an idempotent exact diff: add this exact string if absent, remove this exact string if present. Replaying a step is always safe.
- When ownership cannot be established, the installer declines ownership and leaves the entry alone.
Journal contents
Keep the journal in the installer's own state directory, not inside the settings file. Record a run id, timestamp and, per rule:
- the exact serialized rule string
- observed pre-state: absent, present, or covered by a deny
- ownership: owned (installer will add it) or pre_existing (person had it first, never touch)
- status: intended, applied, blockedbydeny, superseded, removing, removed
- fingerprint of the settings file at intent time and the expected fingerprint after the planned write
A rule found already present at install time is journaled as pre_existing. Uninstall skips it. This is the single record that preserves pre-existing allows.
Install and repair sequence
- Read settings, compute its fingerprint, classify each of the fixed rules into absent, present, or deny-covered.
- Write journal entries with status intended, ownership and pre-state per rule, and the pre and expected post fingerprints. fsync.
- Apply the exact diff by atomic write (temp file, fsync, rename). Do not add a rule that a current deny covers; record it blockedbydeny and report it to the person.
- Mark entries applied. fsync.
Repair reruns the same sequence. For an owned rule marked applied that is now missing and not deny-covered, re-add it. If a deny now covers it, leave it and mark superseded. Never touch pre_existing entries.
Crash recovery on resume
For each entry still marked intended:
- Rule absent: the settings write never landed. Replay from step 3.
- Rule present and current fingerprint equals the expected post fingerprint: the write landed and only the status update was lost. Mark applied.
- Rule present and current fingerprint equals the pre fingerprint: impossible if the rule was absent at intent time; treat as journal corruption and stop with a message.
- Rule present and fingerprint matches neither: something else wrote the file during the gap. The installer cannot know whether the rule is its own. Demote the entry to pre_existing (decline ownership) and report. The cost is a rule left behind on uninstall, which is safer than removing a rule the person wrote.
Uninstall sequence
- Mark owned applied entries as removing. fsync.
- Remove each owned rule only if present with exactly the journaled string. Leave pre_existing entries, leave all denies, leave any rule whose content no longer matches (treat hand edits as adoption by the person). Atomic write.
- Mark entries removed, then delete the journal. Deleting the journal last means a crash leaves a journal that replays idempotently.
If the journal is missing but the rules are present, treat every rule as unowned and remove nothing unless the person passes an explicit force flag that adopts the exact fixed set.
Limitations
Derived by reasoning about invariants, not from executed tests. Fingerprint comparison assumes the settings file is rewritten as a whole; a format that other tools rewrite frequently will trigger the decline-ownership path more often, which is safe but leaves rules behind. Two installers sharing one settings file each need their own journal and must not claim each other's entries.