# Demo 8: Bring your own app (BYOA)

**Port:** 3007 (upload page) · **Services:** Cloudflare Workers for Platforms (plain Workers as fallback), OpenRouter (optional adapter job)

## Demo moment

The owner of Nordwerk Projektbüro drags in a zip of a "shift board" app that a freelancer built elsewhere (with Lovable, Bolt or Claude Code). Seconds later it is live at `shift-board.<platform host>` as its own isolated Worker. "Apply our theme" switches it to Nordwerk's brand at the edge, without a rebuild. For an app with hard-coded colours, "Adapt to theme" runs an agent job with a €10 token budget and redeploys. Then hand over to the access demo: "office only".

## Inputs

- `app-manifest.schema.json`: the record this demo writes per app
- `nordwerk-office.json` seed: the theme to apply
- `@demo/ui` `themeToCssVars`: the variables to inject
- `fixtures/`: build two sample apps yourself
  - `shift-board-tokens/`: static app that already uses the platform's CSS variable names and `data-brand-logo` / `data-brand-name` slots
  - `shift-board-hardcoded/`: the same app with hard-coded colours and fonts

## Flow

1. **Upload:** accept a zip of built static output; unpack; build the file manifest (path, 32-hex hash, size).
2. **Deploy:** for the user Worker named after the app slug in `CF_DISPATCH_NAMESPACE`:
   - create an assets upload session, upload the files, then upload the script with the assets completion token (Workers for Platforms static assets API);
   - the script is a minimal `index.js` that serves `env.ASSETS`;
   - bindings: `BUSINESS_SLUG`, and if `data.supabase` is set, `SUPABASE_URL` and `SUPABASE_ANON_KEY` as plain text.
   First uploads are live as soon as the API returns.
3. **Dispatch Worker** (in `dispatch-worker/`, owned by this demo): routes `<slug>.<BYOA_BASE_HOSTNAME>` to the user Worker. If the app's manifest has `theme.mode = inject`, wrap HTML responses with HTMLRewriter:
   - add `<style>:root{…}</style>` from `themeToCssVars` to `<head>`
   - set the logo `src` on the logo selector and the text on the name selector
   - set `font-family` on `<body>`
4. **Manifest store:** keep `AppManifest` records in Workers KV keyed by slug, so the dispatch Worker reads them at the edge.
5. **Adapter job (optional):** reuse the job pattern from `job.schema.json` (capped OpenRouter key, usage meter): an agent rewrites hard-coded colours and fonts in the app's CSS to `var(--color-*)` and `var(--font-sans)`, then redeploy and set `theme.mode = adapted`.
6. **Hand-over:** the deployed hostname is what `demos/access-control` protects (`ACCESS_DEMO_HOSTNAME`).

## Setup

- Workers for Platforms enabled on the account (a paid add-on); create the dispatch namespace.
- The dispatch Worker has its own Wrangler configuration in `dispatch-worker/`, so Wrangler is fine there; elsewhere use the `cf` CLI.
- A route or custom domain for `*.<BYOA_BASE_HOSTNAME>` pointing to the dispatch Worker. Without a zone, route by path on the dispatch Worker's `workers.dev` URL (`/<slug>/`).

## Env

`CLOUDFLARE_API_TOKEN` (Workers Scripts edit, Workers for Platforms), `CLOUDFLARE_ACCOUNT_ID`, `CF_DISPATCH_NAMESPACE`, `BYOA_BASE_HOSTNAME`; optional `OPENROUTER_MANAGEMENT_KEY`, `ADAPTER_MODEL`, `EUR_TO_USD`

## Fallback

- Without Workers for Platforms: deploy each app as a plain Worker with static assets on `workers.dev` (the same upload API without the namespace path) and do the HTMLRewriter injection inside the generated `index.js`.
- A screen recording of upload, deploy and theme switch.

## Done when

- A zip deploys and serves within about ten seconds.
- "Apply our theme" visibly rebrands `shift-board-tokens`; logo and name change in `shift-board-hardcoded`.
- The hostname works as the protected app in the access demo.

## Out of scope

Building from source, server frameworks that need adapters (Next.js via OpenNext), containers, customers' own domains, listing the app in the catalogue (mention in the pitch).
