# Installer ownership journal with file locking and content verification for safe uninstall
## Problem
A CLI installer adds a fixed small set of exact entries to a settings file that a person also edits by hand. Repair and uninstall must touch only installer-added entries, never pre-existing or user-added entries, respecting explicit denies. Process can crash at any point. Concurrent user edits must not cause lost updates or incorrect removals.
## Failures prevented
**Race from missing lock**: User opens settings during install. Installer reads, computes changes, writes. User saves unrelated edit, overwriting installer changes. Recovery finds marker missing, enters undefined state.
**Non-atomic metadata write**: Installer writes settings successfully, crashes before writing completion marker. Recovery cannot distinguish write failed from write succeeded then marker deleted.
**Blind removal during uninstall**: User modifies installer-owned rule before uninstall. Uninstall removes by pattern without verifying content, destroying modification.
**Partial operation without progress**: Uninstall processes three of six rules, crashes. Resume has no record of completed rules. Reprocessing or skipping both incorrect.
**System metadata in fingerprint**: Settings system adds timestamps. Fingerprint at install never matches at uninstall for unchanged rules, preventing clean uninstall.
**Duplicate install**: Running installer twice creates duplicate ownership claims.
**Unbounded stale entry accumulation**: Uninstall removes rule then clears journal entry. Crash between steps leaves journal entry forever. Skip logic never clears it, accumulating unbounded stale state.
**Orphaned rules from wrong ordering**: Clear journal entry then remove rule. Crash between steps orphans rule in settings with no ownership record, making it uninstallable.
**TOCTOU in compare-before-remove**: Between fingerprint check and removal, external process modifies rule. Removal proceeds on stale comparison, potentially destroying concurrent edit.
**User duplication ambiguity**: Developer duplicates installer-owned rule. Uninstall finds multiple matches but journal records single insert. Removal of any match could delete developer copy instead of installer copy.
## Core invariants
1. Ownership decided once at install by observation, recorded, never re-derived
2. Deny overrides allow at every step, installer never touches denies
3. All operations hold advisory file lock during read-modify-write
4. Atomicity through single-write: compute changes, write once atomically
5. Content fingerprint verification: remove only if current matches recorded
6. Incremental progress: record each outcome before next step
7. Operation ordering: clear ownership first, then modify settings
## Journal structure
In installer state directory:
```
operation_id: unique identifier
timestamp: when operation ran
status: pending | confirmed | removing | removed | cleanup
per_rule:
pattern: exact rule text
action: allow or deny
fingerprint: digest of immutable fields only
ownership: installer_added | pre_existing
observed_state: was_absent | was_present | deny_blocked
result: planned | completed | conflict | skipped | stale_cleared
completion_marker:
value: large random identifier
storage: metadata_field or separate_file
file_state:
before_digest: full file before changes
after_digest: expected file after changes
```
Fingerprint only immutable fields (pattern, action), exclude system timestamps and IDs.
## Install with locking
1. Acquire advisory lock
2. Read settings, compute digest
3. Classify each target:
- Explicit deny present: ownership none, state deny_blocked
- Already present: ownership pre_existing, state was_present
- Absent: ownership installer_added, state was_absent
4. Generate random completion marker
5. Write journal status pending with rules and marker, sync
6. Prepare new settings with installer_added rules and completion marker in metadata
7. Atomic write via temp and rename, sync
8. Update journal status confirmed, sync
9. Release lock
Recovery for pending operations:
```
acquire lock
current_marker = read from settings
if matches journal marker:
mark confirmed (write succeeded)
else:
add missing installer_added rules
write marker
mark confirmed
release lock
```
## Uninstall with compare-before-remove
Critical: use clear-first ordering to bound state accumulation. Stale journal entries are recoverable via cleanup pass. Orphaned settings rules would require external audit tool.
1. Acquire lock
2. Read settings, load journal
3. Create removal log status removing
4. For each installer_added rule in iteration order:
**Decision tree for current rule:**
a. **Locate rule in settings**
- If absent: mark result stale_cleared in journal, sync, continue to next rule
- If present: proceed to comparison
b. **Compare fingerprint**
- Compute current fingerprint from located rule
- If matches recorded fingerprint: proceed to removal
- If differs: mark result conflict in journal with difference detail, sync, continue to next rule
c. **Clear journal entry first**
- Mark result completed in journal, sync
- This establishes ownership release before modification
d. **Remove from settings**
- Prepare new settings with rule removed
- Continue to next rule
5. After all rules processed: atomic write settings via temp and rename, sync
6. Mark journal removed or partial depending on conflict count
7. Release lock
**Bounded convergence property**: Repeated uninstall attempts with same journal state converge to stable outcome. Crash before step 4c leaves journal entry recoverable. Crash after 4c but before 5 orphans that rule but journal is clear, preventing unbounded accumulation. Cleanup pass recovers stale entries.
Recovery for removing operations:
```
acquire lock
completed_set = get result completed or stale_cleared from removal log
for each installer_added:
if result in completed_set: skip
else: resume from locate step (4a)
release lock
```
## Stale entry cleanup pass
Separate idempotent operation run after uninstall or on explicit cleanup request:
1. Acquire lock
2. Load journal with status removed or partial
3. For each rule with result stale_cleared or completed:
- Verify rule absent from current settings
- If confirmed absent: remove journal entry
- If reappeared: log anomaly, preserve entry
4. Sync journal
5. Release lock
This recovers journal entries marked for clearing but potentially left during crashes.
## Conflict reporting
Report preserved rules: pattern, installed version, current version, reason preserved. Optional force mode overrides but logs forced actions.
## Duplicate prevention and user duplication
Before journal creation, search existing journals for same rules. If found and confirmed, error already installed. If pending, resume that operation.
When developer duplicates an installer-owned rule: uninstall finds multiple exact matches. Use last-N removal strategy: remove from end of ordered collection, preserving earlier instances likely added by developer. Document removal order in conflict log.
## Concurrency and TOCTOU
Advisory lock held throughout operation prevents concurrent installer runs and reduces race window with external editors. TOCTOU gap exists between fingerprint check and final atomic write. Resolve by re-reading settings immediately before atomic write and aborting if digest changed since initial read. Requires three-digest tracking: initial, pre-check, pre-write.
Alternative: document accepted TOCTOU window as narrow risk under advisory lock, relying on user coordination to avoid editing during uninstall.
## Requirements
Do not: write settings then separately write marker (non-atomic); read without lock (race); remove without fingerprint check (destroys edits); use full file digest for detection (format changes break); include system metadata in fingerprint (false mismatches); remove rule before clearing journal (unbounded accumulation).
Required: lock before read-modify-write; prepare then write once; marker in atomic operation or readable separate file; fingerprint immutable fields only; record incremental progress; clear journal entry before removing rule; run cleanup pass for stale entries.
## Applicability
For file-based configuration with concurrent human editing and crash possibility. Not for: native transactions available; database with ACID; exclusive installer access; format cannot support metadata or separate files.