Skip to content

ADR: Miniapp Multi-Environment Deployment Model

Status: Accepted Date: 2026-01-29 Deciders: Founders, Engineering Team

Context

Current State

The Emerge miniapp is a Farcaster Mini App deployed to Vercel. Current deployment:

EnvironmentDomainStatus
Productionemerge-mini-app.vercel.appWorking, but using Vercel subdomain
Stagingstg.q-miniapp.emerge.pizzaDeployed, but missing Farcaster credentials
Testtest.q-miniapp.emerge.pizzaHas credentials
Locallocalhost:3338Uses EMBOT37 dev credentials

Problems

  1. Production uses Vercel subdomain - Want custom domain for branding
  2. Staging can't function as miniapp - Missing Farcaster accountAssociation
  3. Local development friction - No ngrok setup for testing Farcaster features
  4. No clear environment separation - Farcaster agents (bots) not isolated per environment

Farcaster Constraints

Each domain requires its own signed accountAssociation because:

  • The signature cryptographically includes the domain
  • Domain in manifest must exactly match hosting FQDN
  • Signatures cannot be reused across domains

Decision

1. Four-Environment Model

EnvironmentDomainPurposeDeploy Trigger
Local*.ngrok.pizzaDeveloper hot-reload testingManual
Stagingstg.q-miniapp.emerge.pizzaPre-production testingPush to staging
Testtest.q-miniapp.emerge.pizzaQA/automated testingPush to test
Productionemerge.pizza (TBD)Live usersPush to main

2. Domain Strategy

Production: Add custom domain (e.g., app.emerge.pizza or emerge.pizza)

  • Configure in Vercel dashboard
  • Generate new Farcaster accountAssociation for custom domain
  • Keep emerge-mini-app.vercel.app as fallback/redirect

Staging/Test: Keep existing *.q-miniapp.emerge.pizza subdomains

  • Already configured in DNS
  • Generate Farcaster credentials for staging (currently missing)

Local Development: Ngrok with stable subdomain

  • Use emerge-mini-app-latest.ngrok.pizza (already in defaults)
  • Generate Farcaster credentials for ngrok domain
  • Developers share the same ngrok credentials (or each dev gets own subdomain)

3. Farcaster Credentials Per Environment

Each environment needs its own set in secrets:

# Production (custom domain)
SECRET_FARCASTER_PROD_HEADER=...
SECRET_FARCASTER_PROD_PAYLOAD=...
SECRET_FARCASTER_PROD_SIGNATURE=...

# Staging
SECRET_FARCASTER_STG_HEADER=...
SECRET_FARCASTER_STG_PAYLOAD=...
SECRET_FARCASTER_STG_SIGNATURE=...

# Test
SECRET_FARCASTER_TEST_HEADER=...
SECRET_FARCASTER_TEST_PAYLOAD=...
SECRET_FARCASTER_TEST_SIGNATURE=...

# Local/Ngrok
SECRET_FARCASTER_NGROK_HEADER=...
SECRET_FARCASTER_NGROK_PAYLOAD=...
SECRET_FARCASTER_NGROK_SIGNATURE=...

4. Farcaster Agent Isolation

Each environment should have its own Farcaster bot account:

EnvironmentBot AccountPurpose
Production@emergebotReal user interactions
Staging@emergebot-stgPre-production testing
Test@emergebot-testQA automation
Local@embot37 (existing)Developer testing

This prevents:

  • Test casts appearing in production feeds
  • Development activity polluting analytics
  • Accidental production actions from staging

5. Vercel Project Configuration

Single Vercel project with environment-based deployments:

Vercel Project: emerge-miniapp
├── Production: main branch → custom domain
├── Preview: staging branch → stg.q-miniapp.emerge.pizza
└── Preview: test branch → test.q-miniapp.emerge.pizza

Environment variables set per Vercel environment (Production, Preview, Development).

Implementation

Phase 1: Fix Staging Credentials (Immediate)

  1. Generate Farcaster accountAssociation for stg.q-miniapp.emerge.pizza
    • Use Warpcast mobile (Settings → Developer → Domains) or web tool
    • Sign with the staging bot account
  2. Add to secrets:
    SECRET_FARCASTER_STG_HEADER=...
    SECRET_FARCASTER_STG_PAYLOAD=...
    SECRET_FARCASTER_STG_SIGNATURE=...
  3. Update miniapp.env staging section:
    [staging]
    FARCASTER_HEADER=${SECRET_FARCASTER_STG_HEADER}
    FARCASTER_PAYLOAD=${SECRET_FARCASTER_STG_PAYLOAD}
    FARCASTER_SIGNATURE=${SECRET_FARCASTER_STG_SIGNATURE}

Phase 2: Setup Ngrok for Local Development

  1. Get stable ngrok subdomain (ngrok paid plan or free .ngrok.pizza)
  2. Generate Farcaster accountAssociation for ngrok domain
  3. Add to secrets and update [default] section
  4. Document ngrok setup in README

Local dev workflow:

bash
# Terminal 1: Start ngrok
ngrok http 3338 --domain=emerge-mini-app-latest.ngrok.pizza

# Terminal 2: Start dev server
pnpm dev:miniapp

Phase 3: Add Production Custom Domain

  1. Choose domain (e.g., app.emerge.pizza)
  2. Add domain in Vercel dashboard
  3. Configure DNS (CNAME or A record)
  4. Generate Farcaster accountAssociation for new domain
  5. Update production credentials in secrets
  6. Set up redirect from old Vercel domain

Phase 4: Create Environment-Specific Bot Accounts

  1. Create Farcaster accounts:
    • @emergebot-stg for staging
    • Keep @embot37 for local dev
  2. Configure Neynar webhooks per environment
  3. Update bot credentials in each environment section

Updated miniapp.env Structure

env
NAMESPACE=MINIAPP

[default]
# Local development with ngrok
URL=https://emerge-mini-app-latest.ngrok.pizza
INGRESS=https://emerge-mini-app-latest.ngrok.pizza
FARCASTER_HEADER=${SECRET_FARCASTER_NGROK_HEADER}
FARCASTER_PAYLOAD=${SECRET_FARCASTER_NGROK_PAYLOAD}
FARCASTER_SIGNATURE=${SECRET_FARCASTER_NGROK_SIGNATURE}
BADGE=LOCAL
# ... other defaults

[staging]
URL=https://stg.q-miniapp.emerge.pizza
INGRESS=https://stg.q-miniapp.emerge.pizza
FARCASTER_HEADER=${SECRET_FARCASTER_STG_HEADER}
FARCASTER_PAYLOAD=${SECRET_FARCASTER_STG_PAYLOAD}
FARCASTER_SIGNATURE=${SECRET_FARCASTER_STG_SIGNATURE}
BADGE=STAGING
BYPASS_MAINTENANCE=true

[test]
URL=https://test.q-miniapp.emerge.pizza
INGRESS=https://test.q-miniapp.emerge.pizza
FARCASTER_HEADER=${SECRET_FARCASTER_TEST_HEADER}
FARCASTER_PAYLOAD=${SECRET_FARCASTER_TEST_PAYLOAD}
FARCASTER_SIGNATURE=${SECRET_FARCASTER_TEST_SIGNATURE}
BADGE=TEST

[production]
URL=https://app.emerge.pizza  # New custom domain
INGRESS=https://app.emerge.pizza
FARCASTER_HEADER=${SECRET_FARCASTER_PROD_HEADER}
FARCASTER_PAYLOAD=${SECRET_FARCASTER_PROD_PAYLOAD}
FARCASTER_SIGNATURE=${SECRET_FARCASTER_PROD_SIGNATURE}
BADGE=
BYPASS_MAINTENANCE=false

Consequences

Positive

  • Full miniapp functionality in all environments - Each has valid Farcaster credentials
  • Safe testing - Bot activity isolated per environment
  • Professional production domain - Custom domain for branding
  • Local Farcaster testing - Ngrok enables testing miniapp features locally
  • Clear separation - Each environment is fully independent

Negative

  • Credential management overhead - 4 sets of Farcaster credentials to manage
  • Multiple bot accounts - Need to create/maintain separate Farcaster accounts
  • Ngrok dependency - Local dev requires ngrok for Farcaster testing

Neutral

  • Vercel remains host - Good fit for Next.js, preview deploys work well
  • DNS configuration required - One-time setup for custom domain

Alternatives Considered

1. Single Farcaster Account for All Environments

Rejected because:

  • Credentials are domain-specific (can't reuse)
  • Would pollute production with test activity
  • No isolation for bot actions

2. Railway Instead of Vercel

Rejected because:

  • Vercel optimized for Next.js
  • Preview deploys work well
  • No compelling reason to migrate

3. Skip Local Farcaster Testing

Rejected because:

  • Can't test miniapp features without valid manifest
  • Would slow development iteration

References

Released under the MIT License.