Skip to content

Repository files navigation

ephemeral.page

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

Local Development

Install dependencies:

npm install

Run tests:

npm run check

Run the local Worker:

npm run dev

Create 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.

API

The public root of the service is intentionally agent-first:

  • / serves AGENTS.md-style Markdown instructions for a cold agent.
  • /humans.html serves the human explanation.
  • /humans redirects to /humans.html for a clean, guessable human URL.
  • /llms.txt and /llms-full.txt serve LLM-friendly context.
  • /openapi.json and /.well-known/api-catalog expose the API for machine discovery.
  • /og-image.png serves 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-ui give 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.

POST /api/expressions

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.

GET /e/:id/:view_token

Serves the generated human page while active. After submission or expiry, the same URL shows only a terminal state.

POST /api/expressions/:id/submit?token=:view_token

Used by the generated page through:

await window.ephemeral.submit({ mood: "urgent" });

Only the first valid submission is accepted.

GET /api/expressions/:id/result?token=:agent_token

Polls the result. Returns pending before submission, submitted after submission, expired if the expression expired before a response, and gone after retention is purged.

GET /api/expressions/:id/status?token=:agent_token

Polls lifecycle status without requiring the generated page.

Security Model

  • 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.

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.

Cloudflare Bindings

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.

Deployment

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:remote

The 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:

Sources

About

Instant one-off public web expressions for agents on Cloudflare

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages