Keyed records that diff cleanly
JSONLT is a format for storing keyed records in append-only files using JSON Lines. Optimized for human readability and version control diffs.
What is JSONLT?
JSONLT (JSON Lines Table) is a data format for storing collections of records. Each line is a standalone JSON object representing one record, and every record has a key field that uniquely identifies it. To update a record, append a new version—the last entry for each key wins.
{"id": "alice", "name": "Alice", "role": "admin"} {"id": "bob", "name": "Bob", "role": "user"}{"id": "alice", "name": "Alice Chen", "role": "admin"}
Alice updated her name. The original line stays; the new version
appends. When read, "alice"
resolves to the latest entry.
Keyed
Each record has a unique identifier field
Append-only
Changes add new lines, never modify existing ones
Last wins
The most recent entry for a key is the current value
Why append-only wins
Three real scenarios where JSONLT's append-only design solves version control headaches.
1. Git blame stays accurate
When lines shift in a JSON array, blame points to whoever caused the shift—not the original author.
abc123 (Alice) | { abc123 (Alice) | "users": [def456 (Dave) | {"id": "alice", "role": "admin"} <- wrong!def456 (Dave) | {"id": "carol", "role": "user"}def456 (Dave) | {"id": "bob", "role": "user"} <- wrong!abc123 (Alice) | ] abc123 (Alice) | }
Dave added carol. Now blame says Dave authored alice and bob too.
abc123 (Alice) | {"id": "alice", "role": "admin"}
abc123 (Alice) | {"id": "bob", "role": "user"}
def456 (Dave) | {"id": "carol", "role": "user"} Dave's addition appends. Alice's records keep their attribution.
2. Merge conflicts disappear
Two developers edit different records. In JSON arrays, this often causes conflicts. In JSONLT, both just append.
{ "settings": [<<<<<<< HEAD{"key": "theme", "val": "dark"}, {"key": "lang", "val": "en"}======={"key": "theme", "val": "light"}, {"key": "lang", "val": "fr"}>>>>>>> feature] }
Alice changed theme, Bob changed lang. Manual resolution required.
{"key": "theme", "val": "light"} {"key": "lang", "val": "en"}{"key": "theme", "val": "dark"} <- Alice{"key": "lang", "val": "fr"} <- Bob
Both changes append to different lines. Git merges automatically.
3. Minimal diff noise
Adding a record to a JSON array changes multiple lines. JSONLT diffs show only what you added.
{ "products": [ {"sku": "A001", "price": 29.99},- {"sku": "B002", "price": 49.99}+ {"sku": "B002", "price": 49.99}, + {"sku": "C003", "price": 19.99}] }
Adding C003 required changing the previous line's trailing comma.
{"sku": "A001", "price": 29.99} {"sku": "B002", "price": 49.99}+{"sku": "C003", "price": 19.99}
One line added, one line in the diff. Every time.
Why JSONLT?
Clean version control diffs
Modifications append new lines rather than rewriting existing content. You can see exactly what changed and when in your git history.
Human-readable format
Plain text JSON you can read, edit, and grep. No special tools required—works with any text editor and standard Unix utilities.
No infrastructure required
Just files. No database servers to manage, no services to configure. Your data lives alongside your code in version control.
Formally specified
A complete specification with conformance profiles, test suites, and reference implementations ensures interoperability across languages.
Simple to use
JSONLT implementations provide a familiar key-value interface. Here's how it looks in Python:
from jsonlt import Table
# Open or create a table
users = Table("users.jsonlt", key="id")
# Insert or update records
users.put({"id": "alice", "name": "Alice", "role": "admin"})
users.put({"id": "bob", "name": "Bob", "role": "user"})
# Retrieve a record
alice = users.get("alice")
print(alice["role"]) # "admin"
# Delete a record
users.delete("bob") Implementations
The JSONLT project maintains reference implementations that demonstrate spec-conformant behavior and serve as test beds for the conformance suite.
Interested in contributing?
Join the discussion →