···11+# Deploy Understory to Railway Spec
22+33+**Date:** 2026-04-10
44+**Issue:** Chainlink #31
55+**Status:** Approved (pending review)
66+**Depends on:** PR #9 (scoring algorithm — merged), PR #5 (social graph crawler — merged)
77+**Unblocks:** #32 (write submission post), #33 (open source on GitHub)
88+99+---
1010+1111+## 1. Goal
1212+1313+Deploy the Understory Next.js application to Railway with a custom domain (`understory.watch`), self-hosted AT Protocol OAuth client metadata, and the 125MB `data/` directory correctly bundled. After this work, the site is publicly accessible with working OAuth login, social graph crawling, and talk browsing.
1414+1515+---
1616+1717+## 2. Background
1818+1919+Understory is currently dev-only behind Tailscale Funnel. The conference ended March 29
2020+ — every day the site isn't live is a day the project's audience shrinks. The codebase is production-ready: 115+ talk pages with HLS video + transcripts, an OAuth login flow, a social graph crawler, and a scoring engine. What's missing is the deployment itself.
2121+2222+**Current state:**
2323+- Next.js 16 / React 19 / TypeScript 5
2424+- AT Protocol OAuth via `@atproto/oauth-client-node`, sessions in-memory (ephemeral by design)
2525+- `data/talks.json` (talk index) and `data/transcripts/*.json` (128 transcript files, ~125MB total) loaded at runtime via `fs.readFileSync`
2626+- OAuth `client_id` currently points at `cimd-service.fly.dev` (shared dev-only metadata host)
2727+- No Railway config exists in the repo
2828+2929+**Target state:**
3030+- Live at `https://understory.watch`
3131+- Self-hosted OAuth client metadata (no cimd-service dependency)
3232+- Railway auto-deploys on push to `main`
3333+- Two environment variables: `APP_URL` and `HOSTNAME=0.0.0.0`
3434+3535+---
3636+3737+## 3. Code Changes
3838+3939+### 3.1 `next.config.ts` — standalone output + data tracing
4040+4141+Add `output: "standalone"` for Railway's builder, and `outputFileTracingIncludes` to ensure the `data/` directory is bundled into the standalone output.
4242+4343+The `data/` directory contains `talks.json` and 128 transcript JSON files loaded at runtime via dynamically-constructed `fs.readFileSync` paths (e.g., `path.join(DATA_DIR, "transcripts", `${rkey}.json`)`) which `@vercel/nft` cannot trace automatically. The `outputFileTracingIncludes` directive explicitly includes them.
4444+4545+Remove the Tailscale-specific `allowedDevOrigins` entry — it's a dev convenience that shouldn't ship to production. Keep the `127.0.0.1` entry (harmless, useful if someone clones and runs locally).
4646+4747+**Before:**
4848+```ts
4949+const nextConfig: NextConfig = {
5050+ experimental: {
5151+ viewTransition: true,
5252+ },
5353+ allowedDevOrigins: [
5454+ "127.0.0.1",
5555+ "bryans-mac-mini.wildebeest-puffin.ts.net",
5656+ ],
5757+};
5858+```
5959+6060+**After:**
6161+```ts
6262+const nextConfig: NextConfig = {
6363+ output: "standalone",
6464+ outputFileTracingIncludes: {
6565+ "/*": ["./data/**/*"],
6666+ },
6767+ experimental: {
6868+ viewTransition: true,
6969+ },
7070+ allowedDevOrigins: [
7171+ "127.0.0.1",
7272+ ],
7373+};
7474+```
7575+7676+### 3.1b `package.json` — postbuild script + standalone start command
7777+7878+Next.js standalone mode fundamentally changes how the app is served:
7979+8080+1. **Static assets excluded.** `.next/static/` (CSS/JS bundles) and `public/` are deliberately excluded from the standalone output. Without them, pages load with no styles and no client-side JavaScript (no hydration, no HLS player, no interactivity).
8181+8282+2. **Data directory not at `process.cwd()/data`.** The standalone server runs from `.next/standalone/`, so `process.cwd()` resolves there — not the project root. The `outputFileTracingIncludes` config puts traced files under `.next/standalone/.next/server/`, but `path.resolve(process.cwd(), "data")` still looks for `.next/standalone/data/`.
8383+8484+3. **Start command changes.** The standalone server is `node .next/standalone/server.js`, not `next start`. Using `next start` would bypass the standalone output entirely, defeating the optimization.
8585+8686+**Fix:** Add a `postbuild` script that copies the three missing pieces into the standalone directory, and change the `start` script:
8787+8888+```json
8989+"scripts": {
9090+ "dev": "next dev",
9191+ "build": "next build",
9292+ "postbuild": "cp -r .next/static .next/standalone/.next/static && cp -r public .next/standalone/public && cp -r data .next/standalone/data",
9393+ "start": "node .next/standalone/server.js",
9494+ ...
9595+}
9696+```
9797+9898+The `postbuild` script runs automatically after `npm run build` (npm lifecycle hook). It copies:
9999+- `.next/static/` → `.next/standalone/.next/static/` (CSS/JS bundles)
100100+- `public/` → `.next/standalone/public/` (currently empty, but correct for future static assets)
101101+- `data/` → `.next/standalone/data/` (talk + transcript JSON files)
102102+103103+The `start` script changes from `next start` to `node .next/standalone/server.js` so Railway (and local `npm start`) use the standalone server.
104104+105105+**Note:** The standalone server reads `PORT` and `HOSTNAME` environment variables. Railway sets `$PORT` automatically, but `HOSTNAME` defaults to `localhost` which won't work on Railway — it needs `HOSTNAME=0.0.0.0` to bind on all interfaces. This is set as a Railway env var in §4.2.
106106+107107+### 3.2 `src/lib/auth/metadata.ts` — shared client metadata builder
108108+109109+The AT Protocol OAuth spec requires that `client_id` is a URL that resolves to a JSON document containing the client metadata. PDS servers fetch this URL during the auth flow and compare it against what the client claims locally. If they don't match, the flow fails with a cryptic mismatch error.
110110+111111+To prevent drift, the metadata object is defined **once** in a shared module and imported by both the HTTP route (§3.3) and the OAuth client (§3.4).
112112+113113+**Create:** `src/lib/auth/metadata.ts`
114114+115115+```ts
116116+export const CLIENT_METADATA_PATH = "/oauth/client-metadata.json";
117117+118118+/**
119119+ * Granular OAuth scopes — replaces the overpermissioned `transition:generic`.
120120+ * Understory only reads follows and searches posts; it never writes records.
121121+ */
122122+export const OAUTH_SCOPE = [
123123+ "atproto",
124124+ "rpc:app.bsky.graph.getFollows?aud=*",
125125+ "rpc:app.bsky.feed.searchPosts?aud=*",
126126+].join(" ");
127127+128128+export function buildClientMetadata(appUrl: string) {
129129+ const clientId = `${appUrl}${CLIENT_METADATA_PATH}`;
130130+ return {
131131+ client_id: clientId,
132132+ client_name: "Understory",
133133+ client_uri: appUrl,
134134+ redirect_uris: [`${appUrl}/oauth/callback`],
135135+ grant_types: ["authorization_code", "refresh_token"],
136136+ response_types: ["code"],
137137+ scope: OAUTH_SCOPE,
138138+ application_type: "web",
139139+ dpop_bound_access_tokens: true,
140140+ token_endpoint_auth_method: "none",
141141+ };
142142+}
143143+```
144144+145145+`buildClientMetadata` is a pure function: given `APP_URL`, it returns the exact metadata object. Both the route handler and `NodeOAuthClient` call it with the same `APP_URL`, making drift structurally impossible.
146146+147147+`OAUTH_SCOPE` is also exported and used by `src/app/oauth/login/route.ts` in the `client.authorize()` call, so the scope requested at authorization time always matches the scope declared in the client metadata.
148148+149149+### 3.3 `src/app/oauth/client-metadata.json/route.ts` — self-hosted metadata endpoint
150150+151151+**Route path:** `src/app/oauth/client-metadata.json/route.ts`
152152+153153+This creates a Next.js App Router route handler at `/oauth/client-metadata.json`. The folder name contains a literal dot — Next.js App Router supports this (directories are just filesystem names), but if any tooling issue arises during testing, the fallback is to rename the folder to `client-metadata` (serving at `/oauth/client-metadata`) and update `CLIENT_METADATA_PATH` in `metadata.ts` accordingly. The AT Protocol spec does not mandate a specific path — only that `client_id` resolves to valid metadata.
154154+155155+```ts
156156+import { NextResponse } from "next/server";
157157+import { buildClientMetadata } from "@/lib/auth/metadata";
158158+159159+export async function GET() {
160160+ const appUrl = process.env.APP_URL;
161161+ if (!appUrl) {
162162+ return NextResponse.json(
163163+ { error: "APP_URL not configured" },
164164+ { status: 500 },
165165+ );
166166+ }
167167+ return NextResponse.json(buildClientMetadata(appUrl));
168168+}
169169+```
170170+171171+**Why a route handler and not a static JSON file:** The `client_id` and `redirect_uris` contain the app's URL, which varies by environment (localhost in dev, `understory.watch` in production). A dynamic route reads `APP_URL` at request time so the same code works everywhere without build-time substitution.
172172+173173+**Content-Type:** `NextResponse.json()` automatically sets `Content-Type: application/json`, which is what PDS servers expect.
174174+175175+### 3.4 `src/lib/auth/client.ts` — derive `client_id` from `APP_URL`
176176+177177+Replace the `OAUTH_CLIENT_ID` environment variable with metadata derived from `APP_URL` via the shared `buildClientMetadata` function.
178178+179179+**Changes:**
180180+181181+1. Remove the `OAUTH_CLIENT_ID` env var check — it's no longer needed.
182182+2. Import and call `buildClientMetadata(appUrl)` for the `clientMetadata` field.
183183+3. Update `client_name` from `"Understory (Development)"` to `"Understory"` (handled by the shared builder).
184184+185185+**After:**
186186+```ts
187187+import { buildClientMetadata } from "./metadata";
188188+189189+function createClient(): NodeOAuthClient {
190190+ const appUrl = process.env.APP_URL;
191191+192192+ if (!appUrl) {
193193+ throw new Error(
194194+ "Missing APP_URL environment variable. " +
195195+ "Set to your app's public URL (e.g., https://understory.watch).",
196196+ );
197197+ }
198198+199199+ return new NodeOAuthClient({
200200+ clientMetadata: buildClientMetadata(appUrl),
201201+ // ... stateStore and sessionStore unchanged
202202+ });
203203+}
204204+```
205205+206206+### 3.5 `.env` update for local development
207207+208208+Update the local `.env` to remove `OAUTH_CLIENT_ID` (no longer used) and ensure `APP_URL` is set:
209209+210210+```
211211+APP_URL=http://127.0.0.1:3000
212212+ASSEMBLYAI_API_KEY=<kept for offline transcription scripts>
213213+```
214214+215215+The `.env` file is gitignored (not committed). This change is documented so developers know what to set after cloning.
216216+217217+---
218218+219219+## 4. Railway Infrastructure
220220+221221+### 4.1 Environments: staging + production
222222+223223+The project uses two Railway environments with a promotion workflow:
224224+225225+| Environment | Branch | Domain | Purpose |
226226+|---|---|---|---|
227227+| **Staging** | `staging` | Auto-generated `*.up.railway.app` | Validate changes before production. Push here first. |
228228+| **Production** | `main` | `understory.watch` (custom domain) | Public-facing. Code arrives via `staging` → `main` merge. |
229229+230230+**Workflow:** Push to `staging` branch → Railway auto-deploys staging → validate on the Railway domain → merge `staging` → `main` → Railway auto-deploys production.
231231+232232+This means you can make tweaks, test them on the staging URL, and only promote to production when confident. The two environments run the same code at different points in time, differentiated only by their `APP_URL` and domain.
233233+234234+### 4.2 Create project and services
235235+236236+Using Railway MCP tools or CLI:
237237+238238+1. Create a new Railway project named "understory"
239239+2. Link to the GitHub repo `musicjunkieg/understory`
240240+3. Create two environments:
241241+ - **Production** environment: deploys from `main` branch
242242+ - **Staging** environment: deploys from `staging` branch
243243+4. Railway auto-detects Next.js and uses its Node.js builder (Nixpacks) for both
244244+5. Each environment gets its own service instance, env vars, and domain
245245+246246+### 4.3 Environment variables
247247+248248+**Staging environment:**
249249+250250+| Variable | Value | Notes |
251251+|---|---|---|
252252+| `APP_URL` | `https://<staging>.up.railway.app` | The auto-generated Railway domain for staging. Set after Railway creates it. |
253253+| `HOSTNAME` | `0.0.0.0` | Required. Standalone server defaults to `localhost`; `0.0.0.0` binds on all interfaces. |
254254+255255+**Production environment:**
256256+257257+| Variable | Value | Notes |
258258+|---|---|---|
259259+| `APP_URL` | `https://understory.watch` | The custom domain. |
260260+| `HOSTNAME` | `0.0.0.0` | Same as staging. |
261261+262262+`NODE_ENV=production` is set automatically by Railway in both environments. `PORT` is set automatically. `ASSEMBLYAI_API_KEY` is not needed at runtime (only for offline build scripts).
263263+264264+### 4.4 Domain configuration
265265+266266+**Staging:** Uses the auto-generated `*.up.railway.app` domain. No custom domain needed — it's for validation only.
267267+268268+**Production:**
269269+1. Add `understory.watch` as a custom domain in the production environment
270270+2. Railway provides a CNAME target (e.g., `<hash>.railway.app`)
271271+3. At your registrar, create a CNAME record: `understory.watch` → `<railway-cname-target>`
272272+4. Railway auto-provisions an SSL certificate via Let's Encrypt
273273+5. DNS propagation typically takes 5–30 minutes
274274+275275+### 4.5 Branch setup
276276+277277+Create the `staging` branch from `main` and push it:
278278+279279+```bash
280280+git checkout main
281281+git checkout -b staging
282282+git push -u origin staging
283283+```
284284+285285+Going forward: feature branches merge into `staging` for testing, then `staging` merges into `main` for production release.
286286+287287+---
288288+289289+## 5. OAuth Flow in Production
290290+291291+The OAuth flow works as follows after deployment:
292292+293293+1. User clicks "Log in" on `understory.watch`
294294+2. App calls `client.authorize(handle)` which initiates the OAuth handshake
295295+3. The PDS at the user's handle fetches `https://understory.watch/oauth/client-metadata.json` to validate the client
296296+4. PDS confirms `redirect_uris` includes `https://understory.watch/oauth/callback`
297297+5. User is redirected to their PDS's authorization page
298298+6. After approval, PDS redirects to `https://understory.watch/oauth/callback` with an auth code
299299+7. App exchanges the code for tokens, stores session in-memory, sets the `understory_did` cookie
300300+8. User sees their avatar in the Nav; `/api/crawl` becomes available
301301+302302+**Session lifecycle:**
303303+- Sessions are in-memory `Map` objects (ephemeral by design)
304304+- On redeploy: all sessions are lost; users' next request transparently re-authenticates via `client.restore(did)` using the DID from their browser cookie
305305+- Cookie settings: `httpOnly: true`, `secure: true` (automatic in production via `NODE_ENV`), `sameSite: "lax"`, `path: "/"`, `maxAge: 7 days`
306306+- No database or Redis needed
307307+308308+---
309309+310310+## 6. Data Bundling
311311+312312+The `data/` directory (125MB) is included in every deploy via `outputFileTracingIncludes` AND the `postbuild` copy script (§3.1b). Both mechanisms work together: `outputFileTracingIncludes` ensures Next.js traces the files for its file system, and `postbuild` copies them to `.next/standalone/data/` so `process.cwd()/data` resolves correctly in the standalone server.
313313+314314+The files are:
315315+316316+- `data/talks.json` — master index (~180 talks), read by:
317317+ - `src/app/talks/page.tsx` (SSR at request time — uses `cookies()` for auth check, so cannot be statically generated)
318318+ - `src/app/talk/[rkey]/page.tsx` (`generateStaticParams` provides the rkey list at build time, but pages are SSR'd at request time due to `cookies()`)
319319+ - `src/lib/crawl/crawler.ts` (runtime, cached once per server lifecycle)
320320+- `data/transcripts/*.json` — 128 transcript files, read at request time by `src/app/talk/[rkey]/page.tsx`
321321+322322+**Note:** Despite using `generateStaticParams`, both talk pages call `getAuthUser()` → `cookies()`, which is a dynamic API. This forces Next.js to server-render them on every request rather than statically generating them at build time. The `generateStaticParams` function only provides the list of valid rkeys for routing — it does not enable static generation when dynamic APIs are present.
323323+324324+**To update data:** Run `npm run build-talk-index` and/or `npm run transcribe` locally, commit the updated JSON files, push to `main`. Railway redeploys with the new data.
325325+326326+**The data is static** — it was generated from ATmosphereConf VODs (conference ended April 5, 2026) and won't change unless a re-crawl is needed (e.g., new talks added retroactively).
327327+328328+---
329329+330330+## 7. Verification Plan
331331+332332+### 7.1 Pre-deploy (local)
333333+334334+Before pushing to `staging`:
335335+- [ ] `npm run build` succeeds with `output: "standalone"` — the `.next/standalone/` directory is created
336336+- [ ] The `postbuild` script ran: `.next/standalone/data/`, `.next/standalone/.next/static/`, and `.next/standalone/public/` all exist
337337+- [ ] `npm test` — all 40 scoring tests pass (unrelated to deploy, but confirms nothing broke)
338338+- [ ] `npx tsc --noEmit` — clean
339339+- [ ] `npx eslint src/` — clean
340340+- [ ] `GET /oauth/client-metadata.json` on `localhost:3000` returns valid metadata JSON with `client_id` matching `http://127.0.0.1:3000/oauth/client-metadata.json`
341341+- [ ] OAuth login flow works locally with the self-hosted metadata (no cimd-service)
342342+343343+### 7.2 Post-deploy — staging
344344+345345+Push code to `staging` branch → Railway auto-deploys the staging environment. Using the auto-generated `*.up.railway.app` URL:
346346+347347+- [ ] Landing page loads with correct styles (confirms standalone build + static assets served)
348348+- [ ] `/talks` shows 115+ talk grid (confirms `data/talks.json` was bundled)
349349+- [ ] `/talk/<any-rkey>` shows transcript + HLS video player (confirms transcript JSON files were bundled)
350350+- [ ] `/oauth/client-metadata.json` returns valid metadata with `client_id` matching the staging Railway URL
351351+- [ ] OAuth login flow completes — avatar appears in Nav
352352+- [ ] `/api/crawl` returns `TalkMentions` data (authenticated)
353353+- [ ] Second `/api/crawl` call returns `cached: true` (confirms in-memory cache works)
354354+- [ ] Build logs show no errors or unexpected warnings
355355+356356+### 7.3 Promote to production
357357+358358+After staging passes, merge `staging` → `main`. Railway auto-deploys the production environment.
359359+360360+### 7.4 Post-DNS — production (custom domain)
361361+362362+After pointing `understory.watch` to Railway's production CNAME and DNS propagates:
363363+- [ ] `https://understory.watch` loads with valid SSL certificate
364364+- [ ] Page loads with correct styles (CSS/JS bundles served)
365365+- [ ] `/talks` and `/talk/<any-rkey>` work (data bundled)
366366+- [ ] `/oauth/client-metadata.json` shows `client_id` = `https://understory.watch/oauth/client-metadata.json`
367367+- [ ] Full OAuth flow works on the production domain
368368+- [ ] `/api/crawl` works on the production domain
369369+370370+---
371371+372372+## 8. Edge Cases
373373+374374+- **DNS not propagated yet:** The Railway-generated domain still works as a fallback. The OAuth flow won't work on the custom domain until DNS resolves AND `APP_URL` is updated to the custom domain.
375375+- **Railway cold start:** First request after idle/redeploy takes 2–5 seconds (Node.js startup + module loading + first `fs.readFileSync`). Subsequent requests are fast. Not a concern for this traffic level.
376376+- **Mid-deploy OAuth flow:** A user clicking "Log in" during the ~10 second deploy window gets an error. The in-flight OAuth state is lost. They retry and it works. Acceptable.
377377+- **`data/` directory missing in standalone:** If `outputFileTracingIncludes` doesn't work as expected (unlikely but possible with future Next.js versions), the `/talks` page will crash with `ENOENT`. The verification plan catches this before the custom domain goes live.
378378+- **Cookie not set on Railway domain:** If the browser blocks cookies on `*.up.railway.app` (some aggressive tracker-blockers flag `*.railway.app` subdomains), OAuth won't work on the Railway domain but will work on the custom domain. Not a blocker — the Railway domain is for smoke testing only.
379379+380380+---
381381+382382+## 9. Files Changed
383383+384384+| File | Action | Responsibility |
385385+|------|--------|----------------|
386386+| `next.config.ts` | Modify | Add `output: "standalone"`, `outputFileTracingIncludes`, remove Tailscale dev origin |
387387+| `package.json` | Modify | Add `postbuild` script (copy static/public/data into standalone); change `start` to `node .next/standalone/server.js` |
388388+| `src/lib/auth/metadata.ts` | Create | Shared `buildClientMetadata(appUrl)` function, single source of truth for OAuth metadata |
389389+| `src/app/oauth/client-metadata.json/route.ts` | Create | Self-hosted AT Protocol OAuth client metadata endpoint |
390390+| `src/lib/auth/client.ts` | Modify | Import `buildClientMetadata`, remove `OAUTH_CLIENT_ID` dependency |
391391+| `src/app/oauth/login/route.ts` | Modify | Update `client.authorize()` scope from `transition:generic` to granular scopes |
392392+393393+5 files touched in the codebase. The rest is Railway CLI/MCP configuration (not committed to git).
394394+395395+---
396396+397397+## 10. Non-goals
398398+399399+- **Redis/database for session persistence.** Sessions are ephemeral by design. `client.restore(did)` transparently re-authenticates on the next request after a redeploy. No user action required.
400400+- **Dockerfile or custom builder.** Railway's Nixpacks auto-detects Next.js and handles the build. No custom config needed.
401401+- **CDN or caching layer.** Railway serves static assets with built-in caching. Dynamic routes are server-rendered per request, which is correct for authenticated endpoints like `/api/crawl`.
402402+- **Health check endpoint.** Railway uses automatic TCP health checks. A custom `/health` route would be dead code.
403403+- **CI/CD pipeline.** Railway auto-deploys from GitHub on push to `main`. No GitHub Actions workflow needed.
404404+- **Monitoring/alerting.** Can be added later if needed. Railway provides basic logs and metrics in its dashboard.
405405+- **Rate limiting.** The Bluesky/Constellation APIs are called server-side per authenticated user. At conference-talk traffic levels, rate limiting is unnecessary.
406406+407407+---
408408+409409+## 11. Acceptance Criteria
410410+411411+### Code
412412+- [ ] `next.config.ts` has `output: "standalone"` and `outputFileTracingIncludes` for `data/`
413413+- [ ] `package.json` has `postbuild` script copying `.next/static`, `public`, `data` into standalone directory
414414+- [ ] `package.json` `start` script is `node .next/standalone/server.js`
415415+- [ ] `src/lib/auth/metadata.ts` exports `buildClientMetadata`, imported by both route handler and `client.ts`
416416+- [ ] `/oauth/client-metadata.json` route exists and returns valid metadata
417417+- [ ] `src/lib/auth/client.ts` uses `buildClientMetadata(appUrl)` (no `OAUTH_CLIENT_ID` env var)
418418+- [ ] `npm run build` produces a working standalone output with `data/`, `.next/static/`, and `public/` present in `.next/standalone/`
419419+- [ ] `npx tsc --noEmit` clean, `npx eslint src/` clean, `npm test` passes
420420+421421+### Railway infrastructure
422422+- [ ] Railway project "understory" exists with a service linked to `musicjunkieg/understory`
423423+- [ ] Two environments configured: staging (deploys from `staging` branch) and production (deploys from `main` branch)
424424+- [ ] `APP_URL` and `HOSTNAME=0.0.0.0` set in both environments (different `APP_URL` values)
425425+- [ ] `staging` branch exists and is pushed to origin
426426+427427+### Staging validation
428428+- [ ] Staging auto-deploys on push to `staging`; build logs clean
429429+- [ ] Landing page, `/talks`, `/talk/[rkey]` all work on the staging Railway domain
430430+- [ ] OAuth flow completes on staging
431431+- [ ] `/api/crawl` returns valid crawl data when authenticated on staging
432432+433433+### Production validation
434434+- [ ] Production auto-deploys on merge to `main`; build logs clean
435435+- [ ] Custom domain `understory.watch` configured with valid SSL
436436+- [ ] Landing page, `/talks`, `/talk/[rkey]` all work on `https://understory.watch`
437437+- [ ] Full OAuth flow works on `https://understory.watch`
438438+- [ ] `/api/crawl` returns valid crawl data when authenticated on production