Hidden truth must be inaccessible by construction, not merely hidden in the UI or protected by prompts.
Never expose through client-callable queries/actions:
caseSolutions,- raw private
caseEvents, npcScripts,- unreleased forensic outputs,
- generation repair/debug payloads containing solution truth.
Do not send these documents to the client and filter fields client-side.
The NPC LLM must never receive the full case solution.
buildNpcContext may include only:
- this NPC's script,
- facts this NPC knows/experienced,
- allowed public world facts,
- this NPC/session conversation history,
- this NPC/session memory.
Never include another NPC's private script.
Use Clerk for player authentication in V1.
Use the Clerk user identity for:
- authorization,
- ownership/membership checks,
- rate limiting,
- abuse correlation.
Display identity is a user-chosen nickname.
- room code is shareable and allows joining while capacity permits,
- reconnect secret identifies a specific player slot,
- store only a cryptographic hash of reconnect secret,
- raw secret returned once,
- use high-entropy value independent of room code,
- never log raw reconnect secrets.
Every session-scoped client call:
- authenticate Clerk user identity,
- verify membership/reconnect state,
- verify requested entity belongs to the session's case,
- verify visibility/discovery rules,
- verify session is in an allowed state.
Never trust client-supplied case IDs/entity relationships without checking ownership.
Once case.status = ready, ordinary runtime functions cannot update generated-case truth tables.
Only explicit developer/admin recovery paths, if ever added later, may bypass this rule.
Treat all LLM output as untrusted structured data.
- validate schemas,
- validate entity references,
- validate lengths/enums,
- re-check server-side permissions,
- never execute arbitrary LLM-proposed state mutations,
- NPC text can only become persisted dialogue/memory, not game truth.
Bound:
- nickname length,
- NPC message length/frequency,
- clue-board note length,
- edge labels,
- public-record search query length,
- case-close explanation lengths.
Sanitize rendering; do not render user HTML.
Public sign-up can burn LLM budget.
At minimum rate-limit:
- case creation/generation,
- NPC messages,
- case-close judging,
- reconnect/join attempts.
Use per-Clerk-user and global limits.
Before public launch, add bot protection such as Cloudflare Turnstile to expensive public entry points, especially case creation.
Provider/API keys remain server-side in Convex environment configuration.
No model/provider secret may enter Next.js client bundles.
llmCalls stores metadata by default, not complete prompts/transcripts.
Sentry should avoid collecting reconnect secrets or other sensitive tokens.
Full NPC conversations are game content and may be persisted as part of session state; retention should follow the 7-day abandoned-session policy unless product requirements later differ.