Long Live Global Heroku
My favorite way to put something on the Internet is one line:
ssh kedge.dev publish '# Hello, world!'
That's a live page with an HTTPS URL: Markdown in, web page out, served from eleven cities on six continents. Your SSH key identifies you.
My second favorite asks a built-in agent to make the application:
$ ssh kedge.dev agent 'make me a sandwich-rating app'
Building a clean sandwich-rating app with a simple submission form and a live ratings list...
https://stack-rank.kedge.run
The result is a one-file HTML app with a built-in replicated database. A rating submitted in Sydney commits in Sydney and converges asynchronously to the other cities, while the page updates in real time in every open browser. When the app is idle, storage is the only meter running.
When you return later, the agent can pick up the existing app and keep working on it:
$ ssh kedge.dev agent --app stack-rank 'add a dozen sandwiches from around the world to the database'
Adding twelve sandwiches from around the world to the app’s database...
Added 12 sandwiches from around the world to the database, including bánh mì, Cubano, chivito, katsu sando, croque monsieur, arepa reina pepiada, and more.
https://stack-rank.kedge.run
Kedge is the platform behind these commands. It combines a CDN and replicated state with an application model that can grow from a single document into a conventional service. It continues the vision that brought me to Fly.io and shaped my four years there: a "Global Heroku".
too much software
Coding agents are leaving us with too much software. Not just code: stateful applications with URLs, users, accumulated data, and nobody particularly eager to operate them.
An agent can produce an application in an afternoon, or even a few minutes, but what it leaves behind can remain in use for years. Somebody still has to keep it reachable, update the machinery underneath it, and recover from hardware failure or code rot.
Heroku co-founder Adam Wiggins described this problem in 2011. Years earlier he'd built Catapult, a little application on a VPS that his friends kept using after he lost interest. It died from the accumulated indignities of owning a server. The Mongrel process crashed, logfiles filled the disk, and the end-of-life distro could no longer take security updates. The code had barely changed, but everything around it had.
Wiggins called Heroku's answer erosion resistance: the result of a strong separation between an application and its infrastructure, formalized in an explicit platform contract. As long as the application upholds its side, the platform can replace servers, kernels, routers, and whole generations of infrastructure underneath it. That idea grew into the Twelve-Factor App: source becomes an immutable release, processes are disposable, state outlives them, and the platform owns routing, supervision, scaling, and logs.
The clever part of Heroku was never merely git push; it was this transfer of responsibility. A platform abstraction is valuable for what it agrees to keep doing after the deploy command returns. The narrower the app contract, the more the platform is free to improve behind it.
Fifteen years later, agents are making the problem Wiggins exposed grow exponentially. One prompt at a time, a sandwich-rating app grows to include a payments system and its own cryptocurrency, eventually burying itself under a pile of Kubernetes manifests and Terraform resources. What began as a small app becomes a sprawling system with nobody responsible for its day-two operations. With an explicit platform contract, agents can iterate narrowly on the application's behavior while the platform maintains and improves the infrastructure underneath.
a computer is a workshop
The industry's current answer to agent-made software is to hand the agent a computer. Sprites provide a durable logical computer whose filesystem survives its temporary VM. exe.dev sells persistent Linux VMs, calls the model "serverful," and suggests putting development, testing, and production on one machine for small apps.
Kedge offers the same kind of workspace. Its writable root and source checkout survive host failure, while the workspace itself sleeps when idle and wakes on a connection or timer.
Some mess is useful during development. Once an application is published, though, preserved state becomes preserved entropy. It accumulates in the package installed by hand, the restart policy that almost works, and the logs inching toward a full disk. That's what killed Catapult. An application needs the opposite continuity: its source, state, route, releases, and history survive while the servers underneath come and go.
Heroku was "serverless" in this sense before the word came to mean chopping an application into functions: the server disappeared behind the contract. Kedge offers a durable computer when development wants one and disposable instances when an application is published.
small software should stay small
Ask an agent for a guestbook, and you'll often receive a framework, an ORM, a migration tool, an auth library, and a respectable suburb of node_modules. That machinery is cheap to generate but still expensive to understand next week.
On Kedge, the smallest application can begin as a single HTML or Markdown document. If it needs data, declare reads and writes in the document, and Kedge compiles them at deploy time into schema and queries against the built-in database:
ssh kedge.dev publish --html <<'HTML'
<form data-kedge="ratings">
<label>Sandwich <input name="sandwich" required></label>
<label>Score <input name="score" type="number" min="1" max="5" required></label>
<button>rate</button>
</form>
<ul data-kedge="ratings">
<template><li>{{sandwich}}: {{score}}/5</li></template>
</ul>
HTML
All of that fits in one HTML file backed by shared, live, multi-writer data. The attributes resemble htmx, but declare data rather than requests to endpoints you operate. There is no application server or JavaScript of your own.
The form is a plain POST that works with scripting off. With scripting on, new ratings appear immediately in every open browser, and offline submissions queue for ordered replay.
The page declares intent through an explicit contract. Within that boundary, Kedge can change how queries compile, where rendering happens, and what gets cached without changing the application. The less the application specifies, the less that can rot.
Data bindings are Kedge's narrowest application contract. Beyond them, Kedge can run a script with a shebang as a serverless function handler or deploy a conventional service from source or a container image. This layered application model means the sandwich app doesn't need to settle its container strategy before anybody has rated a sandwich.
Agents generate complexity faster than anyone can maintain it, which is why the shortest correct implementation matters. It leaves less for the next session to secure and reconstruct. At its smallest, the application already has a name, URL, database, and deploy history.
cheap to ignore
An app is cheap to create exactly once, but it has to be cheap to ignore every day after. Most agent-made software will be lightly used, so its economics cannot assume a long-running server per URL.
While I was at Fly.io I sketched what I called the Serverless Server: lightweight VMs restored from snapshots on demand, without forcing applications into a function runtime. Kedge builds on that design. A service initializes once per host; Kedge freezes its VM as it begins listening, then resumes later instances from that point with clean memory and filesystem pages shared copy-on-write.
A new instance scales out from a warm pool in about 1.5 ms. Once the app's image is present on a host, a service restores from zero ready for connections in about 16 ms; end-to-end requests take tens of milliseconds.
A conventional service can disappear between visitors without being rewritten as a function, while a one-file handler can receive a fresh hardware-isolated VM for every request. Both scale to zero, so an idle app pays only for storage.
state should not give the app a home address
Even while application code runs in cities around the world, a volume or database pinned to one region still gives the app a home address.
Fly.io's founding vision of a "Global Heroku" was simple: push source to the cloud, have it built into VMs, and run it in cities close to users. Fly.io made globally distributed compute feel like one platform; a single fly deploy could put an app behind an anycast network around the world.
State is where the illusion got thin. A Fly Volume lives on one server's local NVMe: fast and inexpensive, but a physical home address for its Machine. Replication between volumes is strongly recommended, but left to the application. For many users, that is where the Global Heroku abstraction ran out.
Twelve-Factor also left state to a "backing service", typically a database in one region. A global application still has to route requests home or own its replication.
Every Kedge application instead gets a replicated SQLite database at /shared.db and a replicated file tree at /shared/, exposed as ordinary local paths. Reads and writes complete against a host-local copy; changes converge between cities and settle into object storage so the app can scale to zero.
Attaching to a global database service is just opening a file.
The local-first model is intentionally eventually consistent rather than pretending Sydney and Virginia share a memory bus. A write returns after its local commit; if the host dies before shipping it, the current loss window is roughly the final second of writes. Replicas normally converge in under a second, concurrent writes to the same row resolve last-writer-wins, and schema changes or unique-value claims coordinate cluster-wide and can fail retryably during a partition.
Syzy is the engine, a local-first, multi-writer replication layer for stock SQLite and Postgres binaries, inspired by Litestream and Corrosion.
Wiggins wrote the other half of this story too. In 2019 he co-wrote Ink & Switch's essay on local-first software with Martin Kleppmann, arguing that work completes against a nearby copy and synchronization follows. They were writing about personal devices; Kedge applies the same property to application state. An HTML submission appears in the browser, replays to the nearest replica, converges between cities, and settles into object storage.
With replicated state, durable data belongs to the application rather than any particular instance. Kedge serves a data-backed site's files and bindings directly, without an application process; a service still runs as a process, but Kedge can route around a wedged instance and start another without recovering a local disk. Software that needs a local disk for performance, governance, or other requirements can use a traditional persistent volume as an escape hatch.
branch the application, not just the code
Once an application contains data people care about, every change has to preserve or migrate it. A code preview backed by an empty database proves very little, and pointing it at production proves rather too much. Representative previews are hard because source, infrastructure, and state usually branch separately.
Every Kedge app has a Git repository behind it, including apps created by the one-line publishes above. Clone any app and push a non-production branch; it gets an isolated preview URL with its own fork of the database and files while the production branch stays live. A fork creates a separate, long-lived application. The stack-rank app from the top of this post is published as forkable, so you can take a copy of it, sandwiches included:
ssh kedge.dev fork stack-rank
The operation branches source, database, and files copy-on-write at one instant. Unchanged storage remains shared; new writes stay isolated. An agent can rehearse a migration against the application as it exists, inspect the result, and discard the experiment.
Database branching is not new, and Neon is a leading example. The difference is the unit: source, state, and route belong to one application lifecycle, so branching the application is one operation.
what the agent leaves behind
Fly.io has since focused on Sprites and "Computers for Agents." As he handed over the CEO role, founder Kurt Mackey wrote that the two principles behind Global Heroku "probably don't matter anymore". Software is becoming personal, so audiences are small and global placement matters less. To a first approximation, nobody reads documentation anymore; an agent can one-shot a deployment, so ergonomic abstractions matter less too.
I think the same premises lead somewhere else. Small audiences are not necessarily local: families, remote companies, and tiny Internet communities cross continents. Applications with small audiences rarely justify operating their own distributed architecture, which is exactly why a platform should amortize one across all of them.
And an agent can one-shot a deployment, but that says nothing about who keeps the system running. A platform contract is exactly what keeps small software from becoming an infrastructure project.
Agents sometimes need a whole computer to work in, but what should survive is the application they leave behind. I'm still fond of the old promise: bring us your app, and we'll own all the machinery underneath.
Long live Global Heroku.