Status: Phases 0–2 shipped in the access-controlled Alpha on
sites.smol.ai on 2026-07-24. Phases 3–5 remain proposed.
| 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.
Forge Deploy turns an exact SmolForge commit plus a versioned Forge manifest into an immutable, inspectable release.
SmolForge remains authoritative for:
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.
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.
Keep a modular control-plane monolith while physically separating the boundaries where untrusted code or privileged credentials make isolation material:
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.
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.
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:
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.
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:
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.
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.
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:
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:
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:
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:
All declared application components are required in v1alpha1. Optional-unit
semantics are deferred.
release_capabilities#One immutable application capability captured with a release:
http, assets, state, or realtime
kind;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:
Retries append attempts rather than overwriting earlier evidence.
deployments#One release applied to one environment and resource-instance scope.
Important fields:
forge-plan/v1alpha1 snapshot and digest;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:
http, assets, state, or realtime component kind;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:
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:
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:
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:
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:
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:
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:
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.
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.
Adapters run only in trusted control-plane services. Rather than forcing every provider through one identical lifecycle, Forge defines capability-oriented adapter families:
Common operations are:
Every adapter requires conformance tests for idempotency, stale events, retries, cancellation, suspension, credential isolation, quota enforcement, and partial failure.
The first adapter supports:
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:
/.forge/realtime routes to a stable Forge-owned hibernating gateway;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:
The future Railway adapter targets persistent container services:
References:
The future Modal adapter targets asynchronous jobs, sandboxes, and GPU work:
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:
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.
forgeBuild.ts from that SHA.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 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.
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.
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.
*.sites.smol.ai only as the documented pre-production hostname.noindex response policy and shared-domain
safeguards before routing user content.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.
Forge is pre-production and now has one generalized Deploy authority. The current ownership and retry invariants are documented in the delivery reconciliation contract; no shadow projection or compatibility authority exists.
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.
Exit: the platform domain is production-isolated and general self-service no longer depends on a shared-domain exception.
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.
Exit: each new adapter passes conformance and a mixed-provider release has documented partial-failure and compensation behavior.