Skip to content

Architecture

The initial production topology and service boundaries.

Updated View as Markdown

StackShip starts as six independently deployed Workers in one pnpm/Turborepo monorepo.

Workspace Runtime Domain
apps/web Next.js 16 through OpenNext stackship.run
apps/api Hono api.stackship.run
apps/mcp Hono and the MCP TypeScript SDK mcp.stackship.run
apps/docs Nimbus on Astro docs.stackship.run
apps/orchestrator Cloudflare Workflows internal service binding
apps/credential-broker Worker and Durable Objects internal service binding

The separate public analytics surface, r.stackship.run, is a PostHog-managed US reverse proxy rather than a seventh StackShip Worker. The dashboard’s shared product-event helper allows only the documented event and property schemas. The managed proxy forwards the PostHog protocol and is not a StackShip validation boundary. Its root is not a health endpoint; release checks use PostHog’s proxied SDK asset and project-configuration paths.

Request flow

The dashboard calls the native API through a same-origin /api/control-plane boundary. That boundary admits only methods and paths in the generated v1 contract, rewrites the host-only dashboard session into the API’s Better Auth session cookie, rejects unsafe cross-origin mutations, and forwards logs and artifacts without buffering their complete bodies. Authenticated server-side API fetches use Worker’s supported manual redirect mode. The boundary never follows a redirect, and the generated JSON client rejects any redirect response instead of forwarding the session cookie.

Better Auth cookies remain host-only. Browser JavaScript never receives or forwards the API session cookie directly.

The API owns the Better Auth handler at /api/auth. OAuth login and consent routes redirect to dashboard screens while preserving Better Auth’s signed OAuth query. The MCP Worker publishes RFC 9728 protected-resource metadata, points clients to the API authorization server, and verifies JWT access tokens against the API’s JWKS. Its tools hold no business logic: each one forwards the caller’s token to the native v1 API, which independently re-authenticates the credential and confines it to the published MCP operation surface.

Data plane

PlanetScale PostgreSQL is reached from the API through the Cloudflare Hyperdrive binding 397048f06dbc43c1a921c92258384a40. Drizzle owns application and Better Auth schema. Application traffic uses a least-privileged PlanetScale role; schema migrations use a separate direct administrative connection.

The identity Hyperdrive configuration has query caching disabled. Hyperdrive does not expose a supported per-query cache hint, and Better Auth owns its SQL, so sessions, permissions, organizations, and identity reads need the cache-disabled connection. When product reads can tolerate staleness, add a second cache-enabled Hyperdrive configuration and route only those reads through it.

R2 is the object plane for state-related artifacts, policy source and bundle artifacts, plans, run logs, and exports. Workers Analytics Engine is for high-cardinality operational events, while PostHog is reserved for product behavior and funnels.

Configuration archives, policy source archives, lock files, state, and run artifacts stream through Workers and R2. The API does not aggregate an artifact in Worker memory; the launch limit is 100 MB and stream readers enforce bounded read-ahead. When the plaintext length is known, encryption produces an exact-length FixedLengthStream for R2. Unknown-length bodies use sequential multipart upload with one 5 MiB part buffered at a time and at most 10,000 parts; failures abort the multipart upload. Neither path buffers the complete artifact.

The OpenTofu Cloud backend is the sole state writer for managed runs. Its attempt token exposes the run-pinned predecessor through the TFE-compatible API, so the runner neither receives a predecessor-state artifact nor writes one into the working directory. During apply, OpenTofu creates the successor version through /api/v2, streams the raw state through the Worker into its final encrypted R2 identity, and finalizes the metadata and outputs before the command exits. There is no runner upload, second object copy, or in-memory state handoff.

After a zero-exit apply, the session-bound runner asks for publication metadata, never the state body. The API returns an id, checksum, and serial only when the current version and its published upload have the exact managed-run principal, attempt, sandbox, predecessor, lock, fence, object identities, and active writer reservation. The runner binds its terminal result digest to that checksum. The commit transaction independently rechecks the same immutable publication before recording the apply’s resulting state and releasing the exact principal’s lock. An exact callback replay uses the persisted resulting state pointer after release; changed replay bindings still fail closed.

OpenTofu writes errored.tfstate only when it cannot persist state through the Cloud backend. The runner can retain that file as recovery evidence, but it never treats an ordinary root terraform.tfstate as a resulting-state copy. Ordinary Cloud plans and successful applies produce no attempt-scoped state or output copy.

A nonzero apply may still have published partial state through the Cloud backend. StackShip keeps the exact managed-run writer authority and enters recovery instead of calling that apply successful. Recovery may retain the current pointer only when the state version and its published upload prove the same run, attempt, sandbox, predecessor, lock, and fence. Unrelated pointer movement remains a hard conflict.

Artifact deletion removes the logical metadata reference before the physical R2 object. A metadata failure therefore leaves the committed object readable; an R2 deletion failure leaves an inaccessible, unreferenced object for the orphan sweep instead of a metadata row that points to missing ciphertext.

Final encrypted objects are written before their PlanetScale metadata commits. A daily ArtifactOrphanSweepWorkflowV1 instance walks R2 in pages of at most 1,000 objects through an authenticated internal API. It deletes only StackShip-shaped keys older than 24 hours that have no exact committed artifacts.r2_object_key; current uploads, committed objects, and unknown key shapes are retained. The opaque R2 cursor is persisted between Workflow steps, so neither Worker accumulates a bucket-wide key inventory.

Native v1 and TFE-compatible JSON control documents are the deliberate exception: ordinary control documents are capped at exactly 65,536 bytes (64 KiB) before parsing. Variable mutation envelopes have a separate 131,072-byte (128 KiB) cap so they can carry one supported 65,536-byte value plus bounded JSON metadata. Credential-profile mutation documents have a 100,000-byte cap so a supported 64 KiB canonical generic-secret environment map fits inside its bounded version envelope. A declared oversize is rejected without pulling the body, while an unknown-length stream is canceled as soon as it crosses the applicable limit. Artifact bodies never pass through the control-document reader.

The orchestrator persists run control flow in Cloudflare Workflows, but durable step outputs contain only bounded identifiers and digests. Raw capability handles, bootstrap tokens, execution nonces, callback nonces, and bearer credentials never enter Workflow history. A runner-start step reacquires its attempt-bound bootstrap handle through authenticated service bindings and keeps the handle and token only in Worker memory while starting the exact sandbox process.

The credential broker stores each handle in a Durable Object and binds it to one attempt, phase, sandbox, and immutable input facts. An identical bootstrap issue retry may reuse the original handle without extending its expiry even when the caller refreshed the requested expiry; every non-expiry binding must still match exactly. Changed bindings fail closed. Apply-start capabilities retain an expiry-bounded exact redemption result so an identical retry can recover a lost response without accepting a changed request. Bootstrap tokens additionally bind the final manifest and callback-nonce digests. Secret handles identify an encrypted variable version; neither the broker nor Workflow state persists its plaintext.

Managed execution uses private, ephemeral sandboxes created from Daytona’s standard daytona-medium base. StackShip does not build or publish a custom runner image or snapshot. The orchestrator build embeds generated main-runner and policy-runner bundles; after sandbox creation it uploads those bundles, downloads the manifest-selected OpenTofu and pinned OPA releases, verifies their committed SHA-256 values, and starts the selected bundle. Sandbox egress is unrestricted because both this bootstrap and customer operations require public endpoints; it is not a security boundary.

Policy-version publication uses its own Cloudflare Workflow and a dedicated Daytona runner mode on that same standard base. The browser, dashboard proxy, API, and R2 preserve backpressure while a customer .tar.gz source archive is uploaded; the deterministic encrypted object identity is bound to the tenant, build workspace, policy set, and idempotency key. Workflow state contains only tenant, version, attempt, sandbox, and digest identities. The API streams encrypted R2 source and output artifacts, verifies the database-derived digest binding, and performs the atomic pending-to-published or pending-to-failed transition. The signed policy bootstrap token and deterministic execution nonce exist only inside service-bound mint and start callbacks; durable results project safe sandbox and command identities. Rego validation, tests, and bundle creation never run in a Worker. Fixed OPA capabilities, sanitized input, and the absence of customer cloud credentials constrain policy execution; the sandbox network does not.

Failure posture

Public health and version endpoints do not require the database. Authentication and protected product operations fail closed when Hyperdrive or required secrets are absent. The MCP endpoint never falls back to anonymous access.

Native v1 mutations atomically persist their domain change, audit evidence, exact idempotency result, and any required Workflow outbox entry. External dispatch occurs only after commit. Run, source-ingestion, and policy-publication Workflow IDs are deterministic, so delivery can reconcile a lost create response without starting a second logical execution. The orchestrator’s five-minute schedule calls an authenticated internal reconciler that leases at most 25 pending dispatches per tick; organization, dispatch key, and lease owner constrain every completion or retry.

Launch and operational controls

PlanetScale is the source of truth for one versioned production launch-control snapshot. It contains the default-disabled public launch gate, a bounded dark-canary allowlist of exact organization-and-workspace pairs, independent new-run and apply switches, federation-provider and Daytona-region switches, resource ceilings, logical runner and policy release allowlists, and the exact Workflow v1 routing. Workspace public IDs are organization-local: no launch path may authorize a dark canary from a ws_ ID alone. Organization and workspace suspensions are tenant-scoped rows. Every mutation uses an optimistic expected version and an idempotency key and writes immutable actor, request, reason, before, and after evidence in the same transaction.

Control reads fail closed. When the public gate is false, only an exact organization-and-workspace dark-canary pair may bypass that one gate; missing or malformed controls are never an allow. New managed runs then require the new-run switch and recheck suspension, the pinned region, resource ceilings, and logical runner release. Sandbox creation rechecks the stored region immediately before Daytona and never chooses another region. The current Daytona organization supports the required container class only in us, so the eu launch switch remains disabled and an EU workspace never falls back to US. Apply authorization and the runner start intent independently recheck the public and apply gates. These execution checks do not replace product admission and do not independently gate reads, cancellation, cleanup, or recovery.

Product admission is separate from authentication and execution admission. The native cloud GET /api/v2/ping route remains public. Every other TFE-compatible route first validates its cloud credential or bearer capability, resolves a bounded ownership projection, and then admits only the exact owning organization-and-workspace pair while launch is closed. Organization-wide, project-wide, unknown, malformed, missing, and ambiguous ownership all fail closed. Configuration-upload, state-upload, and log capabilities carry no admission bypass; their verified resource ownership must resolve to the same allowed pair.

A closed or unresolved native request returns the same non-disclosing JSON:API 503 service_unavailable response with Retry-After: 60, a request ID, and private no-store caching. Invalid cloud credentials remain 401 invalid_token. The response never reveals whether a resource exists or which pair is on the allowlist. Product admission happens before a handler claims an upload capability or persists a mutation. Separately, a failed new-run execution check uses the same retryable status and never persists a partial run.

Dashboard product admission

Dashboard authentication and product admission are separate boundaries. The authenticated root layout verifies the host-only Better Auth session and active organization, but it does not render the product shell. Organization- wide route groups first call GET /v1/launch-access; a launch-availability 503 fails the render at the dashboard error boundary before the full navigation rail is rendered, without loading or implying project, workspace, run, or state data.

Exact workspace and run route groups do not use the general probe. They call GET /v1/launch-access/workspaces/:workspaceId or GET /v1/launch-access/runs/:runId on the server. Public launch renders the normal organization shell. A dark-canary admission renders only the exact resource surface: desktop and mobile organization navigation, project links, and organization-wide run links are absent. Nested links may remain only when they stay inside the admitted workspace, or move from an admitted run to its exact owning workspace. Unknown, malformed, closed, and non-allowlisted probes fail closed rather than falling back to general access.

The same-origin dashboard API boundary preserves an upstream denial’s status, request ID, Retry-After, and private no-store cache policy while normalizing the error envelope. It streams request and response bodies and never buffers an artifact to implement admission.

Playwright exercises these server probe paths against only the explicit loopback stackship_test database. Its launch-control fixture requires a hard-coded local-E2E marker, snapshots the exact prior production control row or its absence, and restores that exact snapshot during teardown and after each closed or dark-canary case. The fixture refuses remote hosts, another database name, or a missing marker; local PostgreSQL availability is therefore an E2E prerequisite, not a reason to weaken an admission assertion.

Each sandbox create intent atomically acquires a durable regional capacity lease in the same PlanetScale transaction. Active leases occupy uniquely indexed regional slots, so simultaneous contenders cannot exceed the current ceiling and an attempt replay cannot reserve twice. The provider identity is bound to that lease after creation, and successful cleanup releases it in the same transaction that terminates the attempt. Ambiguous provider outcomes keep the lease active until exact-label reconciliation proves cleanup.

Workers Analytics Engine receives only strict operational event names, opaque IDs, bounded enums, durations, counts, and byte sizes. PostHog receives only the allowlisted launch funnel; automatic page, element, and session capture are disabled. Repository names, cloud identifiers, resource addresses, email addresses, secrets, and artifact content are rejected at the telemetry boundary. Unhandled execution and runner control-plane failures emit only a fixed error or non_error classification; caught error messages, attached provider context, request bodies, and authorization material never cross the logging boundary.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close