Driftwood: An Open Source Design Research Platform for Web Apps
Software drifts.
Driftwood catches it.
A small, self-contained, config-driven Kanban I designed and built end-to-end to run a real beta test — the shore patrol for Project Lookout. This is what it does, how it’s built, and the thinking underneath.
One Docker image
Backend, built frontend, and the guide — served from a single container.
One config file
Columns, areas, types, phases — retarget the whole board without touching code.
One endpoint
POST /api/ingest and reports drift in with page, screenshot & console errors.
Zero seat licenses
Self-hosted, yours to fork — no per-tester bill, ever.
I was running a beta out of a Google Sheet. It wasn’t going well.
My “bug tracker” was a spreadsheet everyone ignored and a Slack channel that swallowed every report by Tuesday. I didn’t want Jira. I wanted one page a non-technical tester could open, point at what’s wrong, and get on with their day — and one board I could actually run the fix from.
So that’s all Driftwood is: The Stream for raw reports, a board with real working lanes, and two drawers for the wild ideas that always show up and never fit. No sprints, no story points, no forty-field forms. It runs from one container and one config file — and the whole thing is built on a single idea: software drifts. Every release knocks pieces loose; they float downstream, and someone has to sight them, reel them in, bring them ashore, and build them back into the structure they came from.
“Sight it. Reel it in. Bring it ashore. Build it back in. If a tool needs a manual to do that, it’s the wrong tool.”
Follow a piece downstream
Everything in Driftwood is one of these four beats. It’s drifting on its own — tap any stop to take the wheel.
Sight it
Every release shakes something loose — a rotted link, a number that lies, a label that wandered off. It drifts in here, raw. Your only job is to notice, and say so.
See it running
Real screenshots, straight from the live board — the same coastal design system this page is dressed in.
What’s in the tackle box
Six surfaces, each one a beat in the drift cycle — and not one screen more than the job needs.
A small tool, honestly measured
Designed, built and shipped solo, end to end — and already running the live beta it was made for.
Fish one out of the Stream
Three raw reports just drifted in. Give each a name and steer it — watch it fling itself onto the board. This is the real triage move, shrunk down.
Loved the morning digest — but the “Highway Guardrail Replacement” at the top isn’t painting work.
from owner@roxburypainting
0 of 3 sighted & steered
Six principles, held on purpose
Every screen, endpoint and animation had to answer to these. They’re why the thing feels calm instead of clever.
Opinionated on purpose
A Stream, a board, two drawers, one guide. It isn’t trying to run your company — just your beta — so it says no to nearly everything a “real” PM tool would add.
One metaphor, all the way down
Software drifts, so every surface is a beat in the drift cycle and the whole design system is coastal — warm paper, river blue, tide lines, a bobbing piece of driftwood. The theme is the mental model.
Motion is superficial, never load-bearing
Content is always in the DOM; reveal, parallax and ripples are decoration you can switch off. Every animation is prefers-reduced-motion- and print-safe.
Honest or omitted
Never present a synthetic number as live. Placeholder data is labelled “illustrative — synthetic”; the live views only include figures with a real source. A tile with no honest number is dropped, not faked.
Config over code
One file — columns, areas, card types, phases — retargets the entire board to a new product. Driftwood ships pointed at Project Lookout as a worked example, with zero hard dependency on it.
Tenant-scope everything
Every row carries a tenant; every query is scoped by it. Cross-tenant reads 404 instead of leaking. Reads never mutate — the stats endpoint won’t run the sweep that the board endpoint owns.
#F7F5F0
ink#2A2E33
sea#2C74B3
sea-deep#1E5687
wood#8C6F52
amber#C7862B
rose#B5462F
green#2E7D53
purple#6B4E9E
How it’s built
Boring on purpose where it counts, so the interesting parts stay small. One repo, one image, one database.
Backend
- Framework
- Python 3.11 · FastAPI · SQLAlchemy 2.x.
- Data
- SQLite in dev, PostgreSQL (Cloud SQL) in prod — multi-tenant on one DB, every row and query tenant-scoped.
- Auth
- bcrypt + a JWT in an httpOnly cookie. Roles admin / dev, plus an orthogonal engineer capability that gates the Ops Runbook.
Frontend
- Framework
- React 18 · Vite · Tailwind · react-router · @dnd-kit for drag-and-drop.
- Shape
- A single-page app served by FastAPI itself — no separate host.
- Design system
- A tiny set of palette tokens + hand-rolled
dw-*keyframes. SVG/CSS charts, no chart library.
Schema & seed
- No migration tool
create_allmakes missing tables; idempotent helpers add new columns on upgrade. Small surface, no Alembic.- Seeding
- First-run + idempotent-upgrade seeding of an admin, a sample board, and example experiments — guarded so a default-password admin never lands in prod.
Deploy
- Build
- Docker multi-stage: Node builds the React app, Python serves it. The
dist/is built in the image — nothing committed. - Run
- Cloud Run (us-east4) on durable Cloud SQL Postgres, secrets in Secret Manager, custom domain cmd-z.com.
The path a report walks
The board is the downstream half of the app’s own feedback machinery. One POST, and a raw report becomes a card you can steer.
external_id so retries don’t double-file.Live data — The home dashboard shows real, anonymized Project Lookout metrics without ever touching Lookout’s raw tables. Driftwood reads two purpose-built read-only SQL views as a locked-down role, with a 4-second timeout that falls back to a clearly-labelled placeholder on any error. Never a 500, never half-real data — the badge tells you which you’re looking at.
Development strategy
A handful of bets that kept a solo build shippable — and reusable — from the first commit.
Config-first from commit one
The board config was the very first file. Building against Lookout as a “worked example” — never a dependency — forced every feature to stay retargetable.
One container, one command
Multi-stage Docker → a single Cloud Run service. No orchestration, no separate frontend host, no CDN to invalidate. Deploy is one line of gcloud.
No migration tool, on purpose
Idempotent bootstrap adds missing tables and columns on boot. For a small schema that upgrades in place, it’s less machinery to break than a migration framework.
Fast checks over heavy suites
FastAPI’s TestClient for quick backend smoke tests; the startup seed is idempotent so tests and cold starts share one honest path.
Dogfooded on a live beta
Driftwood runs the actual Project Lookout beta. Every rough edge in triage got felt first-hand, then filed into its own Stream — the tool tests itself.
Zero-friction evaluation
A one-tap live demo pours a throwaway tenant from a scrubbed snapshot and signs you in — real drag-and-drop, auto-reaped. Nobody has to take my word for it.
Challenges & the calls I made
The parts that took real thought — mostly about staying honest, isolated, and safe to ship.
Real data without ever lying
Showing live Lookout metrics meant a decoupled contract: read-only views, a locked-down role that can’t see raw tables, a 4s timeout, and a labelled placeholder fallback. “Live” vs “illustrative” is always on the badge.
Multi-tenant on a single DB
One board is real; demo sandboxes are throwaway tenants on the same database. Isolation is a tenant column on every row plus a scope on every query — cross-tenant reads must 404, not leak.
Evolving schema without migrations
Trading Alembic for idempotent column-adds keeps the surface tiny, but every new column has to be additive and safe to run on every boot. The discipline lives in code review, not a tool.
Alive, but accessible
The river should feel like it’s moving without ever hiding content or fighting a screen reader. So motion is layered on top of a fully-static DOM and disappears under reduced-motion and in print.
Shipping into a shared project
Driftwood shares a Cloud SQL instance and GCP project with the production Lookout service. Deploys are a scoped, single-service source-deploy — verified on the real domain — so the sibling app is never at risk.
Keeping GETs pure
The auto-Backlog sweep mutates, so it lives only on the board fetch that owns it — never on the stats endpoint. A read should never quietly rewrite the board underneath you.
Roll-out & deployment
The beta widens in four phases — from the person who built it to the members it serves — each with an honest exit gate.
In-House
solo dogfoodOne full shakedownWalk every happy path a member or staffer will walk, then deliberately attack the historically-fragile edges: matching honesty, links, timezones, mobile, light mode, EN/ES, races, empty states.
Exit: every check green or filed as a card · zero open trust breaks · one supervised deep sweep done.
Alpha — with the client
2–3 staff, me in the roomGuided sessionsValidate the admin console, the member-intake flow, and the digest content with the real client — and name & route the data gate before any member arrives.
Exit: staff run it unaided · the pool feels relevant to their trades · the data gate is named & routed.
Beta 1 — 10 members
10 real business owners~15–20 min/day, ~2 weeksThe headline test. Real owners, in their own trades, judge whether “best fits” name work they could win — and whether a non-technical owner gets value in ~10 minutes on a phone.
Exit: best fits read “mostly mine” across trades · 10-minute onboarding · zero trust breaks.
Beta 2 — 50 members
the operation industrializesWeekly cadenceScale, a weekly cadence, a tiering signal, and reliability at volume. Fifty people generate a real queue — and this board becomes the single source of truth for it.
Exit: relevance-miss reports trending down · open P1s under the ceiling · a digest open-rate baseline.
# plain source deploy — preserves env, secrets, Cloud SQL & scaling; only the image changes gcloud run deploy driftwood --source . \ --project lookout-becma-beta --region us-east4
Always scoped to the right project — a bare deploy would mint a stray service on ephemeral SQLite that the real domain never points at. Verification happens on https://cmd-z.com, not the run.app URL, with a confirm that the sibling projectlookout.app is unaffected. Rollback is a one-command traffic shift back to the previous revision.
Part of a small family of machines
Driftwood isn’t a one-off — it’s one of the tools I build under Weird Machine, and it was made to serve a sibling: the product it keeps watch over.
Driftwood
The config-driven beta board you’ve been reading about. Free, self-hosted, yours to fork — running live at cmd-z.com.
Open the live appProject Lookout
A procurement-intelligence platform that finds government & supplier-diversity opportunities and matches each to a member business with a deterministic, explainable fit score. Driftwood is its shore patrol.
Visit Project LookoutWeird Machine
My studio — where these projects get designed and built. Same hands, same taste for small tools that do one thing without asking you to read a manual.
See more work