4.0 KiB
Design Decisions
This file captures the rationale behind significant architecture and format choices, so the reasoning is preserved for future maintainers (including yourself six months from now).
Sentinel File vs config.ini (v0.3.2)
Problem
config.ini persisted the save folder path to disk, including the user's
filesystem-username. This is a privacy concern — paths are visible next to
the binary.
Decision
Remove config.ini entirely. The save folder is session-only — set it each
time via Set save folder. The disclaimer acceptance is tracked via a
0-byte sentinel file (NotAlterra_LICENSE_ACCEPTED) alongside the binary.
Rationale
Privacy — no paths written to disk. The save folder exists only in memory while the tool runs.
Simplicity — no config parsing, no INI format to maintain, no migration code for renamed keys.
Sentinel, not config — a 0-byte file communicates exactly one boolean (disclaimer accepted). It cannot grow into a configuration file over time. The format intentionally prevents scope creep.
What was removed:
AppConfigstruct (save_path, ini_path, save_scan, disclaimer_accepted)load_config()/save_config()with INI parsing- Cached
ini_path— now derived from save folder at runtime - Four integration tests for config round-trips
Manual Path Entry vs Auto-Discovery (v0.3.0)
Problem
Auto-discovery scanned user profiles and system directories for Subnautica 2
save folders. This is a privacy concern — it traverses /home/* (Linux) and
C:\Users\* (Windows).
Decision
Replace full auto-discovery with manual path entry via Set save folder.
Keep a lightweight quick_discover() that checks only the current user's
default install paths at startup.
Rationale
Privacy — no scanning of other users' profiles or system drives.
Current-user convenience — quick_discover() checks 1 path on Windows,
3 paths on Linux, all within the current user's own directories. Returns
the first match silently, no UI. If nothing is found, the user enters their
path manually.
Discovery module retained — validate_custom_path() and
derive_ini_path() still live in discovery.rs for the manual entry flow.
The aggressive scan functions (discover_save_folders(), scan_other_users(),
walk_for_subnautica()) are removed.
tar.gz Backup Format (v0.4.0)
Problem
Directory-tree backups (NotAlterra_Backups/notalterra_copy_<timestamp>/)
are messy, uncompressed, and have no integrity guarantees.
Decision
One tar.gz archive per backup event, stored in backups/saves/.
Rationale
No vendor lock-in — standard tar -xzf recovers data without the tool.
If NotAlterra stops working, the user's backups are still accessible with
standard system utilities.
Single file per event — reduces clutter. One backup = one file, not a directory tree with 15+ loose save files.
Compression — save files compress well (~75MB → ~20MB). Reduces disk usage without user effort.
Pure Rust implementation — tar + flate2 crates, 200M+ downloads
combined. No system dependencies, no external tools.
Per-entry restore — extracting a single save file from the archive does not require decompressing the entire archive.
Safeguards
- Atomic write: backup written to
.tmpfile, then atomically renamed. Power loss during backup discards a temp file, not a real backup. - Integrity check after creation: archive is read back and validated before reporting success.
- SHA256 manifest: a
MANIFESTfile inside each archive records the hash of every contained save file. On restore, each extracted file is verified against its expected hash — silent bit-rot detected before bad data reaches the save folder. - Fuzz target: round-trip fuzzing (create archive from diverse inputs → restore → compare) catches logic bugs.
Migration
Existing NotAlterra_Backups/ directory-tree backups are detected and
transparently imported on first run after upgrade. No manual migration
required.