Elevator pitch

Drift — a build system for your content pipeline: edit the brief, rebuild only what's stale, verify everything.

Every title, caption, post, poster, keyframe, narration and delivery video descends from one brief. Change one line and everything downstream silently drifts stale. Drift treats that pipeline as a dependency graph: every asset is content-addressed and fingerprinted, so a change has a provable blast radius, only stale assets rebuild, and a manifest re-verifies every byte from disk.

Inspiration

A creator's content is a hidden dependency graph. Every title, caption, tag, post, poster, keyframe, narration, and delivery video descends from one brief — edit the brief and everything downstream silently drifts stale. Nothing tells you which posts are now wrong, schedulers publish the stale assets anyway, and rebuilds are all-or-nothing. Software solved this decades ago: a compiler tracks dependencies and rebuilds only what changed. Content pipelines don't have that — and they also can't prove what a build actually shipped. Drift is the compiler that was missing.

What it does

Drift treats a content pipeline as a content-addressed build graph. Two source files (brief + product reference) compile into an immutable 18-node graph — shot plan, title, description, tags, captions, posts, poster, cutout, keyframes, narration, composed delivery video. Every node's identity is a SHA-256 fingerprint over its own recipe and its exact input hashes (canonicalized with JCS / RFC 8785; floats rejected so a hash can never depend on binary rounding).

Walking the graph topologically, each node either passes a reuse proof or is marked rebuild with a reason code. Before trusting a reuse, the on-disk bytes are re-hashed — a tampered file is regenerated, never trusted. Only stale nodes rebuild. A manifest binds every output hash, and verify re-reads every file from disk and re-hashes it: change one byte and it fails.

The numbers fall out of the algorithm, not special cases:

  • Fresh build: 18 rebuild / 0 reuse
  • Identical brief again: 0 rebuild / 18 reuse
  • Edit the handle (bound into exactly two nodes): 2 rebuild / 16 reuse

A FastAPI + SQLite control plane with a durable queue (atomic claim, lease + heartbeat, idempotent submit) and a separate worker run the engine; a Vite/React dashboard commits a brief and watches the build flow.

How we built it

The domain layer is pure Python, stdlib only, zero I/O: compile → fingerprint → impact → build → verify. JCS canonical JSON is implemented in-house and conformance-tested. The queue is SQLite with BEGIN IMMEDIATE for atomic claims — zero external services, so a judge can clone the repo, run uv sync, and the whole stack works on one box.

Media generation is deterministic on purpose: images are HTML/CSS/SVG rendered by headless Chrome (identical markup → identical bytes), narration is TTS, video is ffmpeg with Ken Burns motion and crossfades whose timeline is solved from the narration length. No model, no GPU, no API key — every byte is a pure function of the source, so every byte can be content-addressed and re-verified.

44 tests cover the impact engine, the handle blast radius, tamper re-hash, manifest verification, queue semantics, and a full end-to-end media build (ffprobe-verified MP4, clean re-verify).

Challenges we ran into

  • The handle had to be a bound parameter, not source text. Put it in the brief and a one-character edit would cascade through all 18 nodes. Bound into only post.x and post.linkedin, a handle edit rebuilds exactly 2 — proven by test, never special-cased.
  • Reuse must re-hash disk bytes. Trusting a cached state file would bake a tampered asset into the next manifest as legitimate. The build re-hashes the on-disk file before every reuse; anything that diverged is regenerated.
  • verify must pick the latest manifest by created_at, not filename. Build IDs are random hex, so filename order is meaningless — a lexicographic pick verifies an arbitrary (sometimes stale) build. The manifest records its own timestamp and verify sorts by it.
  • Media had to be deterministic to stay content-addressed. A generative model renders different bytes per run, which breaks fingerprinting. HTML + headless Chrome renders identical markup to identical bytes, so images are a pure function of the source, same as text.
  • Mixed-size frames break ffmpeg concat. The poster is portrait, keyframes landscape — every frame is normalized to a 1280×720 canvas before assembly.
  • The demo video itself. We wanted the demo to be made by the project, so the narration goes through Drift's own audio node (edge-tts via its documented DRIFT_EDGE_VOICE seam), the slides through its HTML+Chrome pipeline, and the assembly through its ffmpeg composition. No external tooling for any frame of the reel.

Accomplishments that we're proud of

  • A one-line edit has a provable blast radius: 18/0 → 0/18 → 2/16, all live, all with reason codes.
  • Tamper detection that actually works: change one byte of any asset and verify reports the exact node that diverged.
  • Deterministic media with zero API keys — the whole 18-asset build (including an MP4) runs free, forever.
  • The demo reel is a Drift output: narration, slides, and assembly all through Drift's own pipeline, re-verifiable from disk.
  • An honest repo: README.md's what's-real-vs-pending table says exactly what is built and what isn't.

What we learned

Provenance beats trust. A "cached" flag is a statement until the bytes re-hash. Determinism is the enabler of verification — if generation isn't a pure function of the source, nothing can be content-addressed and nothing can be proven. Blast radius should fall out of the fingerprint algorithm, not special cases. And the boring bugs (filename order vs timestamps, frame dimensions before concat) bite harder than the interesting ones — the integrity of the proof lives in those details.

What's next for Drift

  • LLM copy generation, plugged into the existing generation seam (env-gated, off by default)
  • Hosted TTS / image providers through the same seam — swap deterministic backends for paid APIs without touching the graph
  • Multi-project support — one queue, many content directories
  • Postgres backend as a drop-in for the SQLite queue when a shared box is needed
  • Webhooks so schedulers learn which assets changed after a build, instead of publishing stale ones

Built With

Share this project:

Updates

Submission history