Gateway app installs
Operator runbook: Gateway app installs, where the Gateway installs app releases itself with its own deploy token (everyapp deploy-token add).
Mirrored from
docs/runbooks/gateway-app-installs.mdin the EveryApp repository, where it is edited.
A signed-in organization owner or admin can install a public GitHub app release or an immutable oss.new conversion from Admin → Apps → Install app. On a self-hosted Gateway that means the install organization's owners and admins (the oldest organization with an owner, the one setup's claim made): the Gateway's CF_DEPLOY_TOKEN is the account's, so no other organization may spend it, and nobody installs before the Gateway has an owner. External sites can open:
https://<gateway>/install?source=<URL-encoded release URL>The source site asks for the user's Gateway address and opens this link. There is no central Gateway discovery service. For oss.new, the source is https://oss.new/apps/<64-character lowercase hex key>. The Gateway preserves this link through sign-in and pre-fills a standalone approval page, outside the app sidebar. Opening the link never installs anything: the admin must submit Install on their Gateway. The source field stays visible while availability, job progress, failures, and the installed app link update below it. A source that came with the link is shown read-only. Opened from Admin → Apps → Install app without one, the field is editable: disabled during installation and after a successful one, when the release's setting and secret fields are hidden too; after a failed installation it is editable again, and editing it clears the previous result and requires a new approval. After an ordinary failure, Try again returns to the reviewed release with the values already entered; a failure that needs a confirmation (deleting Durable Object data or Workflows, replacing a changed Worker, enabling Email Routing) offers its checkbox and Continue with this update instead. Admin → Apps → Install app opens the same page. The source is retained as deployment provenance.
The workspace appears above the source. One eligible workspace is shown read-only; with several, the user chooses from active workspaces they own or administer (on a self-hosted Gateway, only the install organization is ever offered; the Install app entry and this list go by the person, so an admin of the install organization whose active workspace is another one still sees both), defaulting to their active workspace when eligible. Selecting a destination does not change the session's active workspace. Approval and progress requests include the selected organization ID, and the Gateway checks its membership, live admin role, and active status again; on a self-hosted Gateway a request for any organization but the install organization is refused and logged (app_install.refused, with the organization ids and the user). Changing the active workspace in another tab cannot redirect an approved installation or its progress polling. Both fields are locked while installation runs; editing either after completion clears the previous result and requires fresh approval.
Configure a self-hosted Gateway
Gateway app installs are off until the operator turns them on: the deploy token is account-wide, so spending it from a browser is a choice, never a side effect of having stored the token. On a self-hosted Gateway only the install organization's owners and admins install, and only from a session signed in within the last 10 minutes (an older one gets APP_INSTALL_REAUTH_REQUIRED: sign in again, then install; polling a running job has no such rule). Interactive pnpm everyapp setup offers Let your Gateway install apps? (Yes/No, default No), explaining: "Your Gateway gets its own Cloudflare token and installs an app when an owner or admin approves it, for example from a Deploy to EveryApp button. Later: everyapp deploy-token add (or remove)." Enter accepts No and continues setup without changing credentials. Yes runs the same steps as pnpm everyapp deploy-token add. Existing tokens and hosted Gateways are left alone; unattended setup does not prompt, and neither does --yes, which answers No.
Later, or for a Gateway set up without it, run at a terminal:
pnpm everyapp deploy-token addIt opens Cloudflare's page for a new account API token, its name (EveryApp <gateway host>) and permissions filled in through a token template URL: Workers Scripts, D1, Workers KV Storage, Workers R2 Storage, Queues, Hyperdrive, Vectorize, AI Gateway, Secrets Store, Access: Apps and Policies, Email Routing Rules and Zone Settings Edit, and DNS and Zone Read. The five without a short key (Hyperdrive, Vectorize, AI Gateway, Secrets Store, Email Routing Rules) go in by permission group ID, the IDs Cloudflare's API lists. The person picks the Gateway's account and zone, creates the token and pastes it at a hidden prompt; it is stored as CF_DEPLOY_TOKEN with BROWSER_APP_INSTALLS=on, in one Gateway version. An account token belongs to the account, not to the person who made it. A Gateway that holds a token keeps it: add then only turns installs on, so a rerun never leaves a second token live. Under --json it stops with rerun_interactive (needsTerminal) when a token must be made. The everyapp login can't make API tokens itself, so the person creates it.
Setup already set CLOUDFLARE_ACCOUNT_ID. It is one of the Gateway's own keys, so secret set refuses it (SECRET_NOT_SETTABLE).
Workers Scripts Edit also covers Workflows, Queue consumers, cron schedules and Custom Domains; Access Edit lets the installer reconcile public callbacks and bearer routes. Inbound email uses Zone Read (to find the zone), Email Routing Rules Edit and Zone Settings Edit (to turn Email Routing on for a zone that has it off; explicitly enabling routing also changes its MX/SPF records). Missing API permissions stop the installation with CLOUDFLARE_PERMISSION_REQUIRED. Keep the token on the Gateway; neither the installing site nor the browser receives it. The Gateway must already have its normal GATEWAY_URL, APP_BASE_DOMAIN, identity keys, and Custom Domain in that account.
To turn installs off again, pnpm everyapp deploy-token remove takes CF_DEPLOY_TOKEN and BROWSER_APP_INSTALLS off the Gateway (it asks first; --yes skips the question), then delete the token in Cloudflare (Manage account → Account API tokens). To keep the token and only pause installs, set BROWSER_APP_INSTALLS to off:
BROWSER_APP_INSTALLS=off pnpm everyapp secret set BROWSER_APP_INSTALLS --from-env BROWSER_APP_INSTALLS --jsonOnly exactly on enables Gateway app installs; unset, off or anything else keeps them off. Turning them off prevents new installs without affecting already installed apps. Missing deployer credentials still make installation unavailable.
The installer uploads ordinary private account Workers, disables workers.dev and preview URLs, and adds each app's service binding and Custom Domain to the Gateway. Existing Gateway bindings, including secrets, are inherited. Browser and CLI deploys share deployment leases, Worker ownership claims, and the CLI's record of the Gateway's live version, bound apps, and published hostnames. The app's serving deployment version ID is recorded in the resource ledger immediately after upload, so the CLI can manage it even if a later install step fails. Other Workers and existing DNS records are never taken over. The browser can install, retry, and update the same release as pnpm everyapp install; a Worker changed outside EveryApp needs explicit replacement confirmation. Removing Durable Object data or Workflows needs its own confirmation, as does enabling Email Routing or registering bundled providers. A disabled app stays disabled after an update.
Run the first CLI update at a terminal: the pinned Cloudflare CLI asks for confirmation before replacing code previously uploaded through the script API. Later CLI updates can run unattended. This existing Cloudflare confirmation still applies after the Gateway's ownership and version checks pass.
Self-hosted Gateway app installs use @every-app/perimeter/install, the same Worker policy, Cloudflare Build Output schema, placeholder rewriting, resource provisioner, ownership ledger, Durable Object checks, inbound-email planner, and Access route plan as the CLI. It supports D1 SQL migrations, KV, R2, Hyperdrive, own Durable Objects, Workflows, helper Workers, Queues and their consumers, cron schedules, Vectorize, AI Gateways, Secrets Store, declared account bindings (Workers AI, Browser Rendering, Images, email sending, Analytics Engine), app settings/secrets, and Gateway capabilities. The approval page collects installation values; Hyperdrive origins and Secrets Store values go only to provisioning, never to the app as plaintext. Existing secrets are preserved, and setting defaults are applied without replacing configured values. Postgres/Prisma migrations still require source and are refused, just as the CLI release installer refuses them.
Opening an external /install?source=… link reviews the release without registering an app or provisioning account resources. Approval is bound to the reviewed archive hash; a changed release needs a new review. The page identifies an update to an existing app, and lists bundled providers' destination hosts before their separate registration consent. A successful job includes Access reconciliation for the app's public/bearer routes when Access is enabled. Installing a release does not import upstream application data. Gateway providers may still need their credentials configured; certificate readiness and first-visit activation can follow publication.
Hosted Gateways
Hosted installation continues to use its existing THIRD_PARTY_DEPLOYS=on launch gate, dispatch namespace, separate third-party domain, and isolation policy. Enabling self-hosted Gateway app installs does not enable that API or relax its restrictions.
Release intake
Only public GitHub repositories and the exact oss.new endpoint above are accepted. oss.new fetches have a 15-second timeout, send no browser/deployer credentials, and refuse redirects. Both sources use the same bounded archive reader, integrity check, manifest validation, binding allowlist, and migration pipeline. A bad or unsupported bundle is refused before provisioning. There is no arbitrary-URL fetch proxy or source-code execution on the Gateway.
Gateway identity key rotation
Operator runbook: rotate the Gateway's identity signing key pair in a maintenance window; apps answer 401 until they are redeployed with the new public key.
Hosted Install
Operator runbook (hosted tier, post-launch): the hosted Gateway (app.everyapp.com): install, shared first-party apps, the gated third-party deploy API and its deploy keys, launch checks.