Infrastructure — Local / Staging / Production
Three environments (local Docker Supabase, staging cloud, production cloud) + admin portal on Vercel + Expo Go / EAS APK for mobile.
Implementation plan: See IMPLEMENTATION_PLAN.md for the full v0.2 rollout (admin portal, content catalog schema, chapters, i18n, SDLC). This page documents the resulting environment topology.
Environment map
| Local | Staging | Production | |
|---|---|---|---|
| Git branch | feature/* |
staging |
main |
| Supabase | supabase start (Docker, :54321) |
umikora-staging (cloud) | umikora-production (cloud) |
| Admin portal | npm run dev (:3000) |
Vercel preview (auto on staging) |
Vercel production (auto on main) |
Mobile EXPO_PUBLIC_APP_ENV |
local |
staging |
production |
| Mobile target | Expo Go on LAN | Expo Go OR staging APK | Production APK |
| Android package | com.umikora.app.dev |
com.umikora.app.staging |
com.umikora.app |
| App display name | Umikora (Dev) | Umikora (Staging) | Umikora |
| GitHub deploy | n/a | Supabase Integration + Vercel | Supabase Integration + Vercel |
Git workflow
feature/* → PR (CI required) → staging → PR (CI required) → main
stagingandmainare protected branches — direct push is disabled; all changes via PR.- Each PR requires CI
testcheck to pass before merge (no approval required). - Merge is manual — auto-merge is disabled; a human must click “Merge pull request” on GitHub UI after testing. Agent / bot does not execute
gh pr merge. - Force push and branch deletion are disabled on protected branches.
- Push to
staging(via PR merge) triggers Supabase GitHub Integration (auto-deploys migrations + Edge Functions) and Vercel preview build of the admin portal. - Push to
main(via PR merge) triggers Supabase GitHub Integration and Vercel production deploy. - Mobile prod APK is built via
eas build --profile productionfrommainon release tags. - See CONTRIBUTING.md for the full workflow and branch naming rules.
Supabase GitHub Integration
Supabase is connected directly to the GitHub repo via Integrations (Dashboard → Settings → Integrations → GitHub).
- Staging project linked to
stagingbranch — auto-deployssupabase/migrations/+ Edge Functions. - Production project linked to
mainbranch — same. - PR preview databases are automatically created per PR.
- No GitHub Actions secrets needed for Supabase (Integration handles auth).
- CI tests still run via
.github/workflows/test.yml.
Vercel project (admin portal)
Configured via Vercel dashboard (one project per environment, or one project with branch-based env vars):
| Setting | Value |
|---|---|
| Root directory | apps/admin |
| Framework preset | Next.js |
| Build command | npm run build |
| Install command | npm install (workspaces) |
| Preview branch | staging → staging Supabase env vars |
| Production branch | main → production Supabase env vars |
Env vars per environment:
| Var | Where |
|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Vercel project env (preview / production) |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Vercel project env (preview / production) |
Supabase Dashboard (each project)
- Edge Functions → featured-feed → Secrets
APP_ENV=stagingorproduction
- Confirm Settings → API Keys → Publishable key and Project URL for mobile / EAS.
Local development
The recommended path uses local Docker Supabase + auto-generated .env.local files. See SETUP.md for the cross-platform (macOS + Windows) walkthrough.
npm install
npm run supabase:start # start local Supabase (Docker)
npm run setup:local # writes apps/admin/.env.local and apps/mobile/.env.local
# admin portal
cd apps/admin && npm run dev # http://localhost:3000
# mobile (in another terminal)
cd apps/mobile && npx expo start # Expo Go on LAN
scripts/setup-local.mjs reads supabase status and writes:
apps/admin/.env.local:NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 NEXT_PUBLIC_SUPABASE_ANON_KEY=<local anon key>apps/mobile/.env.local:EXPO_PUBLIC_APP_ENV=local EXPO_PUBLIC_SUPABASE_URL=http://<host LAN IP>:54321 EXPO_PUBLIC_SUPABASE_ANON_KEY=<local anon key>
Targeting staging or production from a device
To run a physical-device build against cloud staging/production, override .env.local or use EAS build profiles (see EAS Build section below).
EXPO_PUBLIC_APP_ENV=staging
EXPO_PUBLIC_SUPABASE_URL=https://xxxx.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=sb_publishable_...
Explore tab loads featured-feed Edge Function; offline falls back to mocks/featured.ts.
Manual Supabase CLI (optional)
supabase login
supabase link --project-ref <STAGING_REF>
supabase db push
supabase functions deploy featured-feed
EAS Build (Android APK)
Prerequisites: Expo account, eas-cli, project linked (eas init in apps/mobile).
Set Expo environment variables per build profile (staging / production):
EXPO_PUBLIC_SUPABASE_URL— Project URLEXPO_PUBLIC_SUPABASE_ANON_KEY— Publishable key (sb_publishable_...)
cd apps/mobile
eas build --profile staging --platform android
eas build --profile production --platform android
Staging and production APKs can be installed side by side (different package IDs).
Verify staging
- GitHub Action green after push to
staging. - Supabase Table Editor: 8 rows in
featured_items. - Curl (replace URL + Publishable key):
curl -X POST "https://<PROJECT>.supabase.co/functions/v1/featured-feed" \
-H "Authorization: Bearer <PUBLISHABLE_KEY>" \
-H "Content-Type: application/json" \
-d "{\"limit\":5,\"user_subject_tags\":[\"chemistry\"]}"
- Response
itemsorder should not matchpublished_atonly (editorial_boost affects rank). - Mobile Explore → tap card →
featured_eventshasclickrows.
Content catalog (v0.2)
The content catalog (modules → chapters → cards, pathways, translations) lives entirely in Supabase. The admin portal is the only writer; the mobile app is a read-only consumer (syncs to its SQLite mirror for offline reads). See IMPLEMENTATION_PLAN.md §3 for the schema, §4 for RLS, and §6 for mobile sync.
RLS roles (set via auth.users.app_metadata.role)
| Role | Capabilities |
|---|---|
anon |
SELECT on is_active=true content + all translations |
reviewer |
+ INSERT/UPDATE on translation tables |
editor |
+ Full CRUD on content tables |
admin |
+ user management, read audit trail |
The admin portal Server Actions always use the user’s session JWT (not service_role), so RLS enforces every write.
Audit trail
Every change to content / translation tables writes a row to content_revisions (triggers in migration 20260608000001_content_catalog.sql). The admin Dashboard and System pages render this feed.
Repo layout
scripts/setup-local.mjs One-shot local-env writer
apps/admin/ Next.js admin portal (v0.2+)
apps/mobile/src/data/ SQLite mirror, repositories, supabaseContentClient
packages/shared/ Types, SM-2, radar, featured scoring
supabase/migrations/ SQL schema + seed (content_catalog, featured_*)
supabase/functions/ Edge Functions (featured-feed, set-user-role)
.github/workflows/ CI + deploy
docs/ IMPLEMENTATION_PLAN.md, ARCHITECTURE.md, MOBILE.md, ...