Kitsoki is an autonomous application platform: an application is declared as data — a definition, an ontology, and a set of typed effects — and the platform runs it, observes it, and deploys it. That last part is unconventional - an application deploying itself.
The companion post, The freedom of a 10 GB ceiling, argues that Cloudflare's primitives change what the data plane looks like. This one argues that they change what shipping looks like too: because every Cloudflare resource is an API call rather than a machine, a deployment can be an application we run, test and keep receipts for, instead of a pipeline someone maintains beside the product. That turns out to matter most for the two things a release pipeline is usually worst at: proving what shipped, and not being the most privileged, least examined thing you own.
Building a global SaaS is hard. DNS, ingress, identity, data, backups, and recovery all need an answer. Doing that as a small team without a dedicated operations, database, or procurement function makes it harder still. Cloudflare removes a meaningful amount of that operational burden in exchange for a different set of architectural primitives.
What if my reverse proxy was also my application?
This is the well-trodden part of the story, so it gets one section - but it is the setup for everything after it, because once the entry point is your own code, the shape of a release changes.
At the frontend layer, a Cloudflare Worker can replace Caddy, nginx, or another reverse proxy with application code at the edge. It can authenticate a request, apply policy, and serve an object from R2, D1 or Durable Objects without a round trip through a central application server. The authoritative service remains available for decisions and mutations that require it; the common, safe path can stay close to the user.
flowchart LR
Browser[Browser] --> Edge[Cloudflare Worker at the edge]
Edge -->|validate session and policy| Claim[Verified request context]
Edge -->|read object| R2[R2 object storage, S3 compatible]
R2 --> Edge
Edge -->|response| Browser
Edge -. decisions and mutations .-> Authority[Authoritative application service]
Authority --> Edge
The whole shape
Put the pieces together and the platform has a clear request and lifecycle shape. This is a design-level view: the exact data backend is an explicit environment choice, not an accidental implementation detail.
Two words in the diagram are ours rather than Cloudflare's. A colour is one complete deployed copy of the system - blue or green - and there are always two, so a release is a promotion between colours rather than a mutation of a running one. The router is a small public Worker whose only job is to decide which colour a request belongs to; everything behind it is private.
flowchart LR
Browser[Browser] --> Router[Public router Worker]
Router --> Colour[Private colour Worker]
Colour --> Session[Durable Object for session and lifecycle]
Session --> Container[Private Cloudflare Container]
Colour --> D1[D1 edge SQLite where selected]
Colour --> R2[R2 artifacts]
Container -->|typed internal request| Colour
Yes, but you must test it
We use a local Cloudflare lab for the boundaries ordinary unit tests cannot see: a real Docker-backed Container, workerd, and a real local D1 database. It lets us test the deployment model red/green and compare the cloud-shaped layout with local development on a developer’s machine.
Testing a traditional deployment with high fidelity requires high-availability Kubernetes and Postgres clusters. Standup, teardown and validation of these environments is an entire industry - doing it fast on a developer's machine is not typically feasible. There are many SaaS applications today where dev, QA, etc... cannot feasibly run the application in lab mode on any local machine. Kitsoki on Cloudflare gets much closer than a Kubernetes and Postgres stack ever could - not perfectly, since local emulation and the real platform still differ on storage semantics and timing, which is exactly why the lab's job is to prove boundaries rather than to stand in for production.
flowchart LR
Code[Change] --> Lab[Local lab: workerd, Docker, and D1]
Lab -->|prove the boundary| Gate[Typed checks and receipts]
Gate -->|landed source ref| Deploy[Deployment automation]
Deploy --> Verify[Provider and public read-back]
The lab allows us to do fast red/green testing of deployment issues in hermetic environments with a tight dev loop. Dev and QA can run the entire environment on their own machine in a relatively lightweight way, and it can be simulated within a CI container.
This matters more at the edge than it would elsewhere, because the debugging tools are thinner. There is no psql session to attach to a customer's store at two in the morning, no EXPLAIN on a slow query you did not anticipate, and logs are spread across many small objects rather than pooled in one place. An architecture that removes the operational escape hatch has to replace it with something, and for us that something is a lab faithful enough to reproduce the failure and a platform that emits typed evidence rather than log lines.
Deployments on the application plane
One major goal for Kitsoki is to put everything on the single application plane and remove shell/CLI from our lives. Cloudflare's API-driven resource management allows the entire deployment to be done via the application itself - a Kitsoki story, which is our unit of executable process: a declared sequence of states and typed effects that can be tested with flow tests, cassette mocks and hermetic environments, exactly like any other application we run.
Three more of our words appear in the diagram below. A gate is a check that must be green before a step may proceed. A landed ref is the exact committed source revision a gate went green against, carried forward so that what deploys is provably what was tested. A receipt is the typed, durable record a step leaves behind - what ran, against which ref, with what result. And the router promotion is a compare-and-swap: the new colour becomes live only if the router still points where we last observed it, so two concurrent deployments cannot interleave into a half-promoted state.
flowchart LR
Story[Kitsoki story and gate] --> Green[Green landed ref]
Green --> Image[Build and pin image]
Image --> Inactive[Deploy inactive colour]
Inactive --> Warm[Warm and verify]
Warm --> Router[Router CAS promotion]
Router --> Drain[Drain previous colour]
Drain --> Receipt[Deployment receipt]
Those three words are load-bearing well past the diagram, because a story's run is a trace of typed effects rather than a pile of shell side effects — and a trace can be re-evaluated. Given the landed ref and the receipts, any single step of a deployment can be reconstituted and run again in isolation: the same inputs, the same gate, on its own, without replaying the six steps in front of it or rebuilding the environment by hand.
That turns a red deployment step into an ordinary debugging loop. A CI log tells you what happened and leaves you to reproduce it; a trace lets you re-execute it, change one thing, and watch the same gate go green. It is the same red/green loop the local lab gives us for boundaries, applied to the release itself — and it is why the gates and the receipts are typed rather than printed. A log entry cannot be re-evaluated. A receipt naming its ref, its inputs and its verdict can.
Replaying a step that talked to Cloudflare is only isolated if the provider talks back from a cassette — the recorded, structured response captured from the real API — rather than being called again, which would just be another deployment. That is also where structured mocks earn their place: a gate needs exercising against the failures a provider will not produce on demand, like a rate limit mid-rollout, a 500 while draining the previous colour, or a promotion that half-succeeds. Those are the cases worth having a red/green loop for, and they are the ones you can never schedule.
Kitsoki stories make that sequence an executable practice rather than a release checklist someone has to remember. The local lab answers “does this boundary work?”; deployment automation answers “did this exact landed source reach the provider and remain healthy?” They are different questions, and we keep both answers.
Fewer things to attack, fewer things to patch
A release pipeline is usually the least examined and most privileged thing a team owns. It holds the credentials that can change production, it runs arbitrary shell, and it is maintained by whoever touched it last. Putting deployment on the application plane is partly an ergonomics argument and substantially a security one.
One public surface. The router Worker is the only thing on the internet. Colour Workers, the session Durable Object and the private container are not addressable from outside — there is no ingress controller with its own CVE stream, no load balancer configuration to get wrong, no admin port that was only ever meant to be reachable from the office. The attack surface is a single Worker whose entire job is deciding which colour a request belongs to.
The executor is never handed a command. A gate is requested by name from a declared catalog; the request carries inputs, not a program, arguments, working directory or environment. A caller cannot compose a command to run, which means the pipeline cannot be turned into a remote shell by anyone who can reach it — including us. Credentials travel the same way: as the name of an environment variable, never as a value on a command line, so a secret cannot end up in an argument list, a process table, or a log of what was run.
Provenance is the default, not an add-on. A landed ref means what deployed is provably the revision a gate went green against, and the receipt records which ref, which inputs and which verdict. That is a supply-chain answer that most pipelines reconstruct after the fact from CI logs and commit archaeology, if they can answer it at all. Here it is the artifact the step produces. Combined with replay, "what exactly shipped, and would that same step still pass?" is a question with an answer rather than an investigation.
Failure is bounded and reversible. Promotion is a compare-and-swap on the router, so a deployment either happens or does not; there is no half-promoted state for two concurrent releases to leave behind. Rolling back is repointing the router at the colour that is still running and still warm — not a redeploy, not a restore, not a hurried revert commit at the worst possible moment.
And the maintenance bill is mostly gone. No runners to patch, no Kubernetes version to chase, no ingress controller upgrade, no build agent quietly running an ancient Node. The things that generate routine security work for a small team are the things this architecture does not have — which matters more than it sounds when the alternative is a two-person team choosing between shipping and patching.
Next steps: the container has to go
We are going completely container-free with the Kitsoki Runtime. Containers are the last piece of this architecture that still behaves like a server - they boot, they idle, and they are the reason proactive warming has to exist at all. The runtime replaces them with execution that belongs natively on Workers and Durable Objects, including a new LLM harness designed for that shape rather than ported onto it. An agent loop written for a long-lived process assumes it can hold state in memory across a conversation and block on a model call for as long as it takes; neither assumption survives contact with the edge. The harness is built the other way around - durable state where the platform puts it, and turns that can suspend and resume - so agent work becomes something the platform schedules rather than something a process babysits.
Both halves of this architecture are the same bet, and the data-plane post makes the other one. The primitives the edge gives you are smaller and more explicit than the ones a traditional stack hands you, and the work is in building up from them rather than emulating what you left behind.