# Hosting — Cloudflare Pages · ugrad.ai

**DNS (CNAME target):** **[DNS.md](./DNS.md)** — must be `ugrad-kbatch.pages.dev`, **not** `qbitos.github.io`  
**Where the product is a thing:** [WHERE-IT-LIVES.md](./WHERE-IT-LIVES.md)  
**Ecosystem:** [ECOSYSTEM-MAP.md](./ECOSYSTEM-MAP.md)

| Identity | Value |
|----------|--------|
| productId | `ugrad.kbatch.shadow` |
| CF project | `ugrad-kbatch` |
| version | `version.json` |


## Domains

| Host | CF project | Content |
|------|------------|---------|
| **`kbatch.ugrad.ai`** | Pages **`ugrad-kbatch`** | **This SPA — Shadow Live + dictionary** |
| `dictionary.ugrad.ai` | same | alias |
| `ugrad.ai/kbatch/` | proxy / path | alternate |
| `data.ugrad.ai/kbatch/` | R2 / Worker | `data/analyzed/*` (optional) |

Dashboard: **Pages → ugrad-kbatch → Custom domains → kbatch.ugrad.ai**.

## Deploy (do-once checklist)

```bash
# From repo root — ships SPA + /shadow route
npm run deploy:cf
# same as: npx wrangler pages deploy . --project-name=ugrad-kbatch

# First time in Cloudflare dashboard:
#   1. Pages → Create → Connect git (or direct upload)
#   2. Project name: ugrad-kbatch
#   3. Custom domains → kbatch.ugrad.ai (+ optional dictionary.ugrad.ai)
#   4. Secrets for CI: CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID
```

`wrangler.toml` → `pages_build_output_dir = "."`, project name `ugrad-kbatch`.

**Routes** (`_redirects`):

| Path | File |
|------|------|
| `/` | `index.html` |
| `/shadow` | `shadow.html` |
| `/live` | `shadow.html` |
| `/dojo` | `dojo.html` |

**GlueLam meta** (already set in `index.html` + `shadow.html`):

```html
<meta name="kbatch-gluelam-base" content="https://mueee.qbitos.ai/" />
```

### What to ship

| Path | Size | Ship? |
|------|------|--------|
| `index.html`, `shadow.html`, `js/`, `css/`, `docs/` | small | **yes · Pages** |
| `data/word-index.json` + `data/words/{a-z}.json` | ~20MB | **yes · Pages and/or R2** |
| `data/analyzed/{a-z}.json` | **~1.4GB** (v3 multi-layout + ascii/binary) | **R2 only** |
| `data/research/*` | small→large | **R2** (+ optional Pages stubs) |
| `exports/` | optional | no |

### R2 for analyzed corpus (required for full CF host)

```bash
# Enrich packs with ASCII + binary (pack v3) if not already
npm run enrich:codes

# Upload words + analyzed + research → R2
./scripts/upload-r2.sh
# bucket default: ugrad-kbatch-data  prefix: kbatch/

# Deploy SPA without analyzed/
./scripts/deploy-pages.sh
```

1. Create R2 bucket `ugrad-kbatch-data` (dashboard or `wrangler r2 bucket create`).
2. Custom domain: `data.ugrad.ai` → R2 public / Worker rewrite `/kbatch/*`.
3. In `index.html` + `shadow.html`:

```html
<meta name="kbatch-data-base" content="https://data.ugrad.ai/kbatch/" />
```

SPA loads `analyzed/{letter}.json` from that base first, falls back to `./data/analyzed/` locally.

See also [RESEARCH-LAYER.md](./RESEARCH-LAYER.md) (independent of Wiktionary/OED).

### GlueLam optional CDN

Point shared modules (when not stubbing):

```html
<meta name="kbatch-gluelam-base" content="https://mueee.qbitos.ai/" />
```

Loads `quantum-prefixes.js`, `qbit-dac.js`, `qbit-steno.js`, `qbit-preflight.js` if present.

## GitHub vs Cloudflare vs X

| Host | Role |
|------|------|
| **GitHub qbitOS** | Source, contracts, CI |
| **Cloudflare ugrad.ai** | **Production runtime** |
| **X / Grok** | Story + X Article batch — not the engine |

## Overview pipe

```js
kbatchDict.overview.pipe(longText)
// → OverviewWorkspaceSnapshot v2
// https://fornevercollective.github.io/overview/
```

## Verify after deploy

```js
// on kbatch.ugrad.ai console
kbatchDict.corpus.livesAt()
kbatchDict.corpus.runShadowLive("hello")
kbatchDict.corpus.gluelam()
```
