ephemeral.page is an instant web expression service for agents. An agent creates a one-off public page, a human submits one focused response, and the invoking agent retrieves the result.
This repository currently implements the local MVP loop on Cloudflare Workers primitives:
create expression -> open page -> submit once -> poll result -> expire/cleanup
Install dependencies:
npm installRun tests:
npm run checkRun the local Worker:
npm run devCreate an expression:
curl -s http://localhost:8787/api/expressions \
-H "content-type: application/json" \
-d '{
"intent": "Ask the user whether this audio clip sounds calm, urgent, or confused.",
"result": {
"desired_shape": "{ mood: string, notes?: string }"
},
"expires_in": "24h"
}'Open the returned url, submit the page, then poll the returned result_url.
The public root of the service is intentionally agent-first:
/serves AGENTS.md-style Markdown instructions for a cold agent./humans.htmlserves the human explanation./humansredirects to/humans.htmlfor a clean, guessable human URL./llms.txtand/llms-full.txtserve LLM-friendly context./openapi.jsonand/.well-known/api-catalogexpose the API for machine discovery./og-image.pngserves the social preview image referenced by the human page.- Search explainer pages such as
/what-is-ephemeral-page,/for-agents,/compare/human-in-the-loop,/compare/form-builders, and/compare/mcp-uigive classic search engines and AI search systems focused crawl targets.
The research and rationale are recorded in docs/agent-readiness-research.md. The headless onboarding evidence is recorded in docs/agent-onboarding-evals/2026-05-09-headless-codex-claude.md.
Creates one expression.
{
"intent": "Ask the user whether this audio clip sounds calm, urgent, or confused.",
"mode": "interactive",
"materials": [
{
"type": "audio",
"url": "https://example.com/clip.mp3",
"label": "Audio clip"
}
],
"result": {
"desired_shape": "{ mood: string, notes?: string }"
},
"expires_in": "24h"
}The response includes a human page URL plus agent-only status/result URLs. The MVP uses separate bearer capability links for the human and agent surfaces.
Use mode: "preview" or interactive: false when the intent is to render already-written content as a static, non-interactive preview and collect only the declared result object. Preview mode can show copy that mentions passwords, account setup, sign-in steps, or SaaS brands, but result.desired_shape must not declare secret-like fields such as passwords, API keys, OAuth codes, or tokens.
Serves the generated human page while active. After submission or expiry, the same URL shows only a terminal state.
Used by the generated page through:
await window.ephemeral.submit({ mood: "urgent" });Only the first valid submission is accepted.
Polls the result. Returns pending before submission, submitted after submission, expired if the expression expired before a response, and gone after retention is purged.
Polls lifecycle status without requiring the generated page.
- The request/API surface is shared.
- The human URL and agent URLs use separate high-entropy capabilities.
- Capability hashes, not plaintext tokens, are stored.
- The generated page receives only
window.ephemeral.submit. - The generated page does not receive R2, Durable Object, AI, secret, account, or result-read bindings.
- Create requests are rate-limited before material mirroring or Workers AI composition.
- Requests that ask for secrets, logins, impersonation, dark patterns, or automatic submission are rejected before composition.
- Static preview requests can render existing copy that mentions credentials or sign-in language, but cannot collect credential-shaped result fields.
- Generated page output is checked for disallowed fields, embeds, browser storage, navigation, external fetches, timer-based submission, and sensitive-data copy before it is served.
- External materials must use
https:and are mirrored into private R2 before being served through expression-scoped routes. - The page sends
no-store,no-referrer,nosniff,DENY, and a strict CSP.
Future Welcome Mat / DPoP agent auth can replace the agent result capability without changing the expression lifecycle.
active -> submitted -> purged
active -> expired -> purged
Submission immediately closes the active human flow. The result remains pollable by the agent until the retention cutoff, which defaults to expires_at in the MVP. Cleanup is idempotent and can be retried safely.
Configured in wrangler.jsonc:
EXPRESSIONS: Durable Object namespace for one expression state cell per expression.CREATE_RATE_LIMITER: Durable Object namespace for public create throttling.EXPRESSION_ASSETS: private R2 bucket for mirrored materials.AI: Workers AI binding for production page composition.
Local development uses Cloudflare's local runtime simulation plus a fixture composer and local expression runtime approximation. Production can enable Workers AI by setting COMPOSER=workers-ai; the default production model is Gemma 4:
@cf/google/gemma-4-26b-a4b-it
The production Wrangler environment config binds Workers AI as AI and sets COMPOSER=workers-ai. The default local environment intentionally does not bind AI, so local tests and wrangler dev stay fixture-only and do not create remote AI usage.
Production isolation still needs a remote smoke test for Dynamic Workers or Workers for Platforms.
The production target runs on Cloudflare Workers at:
https://ephemeral.page
Deploy and verify:
npm run deploy
EPHEMERAL_ORIGIN=https://ephemeral.page npm run smoke:remoteThe workers.dev route is disabled in Wrangler so production traffic uses the ephemeral.page custom domain.
Callbacks are disabled in the MVP; agents should poll result_url or status_url.
The deployment runbook is the source of truth for bootstrapping new environments, CI secrets, Cloudflare resources, and domain cutover:
- Requirements: docs/brainstorms/2026-05-01-ephemeral-page-cloudflare-isolated-mvp-requirements.md
- Plan: docs/plans/2026-05-01-001-feat-cloudflare-isolated-expression-mvp-plan.md
- Remote smoke checklist: docs/remote-smoke.md