TL;DR: I built ChronoVault v2 for Hackathon Raptors' Zero Dependency Hackathon (Track D — Data & Storage) — a content-addressable snapshot and recovery engine, zero runtime dependencies, backed by a hand-rolled pack-file format. A comment saying "this is atomic" is worth nothing to a judge, so instead of asserting crash-safety, I hard-killed my own writer process mid-write, over and over, and made the vault prove it survived. This is that story, plus a benchmark number I didn't want to publish and three Windows bugs that only showed up on real hardware.
279 tests. 20 CLI commands. 0 runtime dependencies. judge_mode.py VERIFIED on Python 3.11 and 3.14.
Proving crash-safety instead of asserting it
Anyone can write "atomic writes, crash-safe" in a README. It costs nothing and proves nothing. So recover-check is tested against a real SIGKILL, not a simulated one: a writer subprocess runs a snapshot, gets hard-killed at a randomized point mid-write, and the vault is reopened cold — no manual repair step allowed.
The test checks three things, not one:
- Every snapshot acknowledged before the kill is intact.
- The in-flight snapshot at the moment of the kill is either cleanly absent or a discarded partial write — never garbage returned from a read.
- The vault is immediately usable afterward, with no repair command required.
This is also where the pack-quarantine logic earned its place instead of being a "nice to have." Early in testing, a kill landing mid-pack-write left a truncated pack that the recovery scan initially tried to read as valid — and returned a wrong length from. Quarantine — isolate anything that fails its checksum before it ever reaches the index — closed that hole. The fix wasn't "trust the file less," it was "never let an unverified pack answer a read at all."
Run it enough times and the interesting failures stop being about the crash itself and start being about the code paths a crash exposes that a clean shutdown never does. That's the actual value of hard-killing your own process 300 times instead of once: the first ten runs prove the happy path works, and the next 290 are what find the pack-quarantine gap.
Architecture at a glance
flowchart TD
subgraph L1["CLI"]
CLI["cli.py — 20 subcommands"]
end
subgraph L2["Core Engine"]
Snap["snapshot.py<br/>atomic rename + fsync"]
CAS["Content-Addressed Store<br/>hashlib SHA-256, pack files"]
Idx["path_history.py<br/>rename-aware lineage"]
Rec["recover.py<br/>recover-check, SIGKILL-tested replay"]
end
subgraph L3["Persistence"]
Pack[("Pack files on disk")]
Quar["Pack quarantine<br/>corrupt/truncated packs isolated"]
end
CLI --> Snap
CLI --> CAS
CLI --> Idx
CLI --> Rec
Snap --> CAS
Snap --> Idx
CAS --> Pack
Idx --> Pack
Rec -.->|verified via SIGKILL injection| Snap
Rec --> Pack
Pack --> Quar
style L3 fill:#2d2d2d,stroke:#f5a623,stroke-width:2px,color:#fff
style Rec fill:#2d2d2d,stroke:#f5a623,stroke-width:2px,color:#fff
The shape that matters is where recovery sits: it reads pack files directly, never through the CLI or the snapshot writer, the same way a verify-style tool deliberately bypasses the normal open path. That's deliberate — if the write path itself is what crashed, recovery can't depend on any code the crash might have left half-updated.
The number I didn't want to publish
Every other number in this post makes ChronoVault look good. This one doesn't.
I benchmarked vault snapshot against diskcache — the closest real-world comparison for local content-addressed storage:
| Metric | ChronoVault | diskcache |
|---|---|---|
| Write throughput | baseline | 9–13x faster |
| On-disk size, repeated / near-duplicate snapshots | 5–47x smaller | baseline |
Why it loses on writes: every vault snapshot call pays for a full content hash of each file plus a pack-index lookup before a single byte is committed, because deduplication has to happen before the write, not after. diskcache skips that entirely. That's the whole trade in one sentence: I pay a hashing-and-lookup tax on every write so a hundred near-identical snapshots don't cost a hundred times the disk.
If your workload is write-latency-bound, diskcache is the right tool and I'm not pretending otherwise. ChronoVault wins when the workload is storage-bound — long retention windows, many near-duplicate snapshots, disk-constrained environments. A real loser column next to a real winner column, not a benchmark that only shows its best number.
Where the standard library actually made me suffer
Three places, all invisible until I tested on real Windows hardware instead of assuming POSIX behavior generalizes.
multiprocessing's spawn context raised a KeyboardInterrupt that wasn't one. Worker processes died mid-operation with nobody near Ctrl+C. Windows' spawn-based multiprocessing ties process teardown to _winapi.WaitForSingleObject, which can surface as a spurious interrupt with no signal ever sent — a path fork-based Unix multiprocessing never touches. I misdiagnosed this three separate times as my own bug before proving otherwise, using a detached Start-Job test that ran the worker fully isolated from the parent's console.
WinError 32 — Windows refusing to delete a file it considers still open. POSIX lets you unlink() an open file; the inode survives until the last handle closes. Windows refuses the delete outright. Handling it meant retrying past the handle-release window instead of importing psutil to hide it.
A join-before-drain ordering bug deadlocked the IPC queue. worker.join() was called before the result queue was fully drained. If the OS-level pipe buffer filled before the parent drained it, the child blocked on a full pipe and the parent blocked on join() — a deadlock that only appears under load. Fixed by draining before joining.
None of these are exotic. They're the specific price of touching multiprocessing and cross-process queues without a battle-tested wrapper library between you and the OS.
The edge case that ate an afternoon
Rename-aware file lineage in the path-history index. The question sounds simple — "was this file renamed, or deleted and recreated?" — until content, path, and inode can each change independently, sometimes in the same tick, with zero heuristics borrowed from someone else's diffing library to lean on.
The ambiguous case that actually happened: a file renamed and edited in the same snapshot cycle. "Renamed A→B, then edited" and "A deleted, unrelated B created" are both defensible reads of the same delta, and guessing wrong either invents a false history or silently drops a real one.
I didn't solve it by getting cleverer. I wrote down, explicitly, which cases the lineage tracker resolves correctly, and which ones it honestly doesn't try to disambiguate. A documented limitation beats a confident guess that's wrong on the exact case a judge tests.
What I'd do differently
The Windows CI matrix went in after most of the Windows-specific bugs had already been found manually, one laptop test at a time. Every area I later ran through real Windows hardware for the first time produced at least one genuine finding. If Ubuntu + Windows CI had existed from the start, at least two of those bugs would have surfaced in an automated run instead of on my own machine, days closer to the deadline than I'd have liked.
Where it landed
-
279 tests passing on Python 3.11 and 3.14,
judge_mode.pyVERIFIED - 20 CLI commands, zero third-party runtime dependencies
-
Crash-safety proven, not asserted —
recover-checkverified against realSIGKILLinjection, with pack quarantine closing the gap testing found - CI green across Python 3.10–3.14 × Ubuntu + Windows
-
STDLIB.mddocuments 14 real substitutions, each cross-referenced to the file that uses it - All four bonus categories claimed (+16) — Single File, Reproducible Build, Package Killer re-earned via the diskcache numbers above, STDLIB Log
python judge_mode.py
runs the full verification — test suite, single-file hash check, isolated-mode dependency audit, and the content-addressing proof's 8 invariants — as one command, so a judge doesn't have to trust this post; they can re-derive it.
Closing thought
Anyone with an AI coding agent can produce a CLI that hashes files and calls it content-addressed storage. The hard part was building enough ways to catch myself being wrong — a crash-safety claim verified with a real SIGKILL instead of a comment asserting atomicity, a benchmark table honest enough to put a 9–13x loss next to a 5–47x win, and a judge_mode.py that re-derives every claim in this post instead of asking a judge to trust it. The vault is the artifact. The willingness to kill it 300 times and publish what survived is the actual submission.
Repo: github.com/codewitharyan29/ChronoVault-v2
Track: D — Data & Storage
Built for Zero Dependency 2026, run by @partnerships_raptors.
Top comments (0)