A practical CLI tool for tracking JSON file changes over time. Instead of keeping multiple copies of JSON files, this creates compact delta-based archives that preserve the complete history.
## What it does
This tool solves a simple problem: you have a JSON file that changes regularly, and you want to track its history without storing dozens of full copies.
`json-archive` creates a `.json.archive` file next to your original JSON file. Each time you run the tool, it calculates only what changed and appends those deltas to the archive. You get complete history with minimal storage overhead.
The archive format is human-readable JSONL (not binary), making it easy to inspect, debug, and pipe into other scripts or web visualizations.
## Quick example
```bash
# Create initial archive from data.json (infers output: data.json.archive)
json-archive data.json
# Later, append changes to existing archive
json-archive data.json.archive data.json
# Or let it infer again (won't overwrite without --force)
# Or safely re-run (won't overwrite existing archive)
json-archive videoID.info.json
# Run daily in a cron job to capture changes
# The archive preserves your title/description experiments and view count history
```
## Design philosophy
**Hackable over efficient**: The file format prioritizes human readability and scriptability over binary compactness. You can:
- Open archives in any text editor
- Grep through them for specific changes
- Parse them in JavaScript without special libraries
- Pipe them through standard Unix tools
**Minimal workflow changes**: Archive files sit next to your original JSON files with a `.archive` extension. Your existing scripts need minimal modification.
### Compression support (as a concession)
While the core design keeps things simple and readable, the tool does work with compressed archives as a practical concession for those who need it. You can read from and write to gzip, brotli, and zlib compressed files without special flags.
**Important caveat**: Compressed archives may require rewriting the entire file during updates (depending on the compression format). If your temporary filesystem is full or too small, updates can fail. In that case, manually specify an output destination with `-o` to write the new archive elsewhere.
This works fine for the happy path with archive files up to a few hundred megabytes, but contradicts the "keep it simple" design philosophy - it's included because it's practically useful.
## Archive format
The format is JSONL with delta-based changes using [JSON Pointer](https://tools.ietf.org/html/rfc6901) paths. For complete technical details about the file format, see the [file format specification](docs/file-format-spec.md).
```jsonl
{"version": 1, "created": "2025-01-15T10:00:00Z", "initial": {"views": 100, "title": "My Video"}}
# First observation
["observe", "obs-001", "2025-01-15T10:05:00Z", 2]
["change", "/views", 100, 150, "obs-001"]
["change", "/title", "My Video", "My Awesome Video", "obs-001"]
# Second observation
["observe", "obs-002", "2025-01-15T11:00:00Z", 1]
["change", "/views", 150, 200, "obs-002"]
```
Each observation records:
- What changed (using JSON Pointer paths like `/views`)
- The old and new values
- When it happened
- A unique observation ID
## Commands
The tool infers behavior from filenames:
### Documentation
- [Info command](docs/info-command.md) - View archive metadata and observation timeline
- [State command](docs/state-command.md) - Retrieve JSON state at specific observations
- [File format specification](docs/file-format-spec.md) - Technical details about the archive format
### Creating archives
```bash
# Create archive from JSON files (output inferred from first filename)
json-archive file1.json file2.json file3.json
# Creates: file1.json.archive
# Won't overwrite existing archives (safe to re-run)
json-archive data.json # Won't overwrite data.json.archive if it exists
Contributions are welcome! However, you will need to sign a contributor license agreement with Peoples Grocers before we can accept your pull request.
I promise to fix bugs quickly, but the overall design prioritizes being hackable over raw performance. This means many obvious performance improvements won't be implemented as they would compromise the tool's simplicity and inspectability.
Areas where contributions are especially appreciated: