GuidesSelf-hosting

Self-hosting the starter

You can run your own storefront frontend, on your own hosting, against the hosted Flightdeck API. This is the bring-your-own-hosting model (ADR-0008): the platform hosts no customer storefront code, and instead ships an SDK good enough — and a starter complete enough — that you self-host the frontend and it talks to the hosted commerce API.

This page is the high-level “run it yourself” guide. For the click-by-click Vercel path, see Deploying.

What you host vs. what the platform hosts

The split is the whole point, so be clear about it:

  • The platform is one deployable commerce application — a modular monolith built from strictly-bounded modules (catalog, checkout, payments, accounting, and the rest), fronted by the hosted API at https://api.ruo.pro. You do not run it, deploy it, or operate its database. It is a managed API you call.
  • You run only the frontend — the Next.js starter (or your own frontend built on @dscodotco/sdk). It is a normal web app: catalog pages, a client-side cart, and a server-side handler that places orders through the API.

This is why “self-hosting” here means self-hosting your storefront, not the platform. There is no self-hosted build of the commerce backend, by design — a single multi-tenant deployable is a founding decision (FD-1), not an artifact you clone.

The one-deployable model, briefly

You do not need to understand the platform’s internals to self-host a store, but two properties shape the integration:

  • One API, many tenants. Every store is a tenant of the same deployable. Your frontend is pinned to exactly one tenant (yours) and can never address another — the tenant is injected into every request by the SDK, and the API resolves ownership from your credential, never from a request parameter (see Tenancy).
  • Compliance-critical surfaces stay platform-served. Checkout, the authoritative total, the fraud floor, and the RUO claims/disclaimers are enforced by the API — a self-hosted frontend embeds them, it never reimplements them. Your frontend cannot compute its own total or opt out of the age gate; those are the platform’s job on purpose.

Get the starter

The starter is the reference storefront — a standard Next.js app.

git clone https://github.com/DSCO-Co/storefront-starter
cd storefront-starter
npm install

Distribution note. Publishing @dscodotco/sdk to a public registry and pointing a one-click Deploy button at a public starter repo are the remaining distribution steps (ADR-0008); until they land your operator will point you at the current source of the starter and SDK. The clone-and-run model below is unchanged — only where the bits live is being finalized.

Configure — four environment variables

The only Flightdeck-specific configuration is four env vars:

FLIGHTDECK_API_URL="https://api.ruo.pro"   # the hosted API origin
FLIGHTDECK_TENANT="your-store-tenant-ref"   # your store id, from your operator
FLIGHTDECK_STOREFRONT_TOKEN="…"             # operator-issued; server-side secret
SESSION_SECRET="$(openssl rand -hex 32)"    # signs shopper sessions

Setting FLIGHTDECK_TENANT puts the starter in env-pinned single-tenant mode: it resolves its manifest directly by tenant and ignores the request host, so a fresh deploy renders on any URL — an ephemeral preview host, your own domain, or localhost — before a custom domain is verified. The same codebase runs the platform’s own hosted storefront (resolving by verified domain instead); the starter and the platform storefront never fork.

Run it

Locally:

npm run dev    # http://localhost:3000

Anywhere that runs Next.js in production:

npm run build
npm run start

Nothing about the storefront requires a specific host — it is a Next.js app and the SDK is framework-agnostic fetch, so any Node or edge runtime works. Vercel is simply the lowest-friction path (see Deploying).

How a self-hosted frontend talks to the hosted API

Every call goes through your server — the back-end-for-frontend (BFF) pattern. The shopper’s browser talks only to your server; your server holds the storefront token and calls the API:

shopper's browser  -->  YOUR server (BFF)  -->  @dscodotco/sdk  -->  https://api.ruo.pro
                        (holds the token)
// Server-side only — a Route Handler, server component, or edge function.
import { createStorefrontClient } from "@dscodotco/sdk";
 
const store = createStorefrontClient({
  apiUrl: process.env.FLIGHTDECK_API_URL!,
  tenant: process.env.FLIGHTDECK_TENANT!,
  storefrontToken: process.env.FLIGHTDECK_STOREFRONT_TOKEN!, // never in the browser
});
 
const { products } = await store.products.list();

Two consequences of this shape you must respect:

  • No CORS, ever. The API sends no CORS headers (ADR-0008) — a fetch straight from a shopper’s browser to api.ruo.pro is blocked by the browser. That is the contract, not a misconfiguration: reads included, every call goes through your server.
  • The storefront token is a server-side secret. It is the narrow, checkout-scoped x-storefront-token. If it ever appears in a client bundle (an NEXT_PUBLIC_ var, a "use client" import), it has leaked. Construct createStorefrontClient only in server modules.

See Build your own frontend for the full BFF walkthrough (reads, checkout writes, the authored site) and Checkout for order placement, idempotency, and the fraud floor.

Custom domains

Your self-hosted store renders immediately on whatever host you deploy to, because it resolves by tenant. When you are ready to serve it on your own domain, add the domain at your host and verify it in your merchant console — the platform serves a store on a custom domain only through a verified domain-mapping row (the fail-closed host-resolution rule in Tenancy). See Custom domains.