feat: hosted demo mode#172
Conversation
When ?prefill=demo is present in the URL, the email and password inputs are populated with demo@demo.local / demo via react-hook-form defaultValues. Gated only on the URL param — not on VF_DEMO_MODE — so it works on any instance.
Wrap telemetry, service-accounts, team, and users settings pages with a server-component guard that calls notFound() when VF_DEMO_MODE is true. All four pages were "use client" components, so each page.tsx is replaced with a thin async server wrapper and the original client component is kept as _client.tsx.
Rename VF_DEMO_MODE → NEXT_PUBLIC_VF_DEMO_MODE so Next.js inlines the
value into the client bundle at build time, eliminating the hydration
mismatch where the server rendered with demo-mode on but the client
hydrated with false (non-NEXT_PUBLIC_ vars are not available in the
client bundle).
is-demo-mode.ts now reads process.env.NEXT_PUBLIC_VF_DEMO_MODE directly
instead of going through the server-only env module. Tests updated to
use the new var name, the bare delete replaced with vi.stubEnv("", "")
so vi.unstubAllEnvs() restores it correctly, and the telemetry demo-mode
test gains an assertion that the DB was not queried (the early return is
before the prisma call).
Greptile SummaryThis PR adds a Confidence Score: 4/5Safe to merge; one P2 Zod schema inconsistency is worth fixing but is not a blocker. Only P2 findings present. The implementation is well-structured with no auth bypasses, no hydration mismatches, and correct server-side guards. The single comment is a minor Zod enum strictness issue that would surface only when the env var is set to an empty string. src/lib/env.ts — the NEXT_PUBLIC_VF_DEMO_MODE Zod enum entry. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
BUILD["Build image\nNEXT_PUBLIC_VF_DEMO_MODE=true"]
PROD["Production image\n(flag unset)"]
BUILD --> BANNER["DemoBanner rendered\nin dashboard layout"]
BUILD --> TELEMETRY["sendTelemetryHeartbeat\nreturns immediately"]
BUILD --> NAV["demoHidden nav items\nfiltered from sidebar"]
BUILD --> SIGNOUT["Sign-out button\nhidden in 3 locations"]
BUILD --> GUARD["Settings pages\nnotFound() on server"]
GUARD --> P404_T["/settings/telemetry → 404"]
GUARD --> P404_SA["/settings/service-accounts → 404"]
GUARD --> P404_TM["/settings/team → 404"]
GUARD --> P404_U["/settings/users → 404"]
PROD --> NOOP["All behaviors disabled\nno visible change"]
LOGIN["GET /login?prefill=demo"]
LOGIN --> PREFILL["Form pre-filled\ndemo@demo.local / demo"]
PREFILL --> AUTH["NextAuth credentials flow\n(unchanged)"]
AUTH -->|user exists| OK["Authenticated"]
AUTH -->|user missing| FAIL["Auth failure\n(prod instance)"]
|
11e5fec to
506b025
Compare
Summary
Adds
NEXT_PUBLIC_VF_DEMO_MODEenv flag and demo-mode UI behaviors so the same VectorFlow image can be built as a public hosted demo atdemo.terrifiedbug.com. Implements design atdocs/superpowers/specs/2026-04-25-vf-demo-design.md(gitignored, in main worktree).What's included
NEXT_PUBLIC_VF_DEMO_MODEenv flag (build-time inlined so client components see it consistently — avoids hydration mismatch)isDemoMode()helper atsrc/lib/is-demo-mode.tsreadingprocess.env.NEXT_PUBLIC_VF_DEMO_MODEdirectly (works in both server and client components)?prefill=demoquery param (URL-gated only — no-op in production because the demo user doesn't exist there)/settings/telemetry,/settings/service-accounts,/settings/team,/settings/users) return 404 when demo mode is active. Each refactored into a serverpage.tsx+ client_client.tsxso the guard cannot be bypassed.🌐 [Try the live demo →]CTA above the existing heroBehaviour
NEXT_PUBLIC_VF_DEMO_MODEdefaults to false / unsetNEXT_PUBLIC_VF_DEMO_MODE=trueshows banner, hides admin nav + sign-out, 404s admin settings, and never pings telemetrydemo.terrifiedbug.com/→ Pangolin redirects to/login?prefill=demo→ form pre-filled → one-click sign-in into a seeded demo dashboardImportant architecture note
NEXT_PUBLIC_*env vars are inlined at BUILD time in Next.js, not read at runtime. This means: a single VF image cannot be both prod and demo at runtime via env. The demo image must be built with the flag set. Trade-off accepted for the demo MVP — Stream B'svectorflow-demo-opsrepo will build VF from a sibling clone with the flag set, OR consume a separately-tagged demo image if VF's CI grows that capability.Test plan
NEXT_PUBLIC_VF_DEMO_MODEunset): no banner, all settings pages reachable, telemetry behaves normally, login form not pre-filledNEXT_PUBLIC_VF_DEMO_MODE=trueat build time): banner renders on every dashboard page,/settings/telemetry|service-accounts|team|usersreturn 404, sign-out button hidden in all 3 surfaces, telemetry heartbeat does not fire even iftelemetryEnabled=truein DB, demo nav links hidden in sidebar?prefill=demoquery param on login page populates email and password fields withdemo@demo.local/demo(works on both prod and demo builds — auth fails on prod because user doesn't exist)Out of scope
vectorflow-demo-opsrepo (Stream B, separate private repo)/api/demo/resetendpointPre-existing CI status (not caused by this branch)
Same baseline as
main: 5 pre-existing skipped tests indlp-vrl-integration.test.ts(Vector binary version) andagent-token.test.ts(bcrypt timeout) — both already worked around inmainviadescribe.skipIfand per-test timeouts.