There's a product shape that got very common in 2026: the app builder. A user describes what they want, an agent writes the code, and your platform runs it — Lovable, Bolt, v0, Replit Agent and a hundred vertical clones. Executing the generated code is now a solved-ish problem. The part that stays awkward is storage: each generated app needs to persist state, and you cannot safely hand AI-written code a binding to your whole database.
On August 3, 2026, during Agents Week, Cloudflare shipped a primitive aimed squarely at this: Durable Object Facets for Dynamic Workers. The one-line version from Cloudflare's own title: give each AI-generated app its own database.
The trust problem, stated plainly#
Say an agent writes a little CRM. It needs a place to keep contacts. Your options before Facets were both bad:
- Give the generated code a Durable Object namespace binding. Now AI-written code you didn't audit can address any object in that namespace — every other tenant's data included. That's a data-isolation hole, not a feature.
- Provision a real database per app — a Postgres branch, a fresh D1, whatever. Correct isolation, but now you're standing up and billing a database service for every ephemeral app a user vibe-codes into existence. That doesn't scale to "thousands of tiny apps."
Facets thread the needle: isolated, real, persistent storage for the generated code, with none of the namespace access.
How a facet works: supervisor and child#
A facet is a Durable Object class loaded dynamically from a Dynamic Worker and run as a child of one of your own Durable Objects (docs). Two roles:
- Your class is the supervisor. It holds the real access and decides what the child is allowed to do.
- The dynamically-loaded class is the facet (child). It gets its own isolated SQLite database, used through the ordinary Durable Object storage APIs. That database is separate from the supervisor's, but the two are stored together as part of the same overall Durable Object.
So the generated CRM code gets a genuine SQLite database it can read and write with normal storage.sql calls — but it never receives a namespace binding, and every capability it has is one the supervisor chose to expose. The shape looks like this:
// Supervisor: a Durable Object YOU wrote and trust.
export class Tenant extends DurableObject {
async runGeneratedApp(appModule, request) {
// Load the AI-written class from a Dynamic Worker and run it as a
// facet — a child with its OWN isolated SQLite database.
const app = this.ctx.facets.get("app", () => ({
// dynamically-loaded, untrusted class
class: appModule.AppDurableObject,
}));
// The child persists to its own DB via normal DO storage APIs.
// It has NO handle to the Tenant namespace — only what we pass in.
return app.fetch(request);
}
}
The generated app writes to storage.sql as if it owned a database, because — inside its facet — it does. What it can't do is reach sideways into another tenant. The supervisor is the only thing holding namespace-level access, which makes it the one place you enforce quotas, kill a runaway app, or revoke a tenant. That's the multi-tenant isolation model most people bolt on with careful query discipline — here it's structural.
Facets vs a database-per-tenant#
This is a genuine decision, not a slam dunk. Use the table above, but the short version:
- Reach for Facets when the storage belongs to code you generate and run on Cloudflare, you want isolation by default with a supervisor choke point, and you don't want to provision or bill a database service per app. This is the "thousands of small, code-owned, isolated databases" case.
- Reach for a per-tenant serverless Postgres (Neon, Supabase, Turso) when you need real relational features, external SQL access (BI tools, another service querying the data), or storage that must outlive your Cloudflare runtime.
Facets aren't trying to be your customers' database. They're trying to be the safe, cheap, isolated scratch space for the apps your agent builds — which is a different job.
The caveat, and the pattern to copy#
Facets are in beta, available immediately on the Workers Paid plan as of August 3. Usable for real building today, but wrap the supervisor/facet calls behind your own thin interface so a pre-GA API change doesn't ripple through your product — the same discipline that keeps any Cloudflare primitive from becoming your architecture.
Even if you never touch Cloudflare, the pattern is the takeaway: untrusted, generated code should run as a supervised child that holds only its own storage, never a handle to the namespace. Facets make that a platform primitive instead of something you invent. Paired with @cloudflare/computer — one persistent computer for the agent — it's the two halves of the same Agents Week bet: give the thing that runs generated code a real filesystem, and give the thing it generates a real, isolated database.



