# Forge Deploy architecture

Status: Phases 0–2 shipped in the access-controlled Alpha on
`sites.smol.ai` on 2026-07-24. Phases 3–5 remain proposed.

## Shipped-phase ledger

| Phase | State | Owning-surface evidence |
| --- | --- | --- |
| 0 — Sites foundation | Shipped | Exact-SHA static pushes, immutable previews, production activation, rollback, disable/suspend, source-visibility shutdown, `noindex`, shared-domain safeguards, and usage telemetry were exercised on the live edge. |
| 1 — control plane and trust | Shipped | The generalized project/release model is the sole authority; Alpha membership and current Terms gate enrollment, and automatic production publication requires the protected-branch baseline. |
| 2 — constrained dynamic Deploy | Shipped | Assets, HTTP, managed SQLite Durable Object state, bounded hibernating realtime, preview isolation, deny-all egress, quotas, durable invalidation, rollback without state rewind, automated managed and connected-provider preview retention, direct exact-tree source materialization, and content-addressed provider-version reuse are implemented. |

“Shipped” here means available inside the controlled trial. It does not mean
the shared `smol.ai` registrable domain has passed the Phase 3 production
isolation gate.

The control plane also has a canonical account and entitlement foundation:
personal and organization accounts, account memberships, versioned
`free`/`pro`/`team`/`enterprise` service tiers, time-bounded overrides, usage
gauges and reservations, and immutable entitlement receipts compiled into
Deploy plans. Forge Identity is a controlled-preview capability built on that
foundation; it exposes app-scoped identities and explicitly consented
account/team claims without exposing Forge control-plane sessions.

## Product definition

Forge Deploy turns an exact SmolForge commit plus a versioned Forge manifest
into an immutable, inspectable release.

SmolForge remains authoritative for:

- source repository and commit identity;
- project and environment configuration;
- deployment lifecycle and promotion state;
- provider selection and compiled application capabilities;
- permissions, approvals, audit, usage, and billing.

Cloudflare, Railway, and Modal are execution providers. Provider deployment
IDs, credentials, endpoints, and lifecycle details are observed implementation
state. They do not replace Forge deployment records or policy.

## System flow

```mermaid
flowchart LR
  Git["SmolForge Git<br/>exact commit SHA"] --> Manifest["forgeBuild.ts<br/>repository intent"]
  Manifest --> Release["Environment-neutral release"]
  Release --> Build["Restricted build runner"]
  Build --> Ingest["Trusted artifact ingest"]
  Policy["Environment policy"] --> Plan["Immutable forge-plan/v1alpha1"]
  Release --> Plan
  Ingest --> Plan
  Plan --> Adapters["Provider adapters"]
  Adapters --> CF["Cloudflare<br/>application components"]
  Adapters --> Railway["Railway later<br/>services"]
  Adapters --> Modal["Modal later<br/>jobs + GPU"]
  Adapters --> Applied["Environment deployment"]
  Applied --> Preview["Immutable preview"]
  Applied --> Activate["Environment activation"]
  Activate --> Route["Forge serving and routing"]
```

The lifecycle is:

> Select exact SHA -> create release -> build typed artifacts -> compile an
> environment plan -> apply provider units -> verify deployment -> expose
> preview or activate production -> monitor, suspend, or roll back.

The release is environment-neutral and immutable. A deployment is that release
applied to one environment and resource scope. Preview and production
deployments may reuse the same release artifacts but never silently share
mutable resource instances.

Release build completion, environment-plan compilation, provider publication,
deployment readiness, endpoint availability, and production activation are
distinct states.

## Architectural boundaries

Keep a modular control-plane monolith while physically separating the
boundaries where untrusted code or privileged credentials make isolation
material:

- Git storage and ref coordination;
- build execution;
- dynamic user-code execution;
- future agent execution;
- user-content serving and routing;
- trusted provider and outbound credential proxies.

Cloudflare Workflows may continue as the initial durable orchestrator because
the existing runner already uses it. Workflow choice is an internal control
plane decision, not part of the repository manifest or provider portability
promise.

Neither build nor runtime sandboxes receive Cloudflare, Railway, Modal,
storage, model-provider, Forge-session, or master deployment credentials.
Trusted adapters perform provider operations.

## Accounts, entitlements, and hosted identity

An **account** is the ownership, quota, suspension, and future billing
boundary. Every user has a personal account; every organization has a distinct
organization account. Teams are access-control groups inside organization
accounts and never own service tiers or usage. The account that owns a Deploy
project determines its entitlements, so an individual user's Pro tier cannot
raise the limits of a Free organization.

Service tiers are versioned policy inputs, not deployment plans and not payment
provider records. Forge resolves tier defaults, time-bounded account
overrides, lower project requests, and platform safety ceilings into an
immutable entitlement snapshot and digest. The deployment plan records that
receipt. Runtime requests fail closed when their signed account generation or
entitlement digest is stale.

Forge Identity is an optional application capability declared in
`forgeBuild.ts`. Authorization uses exact callback origins, authorization code
plus PKCE, short-lived single-use codes, app-scoped user identifiers, and
host-only secure application cookies. Account and team claims require explicit
scopes and are revalidated against membership and access generations. Hosted
code receives only a projected `env.forge.identity` principal; it never
receives the Forge console cookie, control-plane bearer token, membership
directory, database binding, or broker credential.

The account service, Deploy control plane, artifact authority, and direct
control-plane recovery route remain independently operable. Forge can deploy
its own product and consume Forge Accounts/Identity without making recovery of
those foundational services depend on the hosted application.

## Design principle: semantic resources before provider products

Forge's public configuration describes the behavior an application requires,
not the product currently selected to implement it. Repositories give resources
stable logical names and declare semantics such as:

- transactional SQLite-compatible data;
- eventually consistent key/value data;
- object storage;
- keyed, serialized actor state with optional realtime delivery;
- persistent services or asynchronous jobs.

Projects may declare multiple resources with the same semantics. For example,
`primaryData` and `analyticsData` may both require SQLite-compatible databases,
while `sessions` and `featureFlags` may both require key/value stores. Those
logical names remain stable when a trusted planner maps them to D1, KV, R2,
Durable Objects, Railway, Modal, or a future adapter.

Portability means that a replacement adapter satisfies the declared interface,
consistency, durability, locality, and lifecycle contract. It does not mean
that Forge pretends D1, PostgreSQL, Redis, and Durable Objects are
interchangeable. Provider changes that require data movement or semantic
changes are explicit migration operations and never occur as a side effect of
a code deployment.

Preview fixtures and seed declarations target these logical resource names.
Small deterministic seed data may be declared alongside `forgeBuild.ts`, while
larger SQL, structured data, and object fixtures may be referenced as immutable
repository files. The normalized seed and every referenced blob contribute to
the deployment digest. Provider-specific IDs and seed-application receipts live
only in the compiled plan and deployment evidence.

Provider-specific behavior remains available through an explicit namespaced
escape hatch such as `provider.cloudflare`. Forge preserves it rather than
silently reducing it to a lowest-common-denominator abstraction, but marks the
affected capability and deployment as non-portable. The UI, API, and deployment
receipt must identify which requirements prevent another adapter from
satisfying the plan.

Sites builds normally receive a verified closure of the exact Git commit
directly from Forge's object store. The runner streams those loose objects into
the sandbox and materializes a detached checkout locally; it does not clone
history or negotiate with the Forge HTTP Git service. Explicit size/depth
ineligibility may use exact-SHA Git transport, while missing or corrupt source
objects are authoritative storage failures. Decompressed objects share a
64 MiB in-flight budget and are released after verification; the retained
compressed closure is capped at 256 MiB.

Connected-provider compatibility builds follow the same credential boundary.
The sandbox produces ordinary compiled output. A trusted adapter freezes the
one active provider base version, validates its binding identities, uploads
assets and a new immutable version with no traffic deployment, and records the
provider receipt. Provider artifacts are keyed before upload by their effective
module, assets, `_headers`/`_redirects` control files, settings, bindings
generation, target, and adapter version.
Identical later deployments reuse the verified immutable provider version.
OpenNext can do so before dependency installation when a versioned prebuild key
matches the exact source, project, normalized manifest, Wrangler and lockfile
digests, pinned adapters, runner image and toolchain, provider target/base and
bindings, policy, and entitlement bounds. Forge then verifies the retained
provider version and ready ISR evidence before attaching it to a distinct new
release. Legacy evidence and incomplete keys fail closed to a normal build.
For Cloudflare-compatible applications, Forge uses its pinned,
credential-free Wrangler compiler to preserve ESM and compiled WebAssembly
module semantics; Wrangler never receives a deployment credential or activates
traffic.
WebAssembly modules emitted by compatible builds are uploaded beside the ESM
entrypoint and contribute to the same artifact identity. Trusted ingest counts
streamed binary bytes before allocation and enforces the project entitlement,
the provider's 25 MiB per-asset and file-count ceilings, 10 MiB per WebAssembly
module, and 25 MiB total WebAssembly. Compiled entrypoints at or above 8 MiB
use Cloudflare's raw-module multipart version format through a backpressured
provider request, so the trusted runner does not retain duplicate whole-module
transport buffers. For OpenNext, the sandbox validates the compiled
entrypoint's UTF-8, SHA-256, byte size, and compressed size without first
copying the module into the runner isolate. Forge retains only that evidence
while it verifies ISR seed and uploads static assets. Immediately before the
provider-version request, the sandbox rechecks the quiesced file's size and
SHA-256, then Forge streams that file directly into multipart without
collecting the entrypoint in Worker memory. A 30-second heartbeat retains the
provider mutation fence throughout the asset upload and slow version request.
The live script-version endpoint requires inherited bindings to name the
literal `latest` version. Forge therefore freezes the sole active production
version and its binding digest before publication, includes both in artifact
identity, uses strict provider inheritance, and rejects readback drift; the
repository cannot select the inherited generation.
Forge's temporary same-Worker ISR helper inherits the complete frozen binding
surface that the final application will also inherit, plus one ephemeral seed
token. Forge omits bindings the final version replaces itself, such as static
assets, version metadata, and declared variables. This keeps the provider's
`latest` inheritance surface compatible after the helper is unstaged and
deleted, without copying secret values into the build sandbox.
ISR objects are seeded in fixed batches of at most four entries and 32 MiB of
source bytes. Every object still crosses a lease fence before PUT and again
before provider HEAD verification. Forge drains the scheduled batch before
helper cleanup, but starts no later batch after a failure. Entry evidence and
the operation's aggregate object/byte totals are committed in one
lease-guarded D1 batch, so a retry can safely skip verified objects. For
small, similarly sized entries, this changes the provider critical path from
roughly two round trips per object to two round trips per four-object batch;
total provider work and artifact identity are unchanged.

Static-asset upload follows the signed capability in Cloudflare's upload
session. Current sessions use raw per-hash request bodies with concurrency 3;
Forge checks the frozen byte count while streaming each body. Older sessions
fall back to the provider-assigned base64 multipart buckets. Repository code
cannot choose the capability, upload token, or concurrency.

Workers with Durable Objects do not receive version-specific preview hostnames.
For Forge's own API, the Sites edge reaches the 0% candidate through a private
service binding and supplies Cloudflare's version-override header. A normal
`workers.dev` origin without an explicitly configured binding fails closed
rather than silently serving the 100% production version.

Connected activation uses the candidate's frozen provider base as its
compare-and-swap fence. This lets an administrator use Wrangler for emergency
recovery, then build a new Forge candidate against that observed provider
version and safely return the normal release pointer to Forge control.

## Three versioned contracts

### Author configuration v1 and `forge-manifest/v1alpha1`

The repository exports author configuration `version: 1` from
`forgeBuild.ts`. Strict static evaluation normalizes it into the private
`forge-manifest/v1alpha1` provider-facing representation. The Git blob SHA,
source digest, normalized source, overrides, and effective representation are
stored with the deployment.

The manifest describes one application, its HTTP/assets components, routes,
build inputs, egress request, and lower limits. Managed durable state and
realtime are built-in application semantics. Provider placement is trusted
policy in `v1alpha1`; Worker scripts, Durable Object classes, namespaces,
service bindings, and provider resources exist only in the compiled plan.

### `forge-plan/v1alpha1`

The trusted compiler output for one release applied to one environment. It
snapshots:

- exact source SHA;
- normalized manifest and digest;
- release-compiler, plan-compiler, and policy versions;
- target environment and resource-instance scope;
- selected provider adapter and version for each application component;
- required artifacts;
- effective routes, limits, egress, access, and promotion rules;
- internal state/realtime backing operations;
- unsupported-capability diagnostics.

The plan is immutable. A change in environment policy, feature entitlement,
plan compiler, or material adapter behavior creates a new plan and environment
deployment. A new source, manifest, release compiler, build input, or artifact
policy creates a new release.

### Typed artifact contracts

There is no universal artifact that accurately represents every provider:

| Component | Artifact contract |
| --- | --- |
| `application.assets` | trusted file inventory, digests, MIME metadata, immutable asset prefix |
| `application.http` | Forge application bundle, route metadata, and runtime contract |
| `application.state` | versioned managed-state facade requirements; no repository executable |
| `application.realtime` | versioned managed subscription protocol and limits; no repository socket handler |
| `service` | OCI image or provider-compatible persistent-service artifact |
| `job` | executable bundle plus model, data, and output references |

The existing Sites `manifest_json` is the generated static file inventory. It
is not the repository-authored Forge manifest and should be named accordingly
in generalized APIs and UI.

## Control-plane data model

The first migration generalizes Sites additively. Existing tables remain
available through the compatibility surface.

### `deploy_projects`

One deployable project rooted in a repository.

Important fields:

- opaque ID, tenant ID, and future cell ID;
- repository tombstone ID;
- stable slug, root directory, and manifest path;
- enabled, disabled, or suspended state and reason;
- created and updated actors and timestamps.

The initial UI exposes one default project per repository. The schema must not
make repository ID unique so monorepos can expose multiple projects later.
Repository deletion disables the project without releasing its historical
hostname or deployment records.

### `deploy_environments`

An addressable environment and its trusted policy.

Important fields:

- project ID, name, and kind such as preview, staging, or production;
- branch or ref selection;
- publication and protection policy;
- preview access and egress policy references;
- active production deployment pointer where applicable;
- monotonically increasing desired generation;
- state and timestamps.

Security-sensitive policy is control-plane state. A repository manifest may
request capabilities, but it cannot widen an environment's permission,
visibility, quota, or egress ceiling.

### `deploy_releases`

One immutable, environment-neutral source and artifact release.

Important fields:

- project;
- exact source SHA and source ref;
- trigger, actor, and idempotency key;
- raw manifest snapshot and normalized manifest digest;
- manifest schema, release-compiler, and artifact-policy versions;
- build orchestration workflow and artifact-set references;
- release status and diagnostics;
- created, started, artifact-ready, failed, and cancelled timestamps.

A release state is `queued`, `planning`, `building`, `ready`, `failed`, or
`cancelled`. A ready release remains ready even when one of its environment
deployments is activated, rolled back, superseded, or expired.

### `release_units`

One immutable `application`, future `service`, or future `job` definition and
its repository artifacts in a release.

Important fields:

- logical name, kind, and runtime;
- artifact kind, location, and digest;
- normalized routes and component requirements;
- bounded artifact metadata and compiler diagnostics.

All declared application components are required in `v1alpha1`. Optional-unit
semantics are deferred.

### `release_capabilities`

One immutable application capability captured with a release:

- release/application unit ID and `http`, `assets`, `state`, or `realtime`
  kind;
- normalized portable configuration and compatibility digest;
- effective lower-limit requests and facade/protocol version;
- compiler diagnostics.

Built-in state/realtime capability records do not expose provider resources.
Preview application never changes production state lifecycle. Only production
activation or a separate authorized state operation advances production
desired state.

Assets-only applications have no state or realtime release capability.
Realtime-disabled entrypoint applications record the trusted denial policy but
do not materialize a realtime deployment component.

### `release_operations`

An append-only compilation, build, or ingest attempt:

- release target and operation kind;
- attempt number, workflow reference, and idempotency key;
- release-compiler, runner, image, and artifact-policy versions;
- status, logs reference, bounded diagnostics, and timestamps.

Retries append attempts rather than overwriting earlier evidence.

### `deployments`

One release applied to one environment and resource-instance scope.

Important fields:

- release ID and environment ID;
- immutable `forge-plan/v1alpha1` snapshot and digest;
- environment-policy and desired-generation snapshot;
- preview deployment discriminator where applicable;
- status and aggregate diagnostics;
- created, applying, ready, failed, cancelled, and expiry timestamps.

Deployment states are `queued`, `applying`, `verifying`, `ready`, `failed`,
`cancelled`, or `expired`. Production activation is separate history; it does
not mutate a ready deployment into a different kind of object.

### `deployment_components`

One application capability materialized in an environment deployment.

Important fields:

- deployment, release-unit, and release-capability IDs;
- `http`, `assets`, `state`, or `realtime` component kind;
- provider adapter key and version;
- provider unit, version, endpoint, or internal resource reference;
- desired, observed, health, and readiness state;
- bounded outputs, diagnostics, and error snapshots.

Multiple components may map to one provider unit, and one application may
compile into several provider units. This explicit mapping lets Forge fuse a
logical scope's state and realtime on the same Durable Object shard or split
them with a managed outbox without changing the repository manifest.

The initial Cloudflare adapter fuses them into a fixed shard pool: 16 shards
for production per project and four for each preview deployment. A trusted
hash maps every logical scope, its state rows, topic sequences, short-lived
outbox, and hibernating sockets to the same shard. The recorded shard count
does not change after the first retained mutation without a separate migration.

Component applicability is explicit. Shared state/realtime gateway readiness
does not imply a per-site Durable Object, database, topic, or socket exists.

### `deployment_operations`

An append-only environment-plan or provider attempt:

- operation such as plan, apply, verify, activate, suspend, or destroy;
- component or internal resource target;
- attempt number and idempotency key;
- desired generation, plan digest, and operation lease/fencing token;
- provider request/reference;
- status, logs reference, errors, and timestamps.

Retries append attempts. They do not overwrite previous evidence.
Immediately before each external mutation, the adapter rechecks the current
generation, plan digest, project eligibility, disablement, and suspension.
Delayed work with a stale fence cannot mutate provider state.

### `deploy_resources`

A stable internal state-backing identity independent of any one release:

- project, stable managed-state capability, and internal resource kind;
- lifecycle and schema-compatibility policy;
- active production runtime/facade version where applicable;
- retained, orphaned, suspended, or destruction-pending state and timestamps.

Release-specific application semantics live in `release_capabilities`.
Applying a preview never changes stable production state. Activating code that
no longer accesses a scope never destroys or orphans that scope; lifecycle
changes require an explicit authorized state operation.

### `deploy_resource_instances`

One physical provider resource bound to a trusted scope:

- logical resource ID;
- production or staging environment ID, or preview deployment ID;
- provider adapter and external reference;
- stable scope key;
- desired and observed state;
- binding and schema metadata;
- retention and deletion policy;
- timestamps and error state.

These records describe trusted gateways and provider namespaces, not one row
per application state scope or live socket. A release that stops accessing
state never implies deletion. Initial dynamic Deploy uses create/update-only
reconciliation. Destroy requires an explicit, separately authorized action.

### `deployment_endpoints`

Zero or more trusted provider or Forge endpoints:

- deployment and optional application component;
- endpoint kind, URL or route, and access policy;
- readiness, activation, retirement, and timestamps.

URLs are nullable and plural. Clients never construct provider or production
URLs from IDs.

Endpoint expiry never deletes artifacts or resource instances referenced by an
active or rollback-pinned production deployment.

### `deployment_realtime_generations`

One managed-realtime generation for an application deployment:

- deployment and realtime component;
- environment access epoch and opaque generation;
- accepting, draining, closed, or failed state;
- reserved endpoint and protocol version;
- effective connection, event, fanout, byte, duration, and spend limits;
- active/closed connection aggregates and provider-operation references;
- deterministic shard count plus drain/suspension acknowledgment summary;
- created, drain-started, closed, and expiry timestamps.

Individual live sockets remain ephemeral provider state rather than durable
control-plane rows. Tickets, cookies, channel names, and message bodies are not
stored in deployment records.

### `environment_activations`

Append-only production activation history:

- environment, desired generation, from-deployment, and to-deployment;
- actor, reason, approval, and idempotency key;
- activation and compensation operation references;
- requested, applying, active, failed, or compensated state;
- timestamps and error state.

The current environment pointer is a compare-and-set projection of successful
activation history. Rollback creates a new activation targeting a prior ready
deployment; it never rewrites a historical activation as rolled back.

### `hostname_claims`

One normalized authority for production names, preview names, aliases, and
tombstones:

- registrable domain and normalized hostname;
- claim kind and owner project or deployment;
- active, redirecting, retired, or tombstoned state;
- predecessor/successor claim where a domain migrates;
- created, retired, and retention timestamps.

Uniqueness is atomic across generalized Deploy projects and deployments.
Repository rename, deletion, project removal, and domain migration never make
a historical hostname immediately claimable by another tenant.

### Future job executions

A deployed `job` unit describes an executable version. Each invocation needs a
separate record containing input reference, status, provider run ID, cost,
logs, outputs, cancellation, and timestamps. A job run is not an environment
deployment.

## Provider adapter model

Adapters run only in trusted control-plane services. Rather than forcing every
provider through one identical lifecycle, Forge defines capability-oriented
adapter families:

- application HTTP/assets runtime;
- managed durable state;
- managed realtime;
- persistent service;
- asynchronous job or GPU;

Common operations are:

1. discover supported capabilities;
2. validate and plan;
3. provision or resolve resources;
4. publish a version;
5. inspect readiness and health;
6. activate or route;
7. retrieve logs and diagnostics;
8. suspend;
9. reconcile observed state;
10. destroy only with explicit authority.

Every adapter requires conformance tests for idempotency, stale events,
retries, cancellation, suspension, credential isolation, quota enforcement,
and partial failure.

### Cloudflare Workers for Platforms

The first adapter supports:

- one Forge application with optional HTTP and asset components;
- dispatch-namespace user-code isolation;
- lazy, serializable state scopes backed by Forge-owned SQLite Durable Object
  classes;
- Forge-owned hibernating realtime connections in the same fixed scope-shard
  pool, with strict quotas and polling fallback;
- per-project and per-environment limits;
- trusted outbound policy enforcement;
- version publication and Forge-controlled routing.

Repository code does not run inside the state or realtime Durable Objects. The
stable Forge gateway exposes serializable operations, atomic state-plus-event
intent commit, and state-emitted browser subscriptions without allowing user
code to bypass the outbound Worker. Live fan-out is best effort and can be
suppressed without failing the state mutation. Cloudflare documents that
Outbound Workers do not intercept `fetch()` from Durable Objects, so
repository-authored classes, actor-originated egress, and outbound sockets
remain deferred.

This provider cost model supports hiding the topology. Workers for Platforms
allows unlimited scripts and prices scripts cheaply enough that an immutable
script per application release can remain an internal lifecycle detail.
Inactive Durable Objects and hibernating WebSocket connections do not accrue
duration charges. They are not free, however: requests, active execution,
storage, connection establishment, and inbound WebSocket messages remain
metered. Forge therefore makes durable scopes lazy and ordinary by default,
while realtime receives stricter connection, fan-out, byte, and lifetime
budgets. Provider prices are inputs to Forge policy, not part of the portable
application contract.

The proposed provider topology is:

- one preview/staging dispatch namespace and one production dispatch namespace,
  not a namespace per customer;
- opaque, deployment-specific user Worker script names so published code is
  immutable;
- a versioned trusted wrapper around each user bundle so raw Cloudflare
  bindings remain in trusted lexical scope and repository code receives only
  Forge runtime facades;
- Forge dispatch routing selects the recorded preview or active production
  script;
- preview and production resolve different managed state scope identities;
- user Workers receive scoped state and subscription-grant facades, never a raw
  Durable Object namespace;
- state storage is retained only after successful mutation; scopes hash over
  16 production or four preview shards, and cold names are bounded before a
  provider call;
- `/.forge/realtime` routes to a stable Forge-owned hibernating gateway;
- application HTTP code issues a single-use subscription grant bound to one
  scope and topic; the edge validates exact origin, deployment generation, and
  preview access epoch;
- sockets and conditional polling share one realtime budget; quota exhaustion
  refuses both transports without disabling authoritative state or ordinary
  HTTP;
- promotion uploads the same release bundle into the production namespace with
  production state policy, verifies it, then changes Forge routing;
- rollback selects an already published production script when compatible and
  never rolls back mutable state;
- activation and rollback increment realtime generation, drain old sockets,
  and require a fresh snapshot after reconnect;
- suspension wakes the bounded shard set, requires acknowledgments, and makes
  sockets inert through a fail-closed state/event gate even if physical close
  is delayed;
- active and rollback-pinned scripts and artifacts are exempt from preview
  retention cleanup.
- connected-account cleanup deletes only the exact Forge-published Worker
  Version after sharing the Worker activation lock and rechecking that the
  provider's current deployment does not reference it; provider resources and
  Forge deployment/artifact evidence remain intact.
- Cloudflare currently labels its per-version deletion endpoint
  [beta](https://developers.cloudflare.com/api/resources/workers/subresources/beta/subresources/workers/subresources/versions/methods/delete/).
  Provider errors therefore leave the version and receipts intact and return
  the cleanup operation to the bounded retry path.

This topology must be proven with an adapter prototype and binding-isolation
tests before the application/state/realtime contracts move from proposal to
approved.

The portable portion of the manifest does not expose customer Cloudflare
credentials, account IDs, raw Wrangler configuration, or arbitrary bindings.
The current `provider.cloudflare` compatibility extension may reference
checked-in Wrangler configuration, but that extension is explicitly outside
the portability guarantee and never exposes provider credentials to repository
code.

References:

- [Workers for Platforms](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/)
- [Workers for Platforms pricing](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/reference/pricing/)
- [Workers for Platforms limits](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/reference/limits/)
- [Workers for Platforms bindings](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/configuration/bindings/)
- [Dynamic dispatch](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/configuration/dynamic-dispatch/)
- [Custom limits](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/configuration/custom-limits/)
- [Outbound Workers](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/configuration/outbound-workers/)
- [Durable Object WebSocket hibernation](https://developers.cloudflare.com/durable-objects/best-practices/websockets/)
- [Durable Object pricing](https://developers.cloudflare.com/durable-objects/platform/pricing/)

### Railway

The future Railway adapter targets persistent container services:

- service creation from an OCI image or supported source artifact;
- health checks and readiness;
- provider endpoint discovery;
- restart, scaling, deployment history, and logs;
- explicit compensation where activation cannot be represented as a Forge
  pointer change.

References:

- [Railway services](https://docs.railway.com/services)
- [Railway deployments](https://docs.railway.com/deployments/reference)
- [Railway public API](https://docs.railway.com/integrations/api)

### Modal

The future Modal adapter targets asynchronous jobs, sandboxes, and GPU work:

- versioned job definitions;
- explicit CPU, memory, GPU, duration, and concurrency requests;
- asynchronous invocation, cancellation, outputs, and logs;
- sandbox execution for suitable isolated workloads.

It does not assume every unit has a persistent URL. Modal's JavaScript SDK can
orchestrate Sandboxes and invoke deployed Functions, while Function definition
remains Python-oriented; that is one reason Modal is not the first application
runtime adapter.

References:

- [Modal Sandboxes](https://modal.com/docs/guide/sandboxes)
- [Modal Web Functions](https://modal.com/docs/guide/webhooks)
- [Modal JavaScript SDK](https://modal.com/docs/sdk/js/latest)

## Placement

Placement is implicitly automatic in `v1alpha1` and cannot be changed by the
repository manifest. The compiler chooses an adapter that satisfies the
application's required capabilities and trusted project policy.

A future manifest version may introduce a unit-level explicit provider request
behind a feature flag. It remains subject to entitlement and compatibility
validation. Provider-specific configuration lives under the existing
`provider.<name>` namespace, such as `provider.cloudflare`, and is excluded
from the portability guarantee.

The initial release contains only one dynamic provider, so `auto` resolves the
complete application HTTP/assets/state/realtime graph to Cloudflare. Provider
choice becomes meaningful only after another adapter implements the same
semantics.

## Deployment lifecycle

1. A push or authorized manual action identifies an exact SHA.
2. Forge reads `forgeBuild.ts` from that SHA.
3. The trusted release compiler validates and normalizes repository intent
   into a new release.
4. Restricted builds produce typed environment-neutral artifacts.
5. Trusted ingest validates and stores those artifacts.
6. Preview policy resolves effective access, egress, state/realtime ceilings,
   quotas, placement, and an immutable plan.
7. Forge creates a preview deployment with deployment-scoped state identity
   and realtime generation.
8. Adapters publish applicable HTTP/assets components and attach lazy
   state/realtime gateways only when enabled.
9. Reconciliation verifies every required application component.
10. Forge exposes the immutable preview and reserved realtime endpoint.
11. Publication rechecks branch head, release and manifest digests, required
    checks, authorization, eligibility, desired generation, and suspension.
12. Forge compiles a distinct production plan for the same release artifacts.
13. Adapters publish production components against stable production state
    and initialize a new realtime generation.
14. After production readiness, Forge records an activation and moves the
    environment pointer with compare-and-set semantics.
15. Activation drains prior realtime sockets; suspension blocks routing and
    sockets, increments desired/access generations, invalidates operation
    fences, and starts reconciliation.
16. Retention cleanup removes expired endpoints and unpinned artifacts.
    Durable resource lifecycle is handled separately.

## Promotion and rollback

For current static Sites and Forge applications, production is an environment
pointer to one ready production deployment. Promotion creates or reuses a
production deployment referencing the same immutable release artifacts; it
does not rebuild. Preview state and realtime sessions are not promoted.

Rollback selects a prior ready production deployment or reapplies a prior
release to current production state. It is code and route rollback, not data
rollback. Stateful projects must acknowledge this distinction. Rollback
increments realtime generation, drains old connections, and makes clients
refetch authoritative state. Custom state handlers or schemas require a
separate compatibility and migration design.

A future multi-provider release may require provider activation operations.
Forge must track:

- the desired production deployment and generation;
- readiness of each required unit;
- which activation operations completed;
- compensation required after partial activation;
- the last known healthy production deployment.

The product must not promise instant pointer-only rollback for a Railway
service, Modal job definition, database migration, or any provider resource
whose old version cannot be reactivated atomically.

## Schema authority

Forge is pre-production. The generalized project, release, deployment,
activation, and hostname records are the only Deploy authority. There is no
Sites shadow projection, dual-write period, upgrade migration, or rollback to
an older schema. Schema changes replace the canonical fresh-install fragments
and require recreating the pre-production D1 database after preserving Time
Travel recovery evidence.

Every deployment requires `forgeBuild.ts`; unsupported YAML configuration and
missing configuration fail visibly before a build starts. Source configuration
is validated before overrides, and the effective configuration is validated
and frozen again before workflow creation.

Split today's single `policy_version` into manifest-schema, release-compiler,
plan-compiler, deployment-plan, adapter, and artifact-policy versions.

## Relationship to Runs and Tasks

Runs executes CI and build work. Deploy consumes exact-SHA build results but
owns environment policy, provider application, preview, promotion, and
rollback.

Tasks creates agent attempts and branches. Devin, Codex, or a future native
agent is a replaceable Task provider. Agents receive short-lived,
branch-scoped capabilities and do not receive production authority.

Once Deploy is stable, the golden loop becomes:

> Task -> agent branch -> draft pull request -> Runs checks -> Deploy preview ->
> feedback and repair -> merge -> policy-gated production -> rollback and
> audit.

Tasks integration is not a prerequisite for making Deploy useful.

## Rollout

### Phase 0: pre-production Sites foundation

- [x] Use `*.sites.smol.ai` only as the documented pre-production hostname.
- [x] Keep projects and accounts explicitly Alpha-enrolled; require platform review
  before anonymous public access on the shared registrable domain.
- [x] Add and test the pre-production `noindex` response policy and shared-domain
  safeguards before routing user content.
- [x] Verify static push, exact-SHA build, immutable preview, promotion, rollback,
  disable, visibility change, and suspension.
- [x] Add queue, artifact, bandwidth, and cost telemetry.
- [x] Keep the existing static behavior unchanged.

Exit met on 2026-07-24: the Sites slice is proven on its owning pre-production
surfaces without claiming production isolation. A public-to-unlisted source
transition stopped both production and preview serving immediately; restoring
the source to public did not silently re-enable hosting.

### Phase 1: generalized control plane and trust

- [x] Lock repository-schema, canonical-manifest, release-artifact, application
  runtime, state, realtime, and environment-plan contracts.
- [x] Add project, environment, release, release-unit, deployment,
  release-capability, release-operation, deployment-component,
  deployment-operation, internal-resource, resource-instance, endpoint,
  realtime-generation, activation, and hostname-claim records.
- [x] Add centralized authorization, transactional outbox, idempotency, audit, and
  environment promotion rules.
- [x] Add desired-generation fencing and operation leases for provider mutations.
- [x] Add Alpha-membership eligibility and current Terms acceptance.
- [x] Validate generalized contracts as the sole Deploy authority.

Forge is pre-production and now has one generalized Deploy authority. The
current ownership and retry invariants are documented in the
[delivery reconciliation contract](delivery-reconciliation.md); no shadow
projection or compatibility authority exists.

### Phase 2: constrained dynamic Deploy

- [x] Implement the Workers for Platforms adapter and its conformance suite.
- [x] Add one Forge application with optional HTTP and assets.
- [x] Make managed serializable state available by default through lazy
  SQLite-backed Durable Object scopes.
- [x] Add bounded state-emitted managed realtime with mandatory hibernation,
  generation drain, and conditional polling fallback.
- [x] Add preview isolation, logs, state/realtime quotas, egress enforcement,
  suspension, and cleanup.
- [x] Test stale plans, partial failure, retries, and rollback.
- [x] Permit default `auto` publication only for production branches meeting the
  mandatory branch-protection and check baseline.

Exit met on 2026-07-24: an eligible repository administrator can enable a
public repository, deploy a
constrained dynamic app, inspect its immutable preview, and publish or roll it
back under recorded policy inside the pre-production allowlist. Live
conformance proved one winner under ten concurrent durable claims, identical
production state before/after code rollback, isolated preview state, two-tab
event delivery, a terminal refusal for the 21st preview connection, and
artifact deletion with retained lifecycle history.

### Phase 3: production domain graduation

- Move all user content to a separate Forge-owned registrable domain.
- Pass the origin, cookie, CORS, egress, abuse, authorization, suspension, and
  migration gates in the security contract.
- Enable general repository-admin self-service and anonymous public hosting
  only after the domain cutover.

Exit: the platform domain is production-isolated and general self-service no
longer depends on a shared-domain exception.

### Phase 4: Tasks golden loop

- Add branch-scoped agent capabilities and Task provider abstractions.
- [x] Connect pull-request checks and dynamic previews.
- Let an agent inspect diagnostics and repair its branch.
- Preserve human or environment approval where policy requires it.

Pull-request previews shipped on 2026-07-29 for same-repository branches.
Forge freezes the exact head SHA into a durable, generation-scoped intent,
coalesces duplicate ref events, builds through the ordinary Deploy pipeline,
and moves the stable pull-request alias only when the completed deployment is
still current. Closing or merging the pull request revokes the alias
immediately. Fork builds, preview secrets, and production promotion remain
separate trust decisions and are not implied by this milestone.

Exit: one fixture completes Task -> pull request -> preview -> repair -> merge
-> production -> rollback with exact-SHA and audit evidence.

### Phase 5: provider and platform depth

- Add Railway persistent services.
- Add Modal jobs and GPU execution.
- Add multiple projects per repository and staging environments.
- Add framework adapters, secrets, custom domains, and remote build cache only
  behind their own reviewed contracts.

Exit: each new adapter passes conformance and a mixed-provider release has
documented partial-failure and compensation behavior.

## Initial exclusions

- arbitrary customer Workers or provider credentials;
- repository-authored Durable Object classes;
- unrestricted build or runtime egress;
- private or unlisted source builds;
- runtime and build secrets;
- general Node, Next.js, Vercel, or server-process compatibility;
- raw WebSocket handlers, client-publish sockets, presence, replay, scheduled
  handlers, queues, email handlers, and arbitrary background triggers;
- custom domains;
- implicit destruction of durable resources;
- bidirectional GitHub synchronization;
- multiple dynamic providers in one initial release.
