Credentials
Every account, every secret, where each lives
A complete inventory of services + secrets the app depends on.
All credential values live in Bitwarden under the nxt-app@outlook.com collection. This document lists what exists; values stay in Bitwarden.
Identity owner
Every third-party service below is registered to nxt-app@outlook.com. Bitwarden seats are scoped to it.
Service inventory
Code + infrastructure
| Service | Purpose | Notes |
|---|---|---|
GitHub (wearenxt org) | Source code | Org-owner seat required for admin |
| Convex | Backend (production + dev deployments) | Dashboard at dashboard.convex.dev |
| Vercel | Web + docs hosting + analytics + Speed Insights | One Vercel team, multiple projects |
| Expo / EAS | Mobile build + OTA | Tied to the Expo organization |
Identity + auth
| Service | Purpose | Notes |
|---|---|---|
| WorkOS | AuthKit tenant — accounts, OAuth, magic link | One tenant covers mobile + web + docs |
| Apple Developer Program | iOS distribution | $99/yr renewal |
| Google Play Console | Android distribution | $25 one-time fee paid |
AI + data
| Service | Purpose | Notes |
|---|---|---|
| OpenAI | Per-school AI blurb (gpt-5.4-nano) | Org-level cost limit configured |
| College Scorecard API | Federal college data | Free, public, single API key for usage tracking |
| logo.dev | School logos | Free tier; attribution required |
| Serper | Google-image proxy for hero images | Pay-as-you-go, ~$0.30/1K queries |
Observability + product
| Service | Purpose | Notes |
|---|---|---|
| PostHog | Product analytics + GDPR erasure | Hosted EU tier (configurable) |
| Sentry | Error tracking | Wired in @sentry/nextjs + @sentry/react-native; DSN currently blank (off by default) |
| Vercel Analytics | Web traffic | Free tier |
| Vercel Speed Insights | Web Core Web Vitals | Free tier |
Optional / not-yet-active
| Service | Purpose | Status |
|---|---|---|
| Resend | Transactional email | Provisioned; not actively sending mail today |
| Cloudflare R2 | Object storage | Provisioned; no active buckets yet |
Files that live on disk (gitignored)
These three files are not in GitHub. They live in Bitwarden so they can be dropped into place on a fresh checkout.
| Path | Purpose |
|---|---|
apps/mobile/keys/AuthKey_68Y3V9WSG7.p8 | Apple ASC API key for non-interactive App Store submissions |
apps/mobile/keys/google-service-account.json | Google Play service account for non-interactive Play Store submissions |
apps/mobile/google-services.json | Firebase config for Android push notifications |
Server-side environment variables
Stored on the Convex deployment via npx convex env set <key> <value>. Inventory:
| Key | Purpose |
|---|---|
WORKOS_CLIENT_ID | WorkOS public client ID |
WORKOS_API_KEY | WorkOS server-side API key |
WORKOS_COOKIE_PASSWORD | 32+ char random string for cookie encryption |
WORKOS_REDIRECT_URI | OAuth callback URL |
WORKOS_WEBHOOK_SECRET | (Optional) webhook signature verification |
OPENAI_API_KEY | OpenAI key for the per-school blurb |
SCORECARD_API_KEY | Federal Scorecard usage key |
LOGO_DEV_PUBLISHABLE_KEY | logo.dev API key |
SERPER_API_KEY | Serper.dev key for hero images |
POSTHOG_API_HOST | PostHog host (region-specific) |
POSTHOG_PROJECT_ID | PostHog project ID |
POSTHOG_PERSONAL_API_KEY | PostHog personal API key — used for GDPR erasure path |
REPORT_ADMIN_EMAIL | (Optional) address notified on new content reports |
REVIEW_LOGIN_EMAILS | App Store / Play Store reviewer test accounts |
A full inventory with inline comments is at .env.example in the repo root.
Client-side environment variables
Set in each Vercel project's Environment Variables UI (web + docs) and via EAS secrets for mobile.
| Key | Where | Purpose |
|---|---|---|
NEXT_PUBLIC_CONVEX_URL | web, docs | Convex public URL for the React client |
NEXT_PUBLIC_WORKOS_REDIRECT_URI | web, docs | OAuth callback (NEXT_PUBLIC variant for middleware time) |
NEXT_PUBLIC_SITE_URL | web | Canonical URL for sitemap + opengraph |
EXPO_PUBLIC_CONVEX_URL | mobile | Convex URL for the React Native client |
EXPO_PUBLIC_WORKOS_CLIENT_ID | mobile | Public WorkOS client ID |
EXPO_PUBLIC_SITE_URL | mobile | Canonical web URL for deep linking |
NEXT_PUBLIC_SENTRY_DSN / EXPO_PUBLIC_SENTRY_DSN | all | Sentry DSN (blank = off) |
Rotation
No rotation policy is in place. Recommended cadence: WorkOS API key, OpenAI key, Serper key, and the Apple ASC API key on a six-month schedule.
Technical detail
Why one Bitwarden collection
Single-collection prevents the "where did I put the X key" tax. The trade-off: anyone who can see the collection can see every key.
Why no Doppler / Vault / 1Password
Considered. Bitwarden was chosen for (a) lowest add-friction for non-technical readers, (b) matching the existing operator setup. Migrating to another manager later doesn't require any code changes — env vars themselves live on Convex / Vercel / EAS, not in a secret manager NXT runs.
Webhook signing
WORKOS_WEBHOOK_SECRET enables HMAC verification of incoming WorkOS webhooks (webhook_events table dedupes by event ID). Unset = webhooks not verified. Today the secret is set; rotate at the WorkOS Dashboard → Webhooks UI if compromised.
Token storage on mobile
WorkOS session tokens are stored in expo-secure-store (Keychain on iOS, EncryptedSharedPreferences on Android). The mobile app never sees the user's password.