Your files have a history.
Files are saved over. Exports replace exports. A folder is renamed, a version is deleted to make room, and the record of how a project got here becomes a pile of filenames that meant something last week. Backups keep copies and version control asks you to commit. Neither one describes what actually happened to a folder of working files while you were busy working.
AFTRIMGE treats a file as an identity rather than a path. It watches the folders you choose, waits for a save to settle before reading it, hashes the settled bytes in the same pass that writes them, and stores content under its SHA-256 so identical content exists once. A rename keeps the history. A later save never replaces an earlier version. Verification reads every byte back and hashes it again, and a restore is planned, checked, written, checked and checked a third time. Records, measurements and deductions are held apart in the data and in the interface, so a deduction is never shown as though it had been observed.
Case Study: AFTRIMGE
Work changes faster than anyone remembers it.
A local temporal engine that records what happened to a folder of working files, proves that what it kept is still exact, and puts a version back.
Files are saved over. Exports replace exports.
A folder is renamed, a version is deleted to make room, and the record of how a project got here becomes a pile of filenames that meant something last week: poster.psd, poster_v2, poster_v2_final, poster_FINAL, poster_FINAL_use-this.
AFTRIMGE watches the folders you choose and records every change as it happens: each version, each rename, each deletion. You can replay the project, inspect any past version, verify that what is stored is still exact, and put it back. All of it on your own machine.
Backups keep copies. Version control asks you to commit.
Neither one describes what actually happened to a folder of working files while you were busy working. A backup is a photograph of a folder at midnight, which answers what existed and never answers what changed, in what order, or what replaced what. Version control answers all of that and asks for a deliberate act at every step, which is exactly the act that does not happen during three hours of design work.
The result is that the most volatile hours of a project are the least recorded ones, and the record that does survive is the filename.
A pile of filenames that meant something last week.
A file is not a file. It is an object with a history.
A path is where a file is. The identity is what it is. Once those are separated, a rename stops being a destructive event and becomes a recorded one, a move keeps its history, and a project folder can be relocated wholesale without the record losing its thread.
The second half follows from the first: if a version is identified by its content rather than by where it sat, then identical content is stored once, a later save never overwrites an earlier version, and a file that returns to earlier content points back at the object that already exists.
Identity, not path. Versions, not overwrites.
Five decisions the whole product follows.
- History, not snapshots: When each version appeared, what it replaced, where the file went. Not a copy of a folder at midnight.
- Evidence, not guesses: Recorded events, measurements and deductions are kept separate, graded, and each can explain what it rests on. A deduction is never displayed as if it had been observed.
- Local custody: The archive lives with you: a database and an object store in a folder you can copy. There is no cloud component to trust, pay for or lose access to.
- Recovery, not just retention: Any kept version can be inspected, compared and restored, with the result verified byte for byte.
- Deterministic, not opaque: Every measurement is reproducible from the same inputs. No model decides what your files are.
Filesystem events are noisy, and the noise is the engineering.
One save in a creative application typically arrives as several modifications, a temporary file created, a rename, a deletion and another modification. Recording each of those would produce six versions of one save, several of them half written. So a change marks the path pending with a stability deadline, any signal that changes size or mtime resets it, and when the deadline elapses the path is probed once more before anything is believed.
A deletion is held before it is believed, because an atomic save and a rename both begin by making a path disappear. A file genuinely still being written, a long video export, stays pending until it stops growing, and waiting is correct: a snapshot of a half-written render would be a lie.
Renames use three mechanisms in descending order of certainty, and none of them guesses. If the operating system reports both halves in one event, that is definitive. If it reports two events linked by a tracker id, each half is held briefly and matched on that id, and halves with no tracker id are never paired with each other however suggestive the timing. Otherwise a deletion is staged, and if byte identical content appears at a new path inside the window the file keeps its identity and the event is recorded with its evidence and labelled inferred.
A snapshot of a half-written render would be a lie.
The central interaction is a playhead, and it had to feel instant.
Scrubbing a timeline fires pointer events at screen refresh rate, and each one is a candidate query. The answer is that a project's state only changes at an event: between two events nothing about the reconstruction can differ. So every playhead position is snapped to the timestamp of the last event at or before it, and that snapped key, not the raw position, is what the engine is asked about and what the result is memoised under.
Dragging across an afternoon in which nothing happened costs zero queries. Dragging across a whole project costs exactly one query per event, ever. While a new state loads the previous one stays on screen and merely recedes, because a view that blanked on every step would read as broken even if it were fast.
The stage keys its plates by file identity rather than by path, which is what makes a rename read as one plate changing its label instead of one plate vanishing and another appearing.
Dependencies point one way, and the interface holds no domain logic.
The valuable asset is the local temporal engine. The interface is a view over it: it cannot read a file, resolve a path, or decide what a filesystem event means. Folder selection happens in the engine, so path handling never crosses into the frontend at all, and the engine talks outward through a single trait, which is why the whole pipeline is testable with a plain struct in place of the desktop shell.
An object's name is the SHA-256 of the bytes AFTRIMGE actually wrote, computed in the same pass that writes them, so a caller's claim about content is never trusted. Writes are staged and then renamed, so a crash mid copy cannot leave a truncated file at a valid content address, and bytes are committed before the row, so a crash can only ever leave an unreferenced object rather than a snapshot pointing at nothing.
Each operational question is answered in exactly one place in the code: where the archive is, whether it is already open, what format the database is, why an object is kept, whether its bytes are intact, and how large an input may be. Two implementations cannot drift apart if there is only one.
Identity comes from the bytes. A caller's claim is never trusted.
Six things that only matter on the day something goes wrong.
- Stat before hash, hash before write: Matching size and mtime stop the work, which is why a 4 GB file is not re-read on every metadata event. A matching hash produces no version and no event, which is what makes twenty identical saves cost nothing.
- Verification re-reads every byte: Stored is not the same as intact. Verification hashes the content again rather than trusting the record that it was once correct.
- A restore is planned first: The stored bytes are checked, written beside the target, checked, swapped in, and checked a third time. The restore is then added to history, and nothing later is removed.
- Four storage states, one rule: Held, reclaimable, missing, untracked. Every piece of content has exactly one, a missing object keeps its record on purpose, and a sweep shows a dry run before it removes anything.
- Tested by killing the process: The suite terminates the application mid capture, mid collection and mid restore, then checks the database and every kept version.
- Hostile paths are refused: A restore cannot leave its project, including through a junction or a symbolic link, and refuses names Windows would treat as devices or streams.
AFTRIMGE 0.2.0 ships with its engineering record published alongside it.
Forty-one sections document every subsystem, invariant and defect found while building it, including an identity bug the chaos test found and a collector race found by reading. A product that writes that down is making a different offer from one that publishes a feature list.
What exists today: local capture with a stability layer, content addressed deduplicated storage, replay and temporal scrubbing, graded image archaeology, integrity verification, planned restore, retention with four states, and a portable archive. What does not: code signing. The builds are published with their SHA-256 and a record of how they were built and tested.
The hardest part of a history product is the part that admits what it does not know.
A change discovered by a scan rather than witnessed live is placed on the timeline at the file's own mtime and labelled as a scan, because stacking an entire folder import at one instant would be less true. An image measured against the archive is graded fact, inferred or possible, and only identical bytes are a fact.
None of that makes the product look more capable. It makes it usable as evidence, which is a different and more durable thing.
Stop naming files final. Point AFTRIMGE at a folder, and from then on its history is kept.