Git-Backed SaaS: SaaS Without a Service

scosman1 pts0 comments

Git-Backed SaaS: SaaS Without the Service | Kiln AI

Skip to main content 4.5k ↓ Free Download

BLOG<br>May 14, 2026 Git-Backed SaaS: SaaS Without the Service<br>Kiln uses git for everything you'd normally need a database for, with the instant-syncing user-experience of SaaS. Every API call is a commit. Every read is a local file.<br>Steve Cosman Kiln Founder

Kiln is a local app. Your datasets, evals, and runs live in your git repo, on<br>your machine, under your control. But hit save, and your teammate's UI updates<br>in seconds. No cloud service, no terminal, no git commit, no<br>pull, no merge conflicts. It uses git for everything you'd normally need a<br>database for, with the easy user-experience of SaaS.<br>Every API call is a commit. Every read is a local file.

A save in one client shows up in a teammate's UI seconds later — no terminal<br>or git knowledge required. In this post:<br>Why we did this<br>How it works<br>What you get for free<br>Bringing your team in<br>Tradeoffs<br>Alternatives considered<br>What's next<br>Why we did this<br>I love git for data. History, branches, blame, PRs, portability. For a data<br>team, it's hard to beat. You get version control, rollback, and an audit trail<br>without building any of it.<br>But Kiln's bigger users hit a wall. Less technical folks like PMs, QA, and<br>subject-matter experts needed in on the workflow. And "just use git" doesn't<br>work for people who don't know what git is.<br>We had a choice. We could build a hosted service with a database and standard<br>SaaS auth. That's the normal move. But it throws away everything good about<br>git: data ownership, the audit trail, portability, the access-management<br>infrastructure your org already runs. Your data goes from "files you control"<br>to "rows in our database, accessible through our API, exportable if we feel<br>like building that."<br>I decided to challenge us: could we get SaaS-grade sync UX without giving up<br>the benefits of git? The answer is yes, git can be your database. You inherit<br>versioning, access control, audit trails, CI, and portability instead of<br>building them. The hard part is making git invisible for less technical users,<br>and that's what the rest of this post covers.<br>How it works<br>Four pieces. Everything below uses pygit2 (libgit2 with Python bindings).<br>No shelling out to git. Users don't need git installed, just our app.<br>Instant local reads, in sync with remote<br>Each project lives as a full clone on disk, in a hidden directory Kiln<br>manages. A read hits a JSON file on local disk. To keep in sync, a background<br>loop polls the remote every 10 seconds and fast-forwards the local clone when<br>there's anything new. The read path itself doesn't touch the network and feels<br>like an instant local app.<br>The poll loop doesn't fight with writes. It runs in two phases. First, a<br>lock-free fetch that only updates remote-tracking refs and never touches<br>the working tree. Second, only if phase one found new commits, a fast-forward that<br>briefly takes the write lock to move HEAD. In the common case (nothing changed<br>remotely), the loop adds zero contention.<br>Reads can be instant because all they need to do is check a single timestamp:<br>if the last successful sync was within 15 seconds, serve from local;<br>otherwise, fall back to a synchronous fetch (rare). The system polls actively<br>while users are working and pauses when they are idle for over 5 minutes.<br>How do you avoid conflicts?<br>Best way to handle conflicts is not to have them, so that's the first plan of<br>attack.<br>Data model design. Kiln stores data as folders of small JSON files.<br>Each entity gets its own file, named with a unique random ID. Most files are immutable.<br>Most operations are append-only. Random IDs ensure we don't conflict on write and<br>paths don't change when users rename things. Two users rarely write the same file<br>at the same time because there are many small entities, not a few large ones.<br>Isolated clone. Kiln keeps its own clone in a hidden directory,<br>separate from any checkout an engineer might have. Any local work you do won't<br>conflict with Kiln's auto-sync system, and the two are kept in sync via remote.<br>Write lock. Every mutating request gets a per-repo write lock.<br>Endpoints don't know it exists. More on this in the middleware section.<br>Background sync narrows the window. With a 10-second poll cadence,<br>two clients would have to make conflicting edits to the same file within a few<br>seconds to collide. The window is small, and conflicts are exceedingly unlikely.<br>Uncommon isn't zero, though. Sometimes the race happens.<br>What if you get a conflict anyway?<br>When conflicts happen, the push fails because the remote moved since our last<br>fetch. Here's the recovery.<br>Cherry-pick the one commit. Each API call produces one commit,<br>so recovery is a one-commit rebase. We do it as a cherry-pick: fetch the new remote<br>HEAD, hard-reset local to match it, then replay our commit on top.<br>fetch remote<br>local_commit = HEAD<br>reset --hard to remote HEAD<br>cherry-pick local_commit onto new HEAD<br>if conflicts:<br>abort cherry-pick<br>reset --hard to pre-request...

saas local kiln remote commit sync

Related Articles