Purpose: if context gets compacted or a fresh Claude session picks up here, this single file gets you back to speed in under two minutes. PROGRESS.md tracks the build plan; this tracks the conversation state — what's mid-flight, what just shipped, what's next.
Phase 7 docs + ops runbook shipped. Only Phase 6 OBS hard test remains. End-to-end ready for full live test.
All Phase 5 surface holds (9 dashboard pages, setup wizard, invite-only auth, two-domain workers + custom DNS). Phase 6 polish is now ~80% done — Stats, Streamer Mode, viewer /account, white-label, Tiptap legal, error boundaries, 404, loading skeletons, OG images, robots/sitemap all shipped earlier in the phase. Recent work concentrated on internal consolidation (audit findings, helper dedupe, brand-hex centralization, public-mode removal) and a structural UI refactor (layout token scale → shadcn primitives → layout shells → page migrations → Card sweep).
Recent (latest commits, newest first):
- pending docs: write operations runbook
9dc825ephase 7: fumadocs site + github pages deployd9b2958chore: add root-level d1 migration scripts5a22de7docs: correct webhook URL to tv-api.mrdemonwolf.workers.dev; mark 0009 applied72d7918phase 6: own analytics — viewer + chat tracking via GetStream webhooks6b49085phase 6: card primitive sweep + dashboard inline-style cleanup6c0b155phase 6: route channel page throughChannelLayout8645e46refactor: serve branding logo via Next.js route + optimize through<Image>7441061feat: stream branding logos through/api/branding/logo, drop public-bucket envb3f44abfeat(infra): bindNEXT_PUBLIC_PUBLIC_BUCKET_URLon web workerd1bc6d7phase 6: route auth + viewer pages throughPageContainer335b3c4phase 6: extract sharedLegalDocPage, dedupe privacy + termsd4c057cphase 6: add layout shells (PageContainer,ChannelLayout,ContentSection)c5bd186phase 6: 11 shadcn primitives →@howlcast/ui3ed92b6phase 6: layout token scale →globals.cssdd5167cfeat: invite-only by design — drop public mode end-to-end8620704docs: pre-Phase-6 code audit report (AUDIT.md)98c51c4feat(env):NEXT_PUBLIC_PUBLIC_BUCKET_URLvia env packagec34b1a7refactor(api): dedupe shared helpers, single drizzle per request9d42c56feat(config): centralize brand hex tokens
Live URLs:
- Web: https://tv.mrdemonwolf.com (custom domain) + https://tv.mrdemonwolf.workers.dev (fallback)
- API: https://api.tv.mrdemonwolf.com (custom domain) + https://tv-api.mrdemonwolf.workers.dev (fallback)
- All
/api/healthreturn{"ok":true}. CI/Deploy via GH Actions, all secrets in place.
Outstanding before Phase 6 ships:
- Hard live OBS test — push real RTMPS, verify webhook fires, Discord fanout fires, channel page flips to LIVE, Stats page records the session (incl. peak viewers + chat count + per-session line/bar charts). Only Nathanial can do.
- Cross-browser sweep — Safari, Chrome, Firefox, iOS, Android. Looking for layout regressions in the new shells + Card surfaces + Stats charts.
- ✅ Operations runbook (
docs/operations.md) — DONE
Replaced: Cloudflare Web Analytics dropped — own analytics via GetStream webhooks shipped instead. Per-session detail page at /dashboard/stats/[id] with viewer-line + chat-msgs/min charts. 1-minute cron sampler keeps a baseline series + prunes snapshots > 7 days.
Done since last RESUME refresh:
- Migrations applied to remote D1 ✅
legal@mrdemonwolf.commailbox set up ✅- GetStream Video webhook URL updated (Go Live no longer hangs) ✅
Repo: https://github.com/MrDemonWolf/howlcast — default branch main. Latest pushed: 6b49085. Branch protection enabled.
- Package manager: bun 1.3+. Catalog in
package.jsonworkspaces.catalog. Runbun install,bun run dev,bun run deploy(NOTbun deploy— collides with bun's built-in). - Monorepo: Turborepo.
apps/web(Next.js 16 OpenNext),apps/server(Hono),packages/{api,auth,db,env,infra,mail,ui,config}. - Auth: Better Auth 1.6.9 + plugins (
username,twoFactor,passkeyvia@better-auth/passkey,magicLink). - Mail:
@howlcast/mail— Resend → SMTP/mailpit → console fallback. - DB: Cloudflare D1 (
howlcast-db). Drizzle ORM. Migrations inpackages/db/src/migrations/. Latest migration:0004_lean_nocturne.sql(dropsallow_signupsafter we removed/signupentirely). - Infra: Alchemy 0.93. Resources declared in
packages/infra/alchemy.run.ts. CI usesCloudflareStateStore(state in a Worker + Durable Object); local dev uses file-based store. - CI/CD:
.github/workflows/{ci,deploy,update-license-year}.yml.oven-sh/setup-bun@v2.2.0. Deploy chains viaworkflow_runafter CI green on main. All 10 secrets set (cloudflare, alchemy, better-auth, stream, twitch). - Lint/format: eslint + prettier (matches fangdash setup). Biome was removed.
- Brand: navy
#091533+ cyan#0FACED+ Bricolage Grotesque + Geist. Dark only. Wolf-themed but understated.assets/logos/howlcast-*.svg+apps/web/public/logos/*are the brand assets.
- Single broadcaster, single-tenant. No
/[username]route. The channel page IS the home page (/). - Two roles only: broadcaster + viewer. One
is_invitedboolean replaces all tier/sub logic. No mods role (broadcaster moderates via GetStream's built-in tools). - Den layout only. No theater, no editorial.
- Two Discord webhooks (public + private), DB-stored, edited from Dashboard → Notifications. No other notification channels.
- Single email template: "Private stream invite". No sub thank-you, raid alert, etc.
- No R2 emote proxy. Emote images load direct from provider CDNs (browser-cached). Only metadata is in KV.
- Twitch ID = single setup input. The first-run wizard at
/setupresolves it via Helix and writeschannelConfig.broadcasterTwitchId. - Cross-subdomain cookies are blocked on
*.workers.dev(Public Suffix List). Same-origin proxy via Next.js rewrites is the workaround. In prod (howlcast.tv+api.howlcast.tv), enablecrossSubDomainCookiesfor cross-origin sessions. - No
/signuproute. Setup creates the broadcaster account; viewers arrive via emailed magic-link invites only.allowSignupscolumn was dropped in 0004. - No hardcoded
mrdemonwolfuser references.MrDemonWolf, Inc.company branding in metadata is fine. Broadcaster's display name reads from DB.
- Channel page (home) — branded chrome, real GetStream player + chat (lazy-loaded), LIVE badge polling, panels grid, public/private mode pill.
/login— single-form sign-in (email+password primary, magic-link + passkey secondary, "use username instead" toggle)./invite/[code]— viewer onboarding. Signed-out: magic-link form. Signed-in: one-click accept → flipsprofiles.isInvited./popout/chat— chrome-free chat for OBS browser source.
3-step wizard, single page state machine:
- Twitch lookup → recap card with avatar + 7TV/BTTV/FFZ provider counts
- Email + password + display name (with strength meter, inline email validation)
- Visibility (Invite-only default)
Creates broadcaster account via auth.api.signUpEmail, writes profiles (broadcaster, verified, invited) + channelConfig row, sets session cookie, redirects to /dashboard. Locked once setupCompletedAt is set.
Sidebar shell with 3 sections:
- Live → Stream (
/dashboard): Provision call → Go live → End. RTMPS card + Title/visibility editor. - Channel → Panels (drag-equiv reorder + drawer editor), Emotes (merged grid + manual refresh), Invite emails (send/list/revoke), Stream key (full RTMPS detail), Notifications (two Discord webhooks UI), Chat (popout-capable).
- Server → Self-host status, Account (broadcaster profile).
Three-tier middleware: session → setupCompleted → role=broadcaster.
setup—getStatus(public query),lookup+commit(public mutations, locked after first run)channel—getInfo/getPanels/getEmotes(public),createCall/updateConfig/upsertPanel/deletePanel/reorderPanels/refreshEmotes(broadcaster)stream—isLive/getStreamCredentials/getViewerToken(public),getBroadcasterToken/provision/goLive/stopLive(broadcaster)admin—listWebhooks/upsertWebhook/createInvite/listInvites/acceptInvite/revokeInvite/updateProfile
/api/auth/*— better-auth handler/api/trpc/*— all routers/api/health—{ok:true}/api/webhooks/getstream— HMAC-verified Video webhook receiver. UpdateschannelConfig.liveStartedAt/EndedAt+ fans out Discord embeds.
Worker also has a 12h cron handler that pulls emotes from Twitch/7TV/BTTV/FFZ → EMOTES_KV.
For full history run git log --oneline. Highlights since Phase 5 wrap:
6b49085 phase 6: card primitive sweep + dashboard inline-style cleanup
6c0b155 phase 6: route channel page through ChannelLayout
8645e46 refactor: serve branding logo via next.js route + optimize <Image>
7441061 feat: stream branding logos through /api/branding/logo
b3f44ab feat(infra): bind NEXT_PUBLIC_PUBLIC_BUCKET_URL on web worker
d1bc6d7 phase 6: route auth + viewer pages through PageContainer
335b3c4 phase 6: extract shared LegalDocPage, dedupe privacy + terms
d4c057c phase 6: add layout shells (PageContainer, ChannelLayout, ContentSection)
c5bd186 phase 6: add 11 shadcn primitives to @howlcast/ui
3ed92b6 phase 6: add layout token scale to globals.css
dd5167c feat: invite-only by design — drop public mode end-to-end
8620704 docs: pre-Phase-6 code audit report (AUDIT.md)
98c51c4 feat(env): NEXT_PUBLIC_PUBLIC_BUCKET_URL via env package
c34b1a7 refactor(api): dedupe shared helpers, single drizzle per request
9d42c56 feat(config): centralize brand hex tokens
39196df refactor(db): drop unused user_bans table, index invites
02234fd fix(security): html-escape, rate-limit, webhook hardening
b44509f feat(api): add html-escape + kv throttle primitives
0383964 fix: lint pipeline + check-types coverage across all packages
1e415c0 fix(setup): client-side setup-completed redirect + private-gate height
258305d phase 6: getstream rewrite round 2
bb81a75 phase 6 polish: refresh UI to claude design v2 handoff
15277a1 phase 6 wrap: full ci test bundle + prod wipe + pro removal
341c4b1 phase 6: white-label + tiptap legal + viewer account + og images
ca20341 phase 6: stats page + streamer mode + error pages + privacy/terms
c1a9003 fix(stream): getstream frontend rewrite + webhook chat unblock
be0eac2 docs: phase 5 sweep — progress, resume, need_to_do
Phase 6 remaining:
- Hard live OBS test — real RTMPS push, verify webhook fires, Discord fanout fires, channel page goes live, Stats records the session (peak viewers, chat count, per-minute chat bars).
- Cross-browser sweep — Safari, Chrome, Firefox, iOS, Android. Watch for regressions in the new shells + Card surfaces + Stats charts.
- ✅ Operations runbook (
docs/operations.md) — DONE
Already done in Phase 6 (don't relitigate): Stats + own analytics, Streamer Mode, viewer /account, white-label, Tiptap legal editor, error boundaries, 404, loading skeletons, OG images, robots+sitemap, code-audit pass, brand-hex centralization, API helper dedupe, public-mode removal, layout token scale, shadcn primitives, layout shells, page migrations, Card sweep, inline-style cleanup, branding logo via Next.js route.
After Phase 6: Phase 7 is the Astro Starlight docs site at docs.howlcast.tv. See docs/docs-site.md.
These are things only Nathanial can do (account creation, paid setup, etc.):
- ✅ Push to GitHub — done (up through
be0eac2) - ✅ GH Actions secrets — all 10 set (Cloudflare, Alchemy, Better-Auth, Stream, Twitch)
- ✅ GetStream account + keys — done; webhook configured at Video & Audio dashboard
- ✅ Twitch dev app —
TWITCH_CLIENT_ID+TWITCH_CLIENT_SECRETset - Decide testing path — local dev (recommended) vs. wipe prod D1 to re-run
/setup. User has an existing prod account from earlier wizard test; can't re-run setup against prod without wiping. - Configure Discord webhooks — once user goes through Dashboard → Notifications and pastes URLs (no env work needed, all UI now)
- Resend domain verify — for real prod email delivery (currently falls through to console). Eventually.
- Buy
howlcast.tvdomain — unlocks custom-domain attach (Phase 1.21–1.23 deferred items).
Paste this single prompt to a fresh Claude Code session in the repo:
Read RESUME.md, PROGRESS.md, NEED_TO_DO.md, and CLAUDE.md.
Tell me where we left off, what the next concrete action is, and run a
`git status` + `git log --oneline -10` to verify state matches RESUME.md.
That's it. RESUME.md → PROGRESS.md → next action.
Read these files in order, then summarize what you understand and tell me the next concrete action:
- CLAUDE.md
- DESIGN-DECISIONS.md
- docs/architecture.md
- docs/build-plan.md (Phase 6 — polish & launch)
- PROGRESS.md
- docs/branding-spec.md (Phase 6 white-label / legal docs spec)
- RESUME.md (this file)
- NEED_TO_DO.md
Then list the design files in
design-handoff/project/and tell me which file you'll reference for which build phase.Wait for me to confirm before starting code work.