# Forge Deploy product and operations

Status: proposed behavior for review.

This document describes what repository administrators, contributors, agents,
and operators experience. It intentionally separates requested configuration,
successful build output, provider readiness, preview availability, and
production publication.

## Initial eligibility

A repository administrator may enable the default Deploy project when all of
the following are true:

- during the `sites.smol.ai` Alpha period, the owning account and project have
  Alpha access;
- the repository is public, active, and not deleted or suspended;
- the user and owning account are eligible for hosting;
- the current hosting Terms of Use are accepted;
- the requested hostname is valid, available, and not reserved;
- the manifest and requested capabilities pass validation;
- required abuse, quota, and billing checks are available;
- initial feature entitlements cover all requested capabilities.

Enablement records the actor, repository, project, terms version,
configuration digest, policy-check results, feature entitlements, quotas, and
timestamp.

The first UI exposes one default Deploy project per repository. The underlying
model supports additional projects later without changing deployment identity.
Self-service is limited to Alpha accounts until hosted content moves to a
separate registrable domain.

The claimed hostname remains reserved to its original project through
disablement, suspension, repository deletion, or Alpha revocation. Reservation
does not guarantee continued serving or content retention: Forge may suspend
the hostname and remove retained Alpha artifacts.

## Repository Sites area

The current repository-level Sites area evolves into Deploy while retaining a
Sites label for the static compatibility path.

The project overview shows:

- project hostname and state;
- source root, manifest path, and production branch;
- publication policy and required checks;
- preview access mode and retention;
- effective build and runtime egress policy;
- application artifact and observed HTTP, asset, state, and realtime health;
- current production deployment;
- recent deployments grouped by source SHA;
- per-component build, publish, readiness, and error state;
- logs, usage, quotas, and audit history;
- controls to redeploy, promote, roll back, disable, or report a problem.

Provider IDs may appear in an advanced diagnostic view. They do not replace
Forge status or become part of stable product URLs.

## Configuration ownership

Configuration is split deliberately:

### Repository-owned intent

`forgeBuild.ts` defines what the application needs:

- build command and one application artifact;
- application entrypoint, static assets, and routes;
- requested lower resource limits;
- requested egress requirements.

The forward resource contract uses stable, repository-chosen logical names and
behavioral requirements rather than provider product names. It permits multiple
resources of the same semantic kind. This is a design goal rather than current
v1 syntax: the shipped v1 contract still exposes one application with built-in
managed state and realtime.

Provider-specific configuration is allowed only under an explicit
`provider.<name>` namespace. The Deploy UI and deployment receipt identify the
affected capabilities as non-portable, show the selected provider resources in
advanced evidence, and never imply that a provider extension can be migrated
without semantic or data-movement work.

### Trusted project and environment policy

The Forge control plane defines what the application is allowed to do:

- repository eligibility and Terms acceptance;
- production branch and required checks;
- automatic or reviewed publication;
- preview visibility;
- egress ceiling and broad-public feature entitlement;
- provider availability and any future explicit-placement entitlement;
- durable-state and realtime quotas, retention, billing, and suspension;
- resource deletion authority.

The effective plan is the intersection of repository intent and trusted policy.
A commit cannot grant itself broader egress, public preview access, production
authority, higher quotas, provider credentials, or resource deletion rights.

## Publication policy

Each production environment has one policy:

| Policy | Behavior |
| --- | --- |
| `auto` | Publish an eligible ready deployment automatically. This is the default only when the production branch satisfies the protection and check baseline. |
| `owner_review` | Require an authorized repository administrator to promote. |
| `environment_review` | Require an actor named by the protected environment policy. |

Even `auto` requires:

- exact source SHA from the configured production branch;
- a protected production branch that agents and untrusted service identities
  cannot write directly;
- the mandatory manifest, build, trusted-ingest, policy, provider-readiness,
  preview-smoke, and security checks;
- source branch head, project configuration, and plan digest still current;
- all required checks passing;
- all required application components ready;
- repository, project, account, and billing eligibility still valid;
- no active suspension or takedown;
- during the shared-domain period, current project/account Alpha access;
- a compare-and-set update so a stale deployment cannot replace a newer
  production release.

An agent has no separate production authority. An agent may push an authorized
task branch and open or update a pull request. Production follows the normal
merge, check, and environment policy.

For an `auto` environment, anyone authorized to merge or directly update the
protected production branch is deliberately a production deployer. If the
branch or check baseline does not meet this contract, Forge changes the
effective policy to `owner_review`; it does not silently retain `auto`.

Because application code can mutate retained production state and emit to
connected realtime clients, the project UI must make this delegated production
authority explicit when `auto` is selected. Rollback changes code and routes;
it does not restore durable state or already delivered events.

On `sites.smol.ai`, `auto` may replace the active deployment for an enrolled
project after exact-SHA, build, ingest, runtime, eligibility, and
compare-and-set checks pass. Removing the project or account from the trial
stops all of its routes.

## Trigger behavior

Initial triggers are:

- a successful push to the configured production branch;
- an authorized manual deployment of an exact commit;
- rebuilding an exact commit under a newly selected release-compiler or
  artifact-policy version;
- reapplying an existing ready release under a new environment policy, plan
  compiler, or provider-adapter version.

A source, manifest, release-compiler, build input, or artifact-policy identity
change creates an environment-neutral release and builds its artifacts once.
An environment-policy, plan-compiler, or provider-adapter change creates a new
deployment from an existing ready release when its artifacts remain
compatible; it does not rebuild merely to refresh provider state.

Forge applies each eligible ready release as an immutable preview deployment.
Publication creates or reuses a distinct production deployment from the same
release artifacts and binds the stable production state scope.

Automatic branch and pull-request previews should use the same durable
ref-change event path as Git receive, API-created commits, merges, and future
imports. They are added only after that unified event and outbox boundary
exists. Agents can still create previews through ordinary authorized pushes
and manual exact-SHA deployment during the initial dynamic release.

## Release, deployment, and activation states

The UI presents one pipeline while preserving four separate state machines.

### Release

| State | Meaning |
| --- | --- |
| `queued` | Exact SHA accepted and waiting for compilation or capacity. |
| `planning` | Repository intent and the build graph are being normalized. |
| `building` | Immutable environment-neutral artifacts are being produced. |
| `ready` | All required release artifacts passed trusted ingest. |
| `failed` | Compilation, build, or ingest failed. |
| `cancelled` | An authorized cancellation reached a terminal state. |

A ready release remains ready. Activation and rollback do not mutate release
readiness.

### Environment deployment

| State | Meaning |
| --- | --- |
| `queued` | A ready release is waiting to apply to an environment. |
| `applying` | Trusted adapters are provisioning the application's provider components. |
| `verifying` | Every applicable application component and shared gateway path is undergoing readiness checks. |
| `ready` | The release is usable in this environment and may be previewed or activated. |
| `failed` | A required application component or policy gate failed. |
| `cancelled` | The environment application was cancelled. |
| `expired` | A preview deployment and its unpinned endpoint reached retention. |

All applicable application components are required in the initial version.
Assets-only sites do not wait for HTTP, state, or realtime. Realtime-disabled
sites verify the denial policy instead of provisioning a transport.

### Activation

An environment activation is `requested`, `applying`, `active`, `failed`, or
`compensated`. `awaiting_promotion`, `live`, and `superseded` are UI
projections from publication policy and activation history rather than
deployment states.

### Endpoint

An endpoint is `pending`, `active`, `revoked`, `retired`, or `expired`.
Endpoint expiry does not delete release artifacts or provider versions pinned
by active or rollback-eligible production deployments.

The UI exposes release, application-component, resource-instance, operation,
activation, and endpoint evidence. Provider components remain an advanced
diagnostic. A failure shows which actions completed, whether cleanup is
pending, and whether durable state changed.

## Immutable previews

Each ready preview deployment receives a preview such as:

`https://preview-{128-bit-random}.sites.smol.ai`

Preview access is snapshotted per deployment:

| Mode | Behavior |
| --- | --- |
| `public_unlisted` | No authentication; high-entropy URL, no public enumeration, and `noindex`. |
| `repository` | Only authorized repository members. |
| `share_token` | Anyone with a scoped, revocable share link. |

During the trial, `public_unlisted` is the default. The other modes remain
reserved until their edge authentication and revocation paths are implemented.
An unlisted URL is never described or treated as authenticated.

Repository access means any current actor with repository read permission.
Membership removal, repository visibility changes, token revocation, or
suspension increments an access epoch checked by the edge on every request.

Repository-authenticated previews use an edge-owned flow:

1. Forge creates a short-lived, single-use code scoped to the actor,
   deployment, hostname, return path, and current access epoch.
2. The browser navigates to reserved
   `/.forge/access/bootstrap#code=...`; the code remains in the fragment and is
   not sent in an HTTP request or referrer.
3. Edge-owned bootstrap code posts it to `/.forge/access/exchange`. The
   exchange is consumed before any repository code runs and all code fields are
   redacted from logs.
4. The edge issues a signed, short-lived, `Secure`, `HttpOnly`, host-only
   `__Host-forge-access` cookie with `Path=/` and redirects to a clean return
   URL.
5. Every request validates deployment, hostname, expiry, access epoch, and
   current project eligibility. Before dispatch, the edge strips its access
   cookie and every edge-owned authentication header; repository code receives
   only application cookies and ordinary request headers.

Exact `/.forge` and the `/.forge/*` namespace are reserved and cannot be
claimed by application routes. Forge bearer tokens and control-plane
credentials are never sent to preview origins.

Share links use the same edge-owned bootstrap boundary:

1. The link contains the high-entropy share secret only in
   `/.forge/access/bootstrap#share=...`.
2. The self-contained bootstrap page loads no third-party resources, clears
   the fragment from browser history, and posts the secret to
   `/.forge/access/exchange` before repository code runs.
3. The exchange compares the stored hash and issues a short-lived `Secure`,
   `HttpOnly`, host-only `__Host-forge-access` cookie with `Path=/`; the raw
   secret and credential are excluded from request and platform logs.
4. Every request checks the deployment's share-access epoch. Revoking or
   replacing the share link increments the epoch immediately.

Share secrets are scoped to one deployment and hostname, replaceable without
rebuilding, and never placed in an ordinary query string or path.

Visibility changes, token revocation, repository visibility changes, deletion,
disablement, and suspension must take effect without waiting for an immutable
asset cache lifetime.

## Production routing and rollback

Production initially resolves:

`https://{project}.sites.smol.ai`

Production points to one ready application deployment. Promotion applies an
existing ready release to the stable production environment; rollback selects
or reapplies a previous release. Neither action rebuilds artifacts.

Rollback:

1. selects a previously ready production deployment or release;
2. verifies current repository and project eligibility;
3. verifies that its plan, compiled components, and retained state remain
   compatible;
4. performs required provider activation or compensation;
5. changes the active environment pointer with compare-and-set semantics;
6. records actor, reason, previous target, new target, and operation evidence.

The UI must distinguish an immediate pointer rollback from a provider
re-activation that is still reconciling.

Rollback changes application code, assets, and routes only. Durable state does
not roll back. Activation and rollback increment a realtime generation, stop
new connections to the old generation, allow a short drain, and then close
remaining sessions so clients reconnect and fetch a fresh snapshot. Stateful
projects require an explicit acknowledgment, and Forge blocks any rollback
that violates a declared application compatibility rule.

## Managed application capabilities

Repository authors deploy one application. They are not asked to choose
between a Worker, Durable Object, key-value store, or companion realtime
service. Forge exposes one portable runtime and compiles it into provider
components.

### Durable state

Every application entrypoint may use `env.forge.state`. State storage is
retained only after a successful mutation, so an application that never
writes provisions no per-project data. An assets-only application creates no
state usage.

The initial adapter uses Forge-owned SQLite-backed Durable Objects behind a
trusted gateway; repository code receives neither a raw namespace nor a custom
object class. Each project uses a fixed 16-shard production pool and each
preview uses four shards; a trusted hash maps caller-selected scopes into that
bounded pool.

Operations are strictly serializable within one caller-selected scope. There
are no cross-scope transactions. A mutation may atomically commit state
changes plus a versioned realtime event intent, which makes common
reservation, counter, room, and coordination workflows safe without
additional infrastructure. Live delivery remains best effort.

Production state belongs to the stable production environment and persists
across deployments. Every immutable preview receives deployment-isolated state
with a retention TTL. Preview data is never promoted into production, and
rollback never rewinds production data. Destruction is a separate authorized
operation with an impact summary and retention policy.

### Managed realtime

Every application entrypoint may use `env.forge.realtime`; the provider path
materializes only when a state mutation emits an event or a client subscribes.
The browser SDK subscribes through a reserved Forge endpoint. Repository code
does not handle raw socket upgrades, connection lifecycle, or provider
identifiers. Application code authorizes an ordinary HTTP request and directly
returns an opaque subscription grant scoped to one state scope and topic. Only
the trusted wrapper can turn that grant into ticket bytes, and the browser
cannot choose a different scope after authorization.

Realtime is a convenience view over authoritative durable state:

- the server emits from an atomic state mutation and browsers subscribe;
  browser-to-server messages are not supported initially;
- events have a contiguous sequence within one `(scope, topic)` and carry a
  state version;
- replay, presence, exactly-once delivery, and durable connection state are
  not promised;
- a client connects and buffers before fetching an atomic versioned snapshot,
  then refetches after a gap or reconnect;
- the SDK uses hibernating WebSockets when available and may fall back to
  foreground-only conditional polling, within the same budget, when sockets
  are technically unavailable.

Connections, fan-out, bytes, topics, and lifetimes have conservative default
ceilings. A repository may lower its limits. Raising them is an audited plan or
feature-entitlement change, not a manifest request that silently increases
spend. Realtime quota exhaustion stops realtime first and preserves normal
HTTP, assets, and durable-state operations. A state mutation still commits
when its event is suppressed; the sequence advances and affected clients are
forced to resynchronize.

Activation, rollback, preview expiry, disablement, access revocation, and
suspension invalidate affected sessions. Re-enable or unsuspend starts new
authenticated sessions; it never restores old sockets or tickets.
Suspension wakes the fixed shard pool, makes sessions inert through a
fail-closed state/event gate, and then closes hibernating sockets.

Custom Durable Object classes, alarms, raw WebSocket handlers, client
publishing, durable replay, and user-directed networking from managed state or
realtime components remain deferred. Cloudflare Outbound Workers do not
intercept Durable Object `fetch()` calls, so Forge does not run repository code
inside those components.

### Per-site configuration

Each Deploy project receives one conservative application profile. A
repository administrator can configure, per site:

- lower durable-state scope and storage ceilings;
- lower realtime connection, event-rate, fan-out, event-size, and daily-byte
  ceilings;
- realtime enabled or disabled;
- preview retention and preview realtime ceilings; and
- the runtime egress allowlist.

These settings describe outcomes and budgets, not provider products. The UI
does not ask whether to create a Worker, Durable Object, namespace, or
companion service. Repository `limits` may lower the trusted project settings.
Only an audited entitlement or plan change can raise them.

## Egress

Build and runtime egress have separate policies.

The default runtime mode is a per-project allowlist. The requested destinations
are versioned with the deployment plan, while the trusted control plane applies
the final ceiling.

A project may receive a `broad_public_egress` feature flag. This means
"arbitrary public Internet destinations through Forge's controlled outbound
layer," not raw unrestricted networking. The outbound layer still blocks
private and platform infrastructure, validates DNS and redirects, applies
ports and size limits, meters usage, records bounded audit evidence, and
supports immediate revocation.

All user-authored external requests originate in the application HTTP runtime
and pass through the provider outbound policy. Forge-managed state and
realtime components do not make user-directed external requests. Runtime code
cannot modify its own egress entitlement.

## Build behavior

The existing restricted runner remains the build foundation:

- detached checkout at the exact source SHA;
- public source only initially;
- no Forge session, repository secret, provider credential, account ID, or
  deployment token;
- build egress limited by a separate dependency-oriented allowlist;
- typed output accepted only from declared unit paths;
- existing trusted validation rejects traversal, symlinks, special files,
  observed size changes, unsupported formats, and quota violations;
- trusted ingest stores immutable artifacts and manifests last.

Before dynamic artifacts are accepted, trusted ingest additionally requires
file-identity and link-count checks, digest verification after the sandbox is
quiescent, and tests for hard links and same-size mutation.

Every build network path applies the same private, loopback, link-local,
metadata, internal-service, unsafe-port, DNS-at-connect, redirect,
DNS-rebinding, credential, size, duration, and quota protections as runtime
egress. The permitted public dependency registries form a separate build
allowlist; the project's runtime egress setting never widens it.

Dynamic build output may include one Forge application bundle. Repository
deployment commands and provider CLI credentials remain prohibited.

## Logs and diagnostics

Users can inspect:

- manifest and compiler diagnostics;
- build logs;
- trusted-ingest results;
- provider publication and readiness summaries;
- runtime logs subject to plan and retention limits;
- egress denials and quota events;
- durable-state contention and limit events;
- realtime connection, drain, fallback, and circuit-breaker summaries;
- promotion, rollback, suspension, and cleanup operations.

Large logs and artifacts live in object storage. Control-plane records hold
bounded summaries, indexes, digests, and references.

Logs must be attributable to tenant, project, environment, deployment,
application component, operation, and source SHA. Provider secrets, preview
tokens, realtime tickets, bearer tokens, request credentials, and configured
sensitive values are redacted before storage or display. Realtime message
bodies are not logged by default.

## Quotas and metering

At minimum, meter and enforce:

- concurrent and queued builds;
- build minutes and sandbox size;
- compiler and provider operation attempts;
- CPU time, wall time, memory, and subrequests;
- runtime request count and duration;
- egress requests, bytes, and destinations by policy class;
- state scopes, operations, contention, duration, and stored bytes;
- realtime connections and connection time;
- realtime events, deliveries, fan-out, rejected work, and bytes;
- artifact count, stored bytes, and retention;
- preview count and lifetime;
- provider-specific billable units.

Every billable or externally mutating operation carries a tenant, project,
deployment or resource target, idempotency key, and audit attribution.

Account and global circuit breakers must stop denial-of-wallet conditions even
when an individual project remains within nominal quotas.

## Disablement, suspension, and repository changes

Repository administrators may disable a project immediately. Platform
operators may suspend a repository, project, deployment, hostname, preview,
runtime, account, or organization.

Serving checks current repository, project, deployment, access epoch, realtime
generation, and suspension state before routing. Artifacts and provider
endpoints are never exposed as an unguarded storage bypass.

A private, unlisted, deleted, or suspended source repository disables its
public Deploy project immediately. Historical deployment and hostname records
remain for audit and takeover prevention.

Suspension takes precedence over publication, rollback, feature flags, cached
previews, and repository-admin actions.

State transitions are explicit:

| Event | Routing and triggers | Retained state | Resume |
| --- | --- | --- | --- |
| Repository admin disables project | Stop routing and new triggers; increment desired, access, and realtime generations; drain active sessions; fence in-flight operations. | Active pointer, releases, deployments, hostname claims, and durable state remain. | Admin re-enables after fresh eligibility checks and explicitly confirms the prior active deployment. |
| Source becomes private or unlisted | Apply a source-visibility eligibility block immediately; stop routing and triggers. | Project state, pointer, history, hostname claims, and resources remain. | Returning public removes only the source block; an admin must revalidate before serving resumes. |
| Source is deleted | Stop routing and triggers; tombstone repository and hostnames. | Audit history and hostname tombstones remain; resource retention policy begins. | No automatic resume. Repository restoration and operator review are required. |
| Platform suspension | Stop routing, previews, triggers, promotion, rollback, and provider mutations except containment; revoke tickets and close active realtime sessions. | Evidence and quarantined resources remain under operator policy. | Only an authorized operator may lift suspension. |
| Billing or quota block | Stop new builds and mutations; serving behavior follows plan policy and global safety limits. | Existing releases and resources remain. | Reconcile eligibility before queued work or serving resumes. |

Disablement and every blocking transition invalidate provider-operation leases.
Public restoration, payment, or flag changes never resurrect a stale operation.

## Audit and abuse operations

The operator surface supports:

- append-only audit and usage records;
- public abuse reporting;
- phishing, malware, spam, open-proxy, cryptomining, and anomalous-egress
  triage;
- immediate suspension with actor and reason;
- artifact quarantine;
- takedown, appeal, restoration, and retention workflow;
- spend, traffic, error, and cleanup alerts;
- reconciliation of provider resources against Forge state.

The target suspension propagation SLO must be published and verified before
production graduation.

## Feature flags

Initial flags should be explicit control-plane entitlements:

- `deploy_application`;
- `broad_public_egress`;
- `realtime_extended_limits`;
- future `explicit_provider_placement`;
- future `railway_service`;
- future `modal_job`;
- future `runtime_secrets`.

Durable state is part of `deploy_application` and has no separate provider
feature flag. Managed realtime works within its conservative default limits;
`realtime_extended_limits` authorizes only reviewed higher ceilings. Feature
flags cap requested behavior. They are never passed to user code as provider
credentials or management authority.

## Acceptance scenarios

Before constrained dynamic Deploy is enabled:

1. A repository administrator accepts current terms and enables an eligible
   public repository.
2. A push is built from the exact SHA and produces typed immutable artifacts.
3. An application cannot access raw provider bindings or a disallowed network
   target.
4. Two competing mutations in one state scope serialize, exactly one
   reservation wins, and the winning mutation atomically emits its version.
5. Preview state is isolated from production and expires according to policy.
6. Managed realtime enforces opaque scoped authorization, state-bound server
   emission, connect-before-snapshot sequencing, fan-out and spend limits,
   activation drains, and snapshot-based reconnect.
7. Polling fallback preserves the same authorization, generation, and quota
   semantics and never bypasses a denial.
8. Configurable preview access, revocation, and repository suspension work
   without cache delay.
9. Default automatic publication rejects stale branch heads, stale plans,
   failed checks, failed components, and suspended projects.
10. Owner-reviewed publication records its approver and compare-and-set result.
11. Rollback selects a prior ready release without rebuilding or rewinding
    state, and old realtime sessions reconnect against the new generation.
12. A failed provider operation is idempotently retried and its earlier attempt
   remains visible.
13. Explicit state destruction requires separate authority.
14. Broad-public egress still blocks metadata, private, platform, rebinding,
    redirect, unsafe-port, and quota-bypass cases.
15. All provider mutations, usage, promotion, rollback, suspension, and cleanup
    are attributable and reconcilable.
