Skip to content

feat: LOCAL_ONLY mode for local development without Firebase/GCP#3696

Open
evand wants to merge 16 commits intomanifoldmarkets:mainfrom
evand:fix/local-dev-v2
Open

feat: LOCAL_ONLY mode for local development without Firebase/GCP#3696
evand wants to merge 16 commits intomanifoldmarkets:mainfrom
evand:fix/local-dev-v2

Conversation

@evand
Copy link
Contributor

@evand evand commented Feb 19, 2026

Summary

Adds a LOCAL_ONLY=true mode that enables fully local Manifold development using only Supabase (Docker), without Firebase credentials or GCP access. This makes it much easier for new contributors to get started.

  • Run API server + web frontend against local Supabase
  • X-Local-User header for auth bypass (no Firebase JWT needed)
  • Auto-login in web frontend with configurable test user
  • Create markets, place bets, browse profiles — all locally

Changes

Backend API (backend/api/src/serve.ts)

  • Skip Firebase init, Secret Manager, and METRIC_WRITER when LOCAL_ONLY=true
  • Use env vars instead of Secret Manager for secrets

Auth bypass (backend/api/src/helpers/endpoint.ts)

  • Accept X-Local-User header as trusted auth in LOCAL_ONLY mode
  • Never fall through to Firebase when LOCAL_ONLY is set

Supabase connection (backend/shared/src/supabase/init.ts)

  • Support SUPABASE_HOST/SUPABASE_PORT env vars for local postgres

Frontend (web/components/auth-context.tsx, web/lib/supabase/db.ts)

  • LOCAL_ONLY auto-login fetches user from local API
  • Support NEXT_PUBLIC_SUPABASE_URL/KEY env vars for local Supabase

Schema loading (backend/supabase/load-local-schema.sh)

  • Three-pass function loading to resolve circular dependencies
  • Strips leakproof keyword (local postgres user isn't superuser)

SQL fixes for local dev

  • Fix constraint primary key syntax in 20 auto-generated schema files
  • Fix trailing comma in answers.sql
  • Fix malformed comments in seed.sql
  • Create missing status_type enum for mod_reports.sql
  • Wrap migrations in safe DO blocks for fresh databases

Scheduler (backend/scheduler/src/index.ts)

  • Skip Firebase, Secret Manager, METRIC_WRITER in LOCAL_ONLY mode

Documentation

  • docs/local-development.md — comprehensive setup guide
  • .env.local.template files for api, scheduler, and web

Credits

Incorporates fixes from @GabrielleVivissi's contributions to #3644:

  • NEXT_PUBLIC_SUPABASE_URL/KEY env var support
  • Synchronous local user ID setting in auth context
  • Local Supabase client configuration

Supersedes #3644 (rebased on current main).

Test plan

  • npx supabase start + ./load-local-schema.sh creates all 111+ tables
  • API: GET /v0/user/testuser returns user
  • API: GET /v0/slug/will-this-test-pass returns market with pool/probability
  • API: GET /v0/me with X-Local-User header returns authenticated user
  • API: GET /v0/search-markets returns results
  • Web: Homepage loads at localhost:3000
  • Web: User profile page loads without errors

🤖 Generated with Claude Code

Evan Daniel and others added 9 commits February 18, 2026 20:10
Loads all schema SQL files into a local Supabase instance in the
correct dependency order (seed → functions → core tables → rest → views).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When LOCAL_ONLY=true is set in the environment:
- API server skips Firebase init, Secret Manager, and GCP metrics
- Scheduler skips Firebase init, Secret Manager, and GCP metrics
- Supabase direct client connects via SUPABASE_HOST/SUPABASE_PORT
  env vars instead of cloud instance IDs
- createClient() accepts full URLs (not just instance IDs) for local
  Supabase connections
- .env.local files are now gitignored

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend:
- endpoint.ts accepts X-Local-User header in LOCAL_ONLY mode,
  bypassing Firebase JWT validation

Frontend (common/util/api.ts):
- setLocalOnlyUserId() sets a user ID sent as X-Local-User header
  on all API calls

Frontend (auth-context.tsx):
- LOCAL_ONLY mode auto-logins as NEXT_PUBLIC_LOCAL_TEST_USER
- Fetches user from local API instead of Firebase auth
- Sets local user ID synchronously before async fetch

Frontend (supabase/db.ts, admin-db.ts):
- Support NEXT_PUBLIC_SUPABASE_URL/KEY for local Supabase connection

Incorporates fixes from GabrielleVivissi: Supabase client URL support,
immediate user ID set in auth-context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- docs/local-development.md: comprehensive setup guide
- .env.local.template files for API, scheduler, and web frontend
  with default Supabase keys from `npx supabase start`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap migration SQL in DO blocks with exception handlers so they
skip gracefully when tables don't exist yet (during local dev
where schema is loaded after supabase start).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The spaced-out comment delimiters `/ * ... * /` are not valid SQL.
Replace with standard `/* ... */` block comments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- isProd() returns false early in LOCAL_ONLY mode (avoids admin.app())
- stripe-endpoints.ts: lazy-init Firestore (was module-level admin.firestore())
- endpoint.ts: fix TypeScript cast for local user credentials
- Fix 20 SQL files with invalid `constraint primary key` syntax
  (should be just `primary key`)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When LOCAL_ONLY=true and no X-Local-User header is present, throw
a clean 401 instead of calling admin.auth() which crashes since
Firebase isn't initialized. This lets MaybeAuthedEndpoint and
optional-auth typedEndpoint paths handle unauthenticated requests
gracefully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three-pass function loading to handle:
1. functions.sql internal dependency (add_creator_name_to_description
   depends on extract_text_from_rich_text_json defined later in file)
2. Circular dependency between functions.sql and table SQL files

Also fixes:
- Trailing comma in answers.sql CREATE TABLE statement
- Missing status_type enum for mod_reports.sql
- Strips `leakproof` keyword (local postgres user isn't superuser)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Feb 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dev Ready Ready Preview, Comment Feb 19, 2026 6:24am

Request Review

@vercel
Copy link

vercel bot commented Feb 19, 2026

Someone is attempting to deploy a commit to the Manifold Markets Team on Vercel.

A member of the Team first needs to authorize it.

Evan Daniel and others added 6 commits February 18, 2026 21:58
Private user data may not include blockedUserIds/blockedByUserIds
arrays (e.g. in local dev with minimal test data). Use optional
chaining to prevent TypeError.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
API calls from hooks fire before the async useEffect in auth-context
sets the local user ID, causing 401 "Missing X-Local-User header"
errors. Fix by initializing localOnlyUserId from
NEXT_PUBLIC_LOCAL_TEST_USER_ID at module load time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- seed-local-data.sql: Complete test data with all JSONB fields the
  frontend expects (probChanges, description, createdTime, closeTime,
  blockedUserIds, etc.)
- Updated docs/local-development.md with correct SQL, proper ordering,
  NEXT_PUBLIC_LOCAL_TEST_USER_ID documentation, reset/troubleshooting
- Exclude seed-local-data.sql from load-local-schema.sh table loop

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The autogenerated table .sql files define CREATE TRIGGER before
CREATE FUNCTION, so triggers silently fail on first load. Add Phase 5b
to re-run table files containing triggers after all functions exist.

Also fix seed-local-data.sql:
- Add collectedFees to contract JSONB (bet panel crashes without it)
- Enable CASH token in system_trading_status
- Set isAdvancedTrader on test user (exposes limit order UI)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant