# Remix Store
The storefront behind [shop.remix.run](https://shop.remix.run).
The storefront runs on Remix 3 and framework-neutral Hydrogen across native Node/Fly and Oxygen runtimes.
## Requirements
- Node.js 24
- pnpm 10.33.4
## Local development
```sh
pnpm install
cp .env.example .env
# Populate PRIVATE_STOREFRONT_API_TOKEN from the configured Hydrogen environment.
pnpm dev
```
The private Storefront token remains server-only and enables request-aware cart,
checkout, and Shopify redirect handling. Oxygen supplies `oxygen-buyer-ip`; Fly
supplies `fly-client-ip`, or behind Fastly, `x-cdn-client-ip` authenticated by
`CDN_ORIGIN_SECRET` (see [`FLY_DEPLOYMENT.md`](./FLY_DEPLOYMENT.md#fastly-cdn)). Other Node runtimes use the client address resolved by
Remix's HTTP adapter from the socket or, when explicitly configured behind a
trusted proxy with `TRUST_PROXY=true`, overwritten proxy headers. Never forward
platform buyer-IP headers outside their runtime. `PUBLIC_CHECKOUT_DOMAIN` is not
a runtime input; checkout uses Shopify's
authoritative `cart.checkoutUrl`. The app does not use durable application
sessions or require `SESSION_SECRET`.
`pnpm dev` runs the framework-native Node server with `remix/assets`. For UI-heavy work, use `pnpm hmr` to preserve browser state while hot-updating client and server modules. Use `pnpm dev:oxygen` when testing the same application under MiniOxygen's Worker runtime.
The example environment points at the live production store. Purchases create real orders and charge real money.
## Validation
```sh
pnpm lint
pnpm typecheck
pnpm test
pnpm test:e2e
pnpm test:e2e:oxygen:dev
pnpm test:e2e:oxygen
pnpm build:oxygen
pnpm preview:oxygen
```
The end-to-end suites use a deterministic local Storefront fixture: `test:e2e` runs native Node, `test:e2e:oxygen:dev` runs MiniOxygen development, and `test:e2e:oxygen` builds and runs the production Worker. Pull requests exercise all three runtimes. Set `BASE_URL` to target an existing deployment instead.
The Oxygen build produces a self-contained Worker at `dist/server/index.js` and browser assets in `dist/client/`. MiniOxygen preview and Shopify CLI discover these standard output directories automatically. The Node server compiles browser modules through `remix/assets` for Fly.
## Deployments
| URL | What it is |
| ------------------------------------------------------------------------ | ------------------------------------------------------- |
| [remix-store.freetls.fastly.net](https://remix-store.freetls.fastly.net) | **Use this one.** Fly app behind the Fastly CDN |
| [remix-store.fly.dev](https://remix-store.fly.dev) | Fly origin, bypasses the CDN (debugging, health checks) |
Fastly caches fingerprinted `/assets/*` files and short-lived root `public/`
files; HTML is `private, no-store` and always reaches Fly. Responses served
through Fastly include `X-Cache` and `X-Served-By` headers.
Oxygen deployments use `.github/workflows/oxygen-deployment.yml`; `pnpm deploy:oxygen` deploys a preview locally. Both pass `--build-command 'pnpm build:oxygen'` so Shopify CLI runs this app's Vite build. The build writes the pinned compatibility date to `dist/server/oxygen.json` alongside the Worker.
Fly setup, secrets, local image verification, and continuous deployment are documented in [`FLY_DEPLOYMENT.md`](./FLY_DEPLOYMENT.md).
## Architecture
- `app/routes.ts` defines the typed route contract.
- `app/router.ts` owns the shared Fetch app, routes, middleware, and runtime boundary.
- `app/assets.ts` defines the shared metadata contract, document asset sources, and document asset factory.
- `app/assets.node.ts` constructs the native Remix asset server; `app/assets.oxygen.ts` constructs Pitlane's Worker-safe manifest resolver.
- `app/node.ts` composes Node static files, Remix Assets, rendering, and routing.
- `app/middleware/storefront.ts` creates a request-scoped Hydrogen Storefront client.
- `app/middleware/render.tsx` contains runtime-neutral streaming SSR.
- `app/runtime.ts` binds environment, cache, and `waitUntil` values to each request.
- `server.node.ts` owns the Node/Fly-compatible HTTP lifecycle.
- `app/entry.oxygen.ts` composes and serves the Oxygen Worker runtime.
- `app/actions/public/entry.tsx` hydrates browser components on both targets.
- `vite/remix-oxygen.ts` composes [Pitlane's Remix Vite plugin](https://pitlane.tools/guides/vite-plugin) with MiniOxygen, then bundles Pitlane's asset manifest into the single `index.js` Worker that Oxygen deploys. Deploy builds serve assets from the deployment's CDN prefix (`HYDROGEN_ASSET_BASE_URL`), so they stay reachable after later deployments.
Pitlane's resolver implements the metadata interface consumed by Remix's `render({ assets })` middleware. Remix owns streaming, frames, and hydration; Pitlane rewrites `clientEntry(import.meta.url, ...)` to portable `file:app/…#Export` identities and resolves them to built browser modules. The installed Remix asset server compiles from the filesystem in Node, so Oxygen needs this build-time bridge. `browserBoundary` in `app/assets.ts` defines which sources are public: the Node asset server refuses to serve anything outside it, and the Oxygen build and dev server fail when a browser module imports something outside it.
## License
MIT License — see [`LICENSE.md`](./LICENSE.md).