Vibes DIY
// the build log

Vibes DIY Blog

Field notes, evals, and stories from building the no-code app builder that turns a sentence into a working app.

Pickathon made the fan-made planner official — your festival could be next

Pickathon made the fan-made planner official — your festival could be next

Pickathon liked the fan-built schedule planner so much they're now co-marketing it as Pick. Plan. Share. If you run a festival like Bumbershoot, Outside Lands, Sasquatch!, or Newport Folk, the same app is a remix away — you just point a coding agent at your schedule feed.

Read the post →

New posts, straight to your inbox

One email field. Real updates. No algorithm required.

Prefer a feed? RSS · Atom

Where your data lives

Where your data lives

Your Vibes app's database isn't a server you call over the network — it's a local replica on the device that answers instantly. Here's the whole path a write takes, in one diagram.

Read the post →
From Local First to User First

From Local First to User First

We didn't return to local-first for the manifesto — we returned because every loading screen we profiled ended at the same place: the network asking permission to show you your own data. The story of a cutover where almost every fix was a deletion.

Read the post →
The first turn does less on purpose

The first turn does less on purpose

We pulled access rules, sound effects, and backend wiring out of the first generation — and taught it to remember what it skipped. The app arrives faster, the skipped work comes back as an offer at exactly the right moment, and nothing gets locked in before you have the context to decide.

Read the post →
Don't reply to bots

Don't reply to bots

Our social bot builds you an app when you mention it — and it just replied, enthusiastically, to a Hacker News mirror bot. The fix wasn't a smarter classifier. It was putting the guard in the layer that can see the tell.

Read the post →
The palette that resized your app

The palette that resized your app

Clicking a color swatch made every app four times bigger. The trail led to a CSS variable name collision with Tailwind's engine, a months-old bug we'd politely documented as a 'platform quirk,' and a repair codemod that proves its own diff.

Read the post →
Your loading screen is a brand deck

Your loading screen is a brand deck

The first-build wait now plays the brand brief Jona Bechtolt of YACHT designed for us — as a pure-CSS storyboard cut from the PDF's own vectors. Along the way the loading screen learned semantics: a color face that means 'seconds away,' recovery that settles instead of rewinding, and status labels mined from the build stream.

Read the post →
The remix is the on-ramp

The remix is the on-ramp

A drum machine turned into four apps in a night — each one a remix of the last. Then we made the homepage work the same way: the remix chain IS the tutorial.

Read the post →
Apply now, verify in the background

Apply now, verify in the background

Making cross-vibe navigation feel instant wasn't about faster answers — it was about asking the access question earlier, on a socket we already had, and remembering the answer honestly. A tour of the small freshness contract behind an instant-feeling hop.

Read the post →
The speed knob that made every mock global

The speed knob that made every mock global

One Vitest flag — isolate: false — gives every test file a shared module registry to run faster, so the last mock quietly wins across files. Three files mocked one module three different ways, a partial mock leaked, and it bricked our npm release for two days. Here's the villain, and the three moves that fixed it.

Read the post →
The meter read zero

The meter read zero

A batch of latent-cost bugs — a green-check gate that a rebase-merge dodged, a cron that did nothing but spin up runners, a deploy that compiled the app twice, an unminified worker parked on Cloudflare's startup-CPU cliff, a lockfile check running once per shard, and a test pool tuned for cores we no longer had. All free and invisible while the repo was public. Going private billed them all at once, and turned it into a performance event.

Read the post →
The invite that can't be an oracle

The invite that can't be an oracle

We built an email-invite growth loop where privacy is structural, not a later patch: the opt-out list shipped before the send button existed, the send endpoint returns the same 'ok' whether or not you're a user, and a sandboxed app can ask to invite a friend but only the platform gets consent.

Read the post →
The eval learned to read the UI

The eval learned to read the UI

Our codegen eval graded generated apps with static regexes — until the failures got semantic. A regex can't tell who wrote a document, and it can't see a quiet refusal: an app that technically satisfies a social-sharing prompt but buries the share affordance in its UI. So the eval hired a judge that reads the generated App.jsx and grades what a user would actually experience.

Read the post →
Ship it dark: the scariest change is the smallest line you write

Ship it dark: the scariest change is the smallest line you write

We shipped social-graph notifications as a sequence of dark slices — schema first, then the wire, then the digest — so the PR that finally turned the feature ON was the smallest and least risky one in the stack. Merge ordering was the rollout guard, no feature flag required.

Read the post →
The three doors

The three doors

Three 'public' schedule feeds — DEF CON, Oregon Country Fair, IETF side meetings — each turned out to be unreachable from where the app runs. The fix walked the data in through the app's own database. Here's how a workaround hardened into a genre, and why the real answer is a server-side fetch.

Read the post →
Add a developer to your vibe

Add a developer to your vibe

You can now let another account edit and ship one of your vibes — push code, publish, run codegen — without handing over your account. One command, or one row in the sharing panel. You keep the destructive and ownership levers.

Read the post →
Followers can see it: Vibes grew a social graph

Followers can see it: Vibes grew a social graph

One platform-wide follow graph, and every app can build on it: a one-line audience label makes a doc visible to your followers, resolved live at read time — new followers see history instantly, an unfollow revokes instantly. Follows now ring the bell and land in the weekly digest — and a follow request to your private account reaches your phone.

Read the post →
The gallery is in the HTML now

The gallery is in the HTML now

The front-page gallery used to wait for React, a WebSocket, and two feed requests before painting a single tile. Now the worker composes the same feed during server render, and the gallery is real pixels in the first HTML byte stream — wearing MTV-style credit plates.

Read the post →
Your app is a database you can npm install

Your app is a database you can npm install

A 40-line Node script opens the same live Fireproof database as your deployed vibe — writes flow both ways in real time, with no REST API, no schema migration, and no API keys to manage. The vibe is the always-on peer; your script just drops in.

Read the post →
Delete is just a put

Delete is just a put

We shipped a careful, provenance-tracked delete permission — and deleted it a few hours later. A delete is sugar for writing a tombstone, and anyone who can write a doc could always destroy it by overwriting. The fix wasn't a better gate; it was one less concept.

Read the post →
You've come a long way, baby

You've come a long way, baby

A year ago this platform was a React SPA with no backend at all — its entire server story was vending an OpenRouter key into your browser. Today it's a stream-based platform with live data, access rules, and server-side AI. The retrospective, with the credit where it's owed: Meno Abels hardened the architecture everything since is built on.

Read the post →
Asked to build platform infrastructure, the agent picked a vibe

Asked to build platform infrastructure, the agent picked a vibe

We asked an agent to build our social mention-builds bot — @-mention us on Bluesky and the platform builds your app and replies with a screenshot. It surveyed the codebase and chose a vibe as the production home: scheduled backend, credential vault, access control, dashboard, all product primitives. When your own agents reach for your product to run your platform, that's what feature-complete looks like.

Read the post →
We shipped a perfect cache that zero real users could hit

We shipped a perfect cache that zero real users could hit

The SWR cache for vibe HTML worked flawlessly — 230ms hits, correct stale-refresh lifecycle — for the bare URL nobody requests. Every real page view carried one query param, and both ends of the cache read it as personalization and bypassed. The fix wasn't more cache; it was teaching the eligibility gate that the platform's own version pin is a cache key, not a personal preference.

Read the post →
The cruft isn't in the files — it's in the seams

The cruft isn't in the files — it's in the seams

Feature velocity is way up, so we braced for a mountain of AI-generated cruft and ran a whole-monorepo housecleaning audit: five parallel explorer agents plus a dead-code linter. The files came back mostly clean. The debt was hiding between them — in workflows, manifests, and docs nobody rewrites.

Read the post →
Cloudflare ate our ETag

Cloudflare ate our ETag

We shipped a whole conditional-request pipeline — input-derived ETags, a 304 fast path, an edge cache — and none of it ever fired, because Cloudflare silently strips validators from HTML. Three plausible fixes died on the wire before a zone-settings dump found all four culprits.

Read the post →
Trust the target, bless the person

Trust the target, bless the person

Designing outbound fetch for user backends died in one sentence: the owner is an attacker in some cases. So trust comes from somewhere that isn't the tenant — the target's own CORS policy, a platform list where adding a host is a PR, and blessing people instead of approving hosts forever.

Read the post →
One pixel of fake scroll

One pixel of fake scroll

iOS scrolls to the top when you tap the status bar — unless your whole app lives in a cross-origin iframe, where the gesture never arrives. We catch it anyway: a hidden pixel of overflow, parked at scrollTop 1, that only the native tap can move.

Read the post →
Your backend can call the model now — and there's no key to leak

Your backend can call the model now — and there's no key to leak

backend.js gets ctx.callAI: one await, text in, text out. No API key exists anywhere in your app — the platform places the call like a switchboard operator, meters it to the user who triggered it, and hands your server code the answer.

Read the post →
Remix meta-hub into your own social media machine

Remix meta-hub into your own social media machine

Every post on this blog now ships itself to Instagram, Threads, and Facebook — published by an owner-only vibe that keeps the API tokens as database records and rotates them on a schedule. Because the publisher is a vibe, one remix makes the whole machine yours.

Read the post →
The third implementation is a markdown file

The third implementation is a markdown file

We built iPhone calendar subscriptions into a festival picker, ported them to a bike-ride tracker, then distilled everything both apps taught us into a codegen skill — and proved it works by asking for the feature in one sentence.

Read the post →
First paint is the app

First paint is the app

How fast a user first sees their app is the product. In one July arc we cached the entire HTML serve at the edge, seeded SSR with real data, compiled the styles on the server, took the auth overlay off the critical path, started the first build before the browser even landed on the page, and made the reveal push-first instead of polled. Then dark mode handed us the invoice.

Read the post →
Announcing beast-mode: follow-through for high-fan-out development

Announcing beast-mode: follow-through for high-fan-out development

We extracted our agent working culture into a portable Claude Code skill. It doesn't make one change better — it makes ten parallel changes all land. Install it in any repo with one command.

Read the post →
Ninety days, twelve headline features

Ninety days, twelve headline features

Since April 1 the platform grew a live data layer, per-app access rules, a server side, in-app editing, a real CLI, DMs, server-rendered vibes, env vars and secrets — and this blog. The quarter in twelve headlines, with links to the deep dives.

Read the post →
Every PR plants a seed: how this blog gets written

Every PR plants a seed: how this blog gets written

Every unit of work that ships here drops a one-file capture note next to the code. Periodically we mine the pile and combine the good ones into posts. That's why reading this blog is the closest thing to watching the project happen in real time.

Read the post →
270 PRs in 17 days — the speed was the safety

270 PRs in 17 days — the speed was the safety

We pulled the stats on 17 days of merges: 270 PRs, ~223k lines of churn, a median PR of 196 lines merged in under an hour, 95% agent-authored. The surprise wasn't the volume — it's that velocity and safety turned out to be the same property.

Read the post →
Not a loop, on purpose

Not a loop, on purpose

Everyone assumes an AI app generator must be an agent loop — a model reading files with tools, editing, iterating. Ours is a pure function: database state in, one streamed response out. That purity is load-bearing, and it's a strength, not a lag.

Read the post →
Every vibe just got a backend

Every vibe just got a backend

One new file — backend.js — runs on our servers for your app: answer webhooks at your own /_api URL, react to every saved document, and run work on a timer. Server writes go through the exact same access rules as user writes, so your permission model stays in one place.

Read the post →
From one CLAUDE.md to forty runbooks

From one CLAUDE.md to forty runbooks

A year of agent documentation, read straight from git history: a 103-line init file, a 326-line grab-bag, a hard split into version-controlled runbooks — and what each era says about the work we were shipping at the time.

Read the post →
If tokens were free, we'd precompute every chip

If tokens were free, we'd precompute every chip

A suggestion chip is a transform of the vibe you're on, and clicking it is real codegen. So we cache the result — a repeat click becomes an instant read instead of regenerating. The interesting part isn't the cache; it's the deny-by-default gate that decides who is allowed to read it.

Read the post →
From the group chat to the front counter

From the group chat to the front counter

We've always said 'make apps with your friends.' True — but it undersold the thing. The same tool that builds a trivia app for the group chat takes the order and the payment for the taco truck, and retires the spreadsheet a small shop has been fighting for years. Here's the repositioning, and why 'we build apps' is the weakest thing we could say.

Read the post →
The edit button that was secretly a save loop

The edit button that was secretly a save loop

We made the code editor live inside the running app — no hop to a separate chat route. 'Make the Code tab editable' sounded like a UI task. It was a distributed save loop in disguise, and the whole thing hinged on one decision: re-pin, don't navigate.

Read the post →
Why our agents don't get a laptop

Why our agents don't get a laptop

A capability that only works on a hand-configured laptop quietly pushes all the work back to the laptop. Here's why we made cloud containers the default home for agent QA — and three war stories from getting browser validation to run there.

Read the post →
Edit a stranger's app, and it's yours

Edit a stranger's app, and it's yours

The /vibe page now meets you where you are: a non-owner edit forks the app to your handle in place, owners get a private draft they can publish in one tap, and small indicators always tell you whether you're the author, an admin, or a read-only guest.

Read the post →
We only needed one Durable Object

We only needed one Durable Object

We had three Durable Object classes that were really one handler surface opened against three shard keys. Here's how we collapsed them into a single class — and which 'separate-class' argument fell apart the moment we pushed on it.

Read the post →
We deleted our auth dependency. Nothing changed.

We deleted our auth dependency. Nothing changed.

We moved our device-id PKI and Clerk token verifiers in-house, out of @fireproof/*, with zero call-site changes — gated by a byte-compatible golden harness so the lift was provably identical before we cut the dependency.

Read the post →
We deleted the preview pane

We deleted the preview pane

We moved first-generation onto the deployed app itself — stream into the card, hot-swap the running iframe, de-blur the app forming behind it — and in doing so deleted the separate preview pane entirely. The risk was the simplification.

Read the post →
Save a tree, skip the tests: leaner CI for docs-only PRs

Save a tree, skip the tests: leaner CI for docs-only PRs

A README typo doesn't need a Playwright suite and a Postgres cluster. Teaching docs-only PRs to skip the heavy CI work — without falling into the 'required check stuck pending' trap.

Read the post →
The NUL byte that broke every write (+7 more)

The NUL byte that broke every write (+7 more)

Eight little fixes from the build log — a NUL byte that took down every Postgres write, an iOS audio-unlock race, a markdown blank line that clobbered a file — each with a lesson bigger than the diff.

Read the post →
Teaching an AI you're not 'the owner'

Teaching an AI you're not 'the owner'

Generated apps used to default to owner-gated permissions — broken for everyone but the creator. Here's how we flipped the default to author-owned, steered the model toward multiplayer without ever saying 'don't,' and built a metric so the prompt could iterate itself.

Read the post →
The vibe that locked out its owner

The vibe that locked out its owner

A vibe locked its own owner out of saving. The fix wasn't a better isOwner check — it was deleting isOwner and treating 'owner' as a seeded, revocable role instead of an ambient super-user.

Read the post →
How we batch-upgrade apps with screenshots

How we batch-upgrade apps with screenshots

Every deployed Vibe serves both a live screenshot and its own source. That closes a visual feedback loop an agent can run alone — see the app, fix the code, push, look again — and fan out across dozens of apps at once.

Read the post →
How we eval the generator

How we eval the generator

Every edit to the system prompt changes every app anyone generates. Here's the harness that measures generation quality — 46 prompts in three sets, scored 1–5, fanned out across a Workflow pipeline — so we know within a coffee break whether a change made things better or worse.

Read the post →
Can one English prompt rebuild our festival app?

Can one English prompt rebuild our festival app?

We tried to recreate Pickathon's built-out 2026 app — data model, views, and a fiddly access policy — from a single plain-English prompt. It got ~90% there on description alone; a URL and a one-sentence schema closed the rest.

Read the post →