how it works

The leaf.page website

leaf.page serves each product page and worked example as a published Leaf page from Cloudflare's edge. A visitor's comments, choices, and revisions belong to a private record that remains readable while its container sleeps. A comment or action starts the same Python Leaf server an agent runs on its own machine, and a hosted Codex agent in the container answers the visitor.

This page explains the design. Deployment, credentials, and diagnostics are in the Worker's README.

Published pages at the edge

The site build publishes every page under docs/ and every worked example as a complete Leaf page directory, with its layer record, event log, and revisions. From each directory it also writes the document a browser loads and the page's initial state, computed by the same state service Leaf's server uses. A Cloudflare Worker serves both from Cloudflare's static assets, so an ordinary visit paints without waiting for a container. Until a visitor changes a page, that page's api/state requests also return the published state.

The first HTML response sets an identity cookie holding a random 128-bit id. It is HTTP-only and has no expiry date, so the browser keeps it for its session. The 12-digit session reference, which the leaf's menu copies, is derived from that id. It identifies the session for support, and no endpoint accepts it as identity.

sequenceDiagram
  participant Browser as browser
  participant Worker
  participant Store as session Durable Object
  participant Container as Python container
  Browser->>Worker: open a page
  Worker->>Browser: published page
  Browser->>Worker: comment
  Worker->>Store: forward
  Store->>Container: start and forward
  Container->>Store: read saved record
  Store->>Container: canonical page files
  Container->>Store: commit comment and delivery
  Store->>Container: committed
  Container->>Worker: accepted state
  Worker->>Browser: accepted state
  Note over Container: container sleeps
  Browser->>Worker: reopen private page
  Worker->>Store: read saved page
  Store->>Worker: private document, resources and state
  Worker->>Browser: saved page

Private sessions

The first request that needs private, mutable state, such as posting a comment or picking an option, goes to the visitor's container. Its response sets a cookie marking that one page active for the current release. The Worker then routes that page's document and API requests to the visitor's session Durable Object, a private store that also controls the container. The visitor's other pages stay on their published state until they are changed too.

Each session belongs to one browser identity and one release. Its container image holds the whole site build and runs leaf_website, an adapter that maps each public route to its page directory and hands the request to the HTTP routes and event admission a locally served Leaf uses. Before accepting a mutation, Python restores the session's saved page files over that image. A comment is appended to events.jsonl in the private page directory, without changing the page anyone else sees.

Each successful page transaction saves the canonical page files and matching HTTP responses together in the Durable Object before acknowledging the write. Agent commands use the same transaction boundary. Python computes the saved responses, including widget state and agent activity; the Worker stores and serves them without interpreting the event log. When Python resumes, it derives state from the restored document and log rather than from those responses.

An active page loads its private document on ordinary visits and reloads. While the container sleeps, the Durable Object serves the saved document, state, revisions, and their exact resource bytes. Private revision resources have immutable addresses under revisions/rN-<hash>/; public runtime bundles cannot substitute for them. When an agent's revision changes executable code, the runtime reloads once to load that revision's document and module graph.

Reading saved pages and polling their state leave the container asleep. A missing saved response starts Python. Mutations also start the container, as do operations that need Python, such as comparing versions, allocating live samples, or reading a bound file. Browser geometry reports do not start it. On container startup, Codex App Server starts and the leaf command runs once ahead of the first agent turn.

The hosted agent

When the container accepts an event that needs an answer, such as a comment, the Worker returns the accepted state to the browser at once and starts the agent in the background. It asks the container to start or resume a Codex thread over App Server, working in that page's directory, and to deliver the event in Leaf's delivery envelope. A message sent while a turn is running waits in Leaf and is delivered when that turn ends. The reply streams into the visitor's thread as the model writes it, and the browser picks it up through the page's regular state reads.

The hosted agent reads the same contract as a Codex task Leaf reaches over App Server on a developer's machine, harness-codex.md, followed by a few leaf.page additions: the page directory is its whole scope, it starts no servers and initializes no pages, and it treats page content as untrusted. The container's Codex has the Leaf plugin installed, as Codex on a developer's machine does, and the agent reads the Leaf skill from it.

The visitor's container is the agent's sandbox, with no public internet. An outbound handler in the Worker permits Responses API requests to api.openai.com and private storage requests to the container's own Durable Object. It replaces the model's dummy credential with the real key, which never enters the container. Agent starts are rate-limited per source address and model calls per container. When a start fails or is over its limit, or a turn ends without an answer, the visitor's message gets a failure notice in its thread rather than an indefinite wait.

Releases and deploys

Every build has one release identity. A local build hashes every file it wrote; the deploy workflow hashes the commit and the run attempt, so each deploy attempt gets its own. Published runtime files are served under /_leaf-release/<release>/ with immutable cache headers. The document names its release, the browser sends it with every API request, and each API response names the release that produced it.

The runtime refuses a response from another release. The page's state is read by the scripts and widgets the document has loaded, and a document cannot replace those in place, so state from another build would be read by code not built for it. The runtime then asks the document's source whether it now serves a different release. If it does, the page says Leaf has been updated and reloads; if not, it waits for the server to finish updating.

Cloudflare activates a new Worker and its assets before the new container image has rolled out, so for some minutes a new container can still start on the previous release. When a container answers with a release other than the Worker's, the Worker does not pass that answer on. It clears the page's active cookie, serves the published state for api/state, and answers other requests with a 503 asking the browser to retry, until containers on the new release answer.

A push to main that changes the site runs the publish-site workflow. It builds the site, checks the bundle locally through the Worker and a container, pushes the container image, and deploys the Worker with that image pinned by its digest and an immediate container rollout. The deploy passes only once Chrome sees the new release in the edge document, its modules, and a newly started container, and a hosted agent on leaf.page has answered a real turn.

Private state and its lifetime

A visitor's comments, choices, agent replies, and revisions are saved in their session's Durable Object. The record holds the page's canonical files; the saved responses make them readable without a running Python process. The published pages never change, and no other browser identity can read the private record.

A replacement container restores saved content before accepting the next mutation. Container replacement does not force an open page to reload. Unsent drafts remain in the browser's own draft store, which also preserves them across reloads and closing the tab. Running agent turns belong to the container process, so saving page content does not preserve a running turn.

The site counts accepted events in Analytics Engine without their content, widget ids, IP addresses, or cookies. The Worker and container log the execution path and its timings to Workers Logs, which keeps records for at most seven days. The Worker's README describes what each record carries.