Skip to content

Latest commit

 

History

History
301 lines (236 loc) · 16.8 KB

ENVIRONMENTS.md

File metadata and controls

301 lines (236 loc) · 16.8 KB

Environments & Deployment

How Wrapper's pieces fit together across local, dev, and prod, and how each one deploys. If you only want to test locally, jump to Local development.

Components

Component What it is Hosting Code
backend Convex functions, auth (Better Auth), billing (Autumn) Convex packages/backend
relay Bun + Hono WebSocket relay for remote sessions Fly.io apps/relay
web Next.js site Vercel apps/web
cli The wrapper CLI (host/attach) GitHub Releases / Homebrew apps/cli
docs Mintlify documentation source docs.wrapper.sh apps/docs
mobile Native iPhone/iPad viewer TestFlight public beta apps/mobile

Public domains

Domain Purpose Current state
wrapper.sh Canonical production website, auth, installer, legal, and support origin Live on the production Vercel app
docs.wrapper.sh Canonical public Mintlify documentation origin Live

Environment model

Two long-lived environments, driven by branch:

local        your machine         convex dev (sleek-echidna) + ws://localhost:8080 + localhost:3000
  |
dev branch   ->  DEV  env    ->   Convex sleek-echidna-539 · wrapper-relay-dev · Vercel Preview
main branch  ->  PROD env    ->   Convex confident-fox-458  · wrapper-relay-prod · Vercel Production

Real Convex URLs:

dev prod
.cloud https://sleek-echidna-539.convex.cloud https://confident-fox-458.convex.cloud
.site https://sleek-echidna-539.convex.site https://confident-fox-458.convex.site

Released CLI binaries treat an unset NODE_ENV as production and default to https://confident-fox-458.convex.cloud, wss://wrapper-relay-prod.fly.dev, and https://wrapper.sh. Source development must set NODE_ENV=development and explicit dev endpoints.

How each component picks dev vs prod

  • web (Vercel): Vercel's Git integration decides by the Production Branch setting. Set Production Branch = main. Then main → Production, every other branch (incl. dev) and PR → Preview. No GitHub workflow needed.

  • backend + relay (GitHub Actions): the deploy workflows pick a GitHub Environment from the branch, so secrets/variables resolve per environment:

    environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'dev' }}
Workflow Trigger Purpose
.github/workflows/deploy-backend.yml push dev/main on packages/backend/** Convex (convex deploy)
.github/workflows/deploy-relay.yml push dev/main on apps/relay/** Fly app (flyctl deploy --app)
.github/workflows/ci.yml every PR/push audit, lint, format, types, tests, web/docs builds, relay smoke (no deploy)
.github/workflows/synthetic-health.yml schedule/manual canonical web/legal/docs and dev/prod relay health

Web deployment is not in GitHub Actions. CI still builds the web app, while Vercel deploys it directly from Git.

Operational ownership, SLO alerts, backup/restore, secret rotation, and the recommended branch protection settings are in OPERATIONS.md.

Env var matrix

backend: Convex (set per deployment with bunx convex env set KEY value)

Key dev (sleek-echidna) prod (confident-fox)
ENVIRONMENT development production
SITE_URL Vercel dev/preview URL https://www.wrapper.sh
BETTER_AUTH_SECRET dev secret prod secret
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET dev OAuth app prod OAuth app
APPLE_CLIENT_ID / APPLE_CLIENT_SECRET dev Services ID + secret prod Services ID + secret
APPLE_NOTIFICATION_AUDIENCES primary dev App ID sh.wrapper.mobile
AUTUMN_SECRET_KEY am_sk_test_… (sandbox) am_sk_live_… (production)
AUTUMN_WEBHOOK_SECRET Autumn Svix signing secret Autumn Svix signing secret
WRAPPER_AUTUMN_PRO_PLAN_ID pro pro
WRAPPER_AUTUMN_PRO_YEARLY_PLAN_ID pro_yearly pro_yearly
RESEND_API_KEY Resend test/live key Resend live key
RESEND_WEBHOOK_SECRET Resend Svix signing secret Resend Svix signing secret
FROM_EMAIL_ADDRESS Wrapper <notifications@wrapper.sh> same
FROM_EMAIL_ADDRESS_PERSONAL Can from Wrapper <can@wrapper.sh> same
WRAPPER_AUTUMN_RELAY_SHARE_FEATURE_ID can_share_relay can_share_relay
WRAPPER_RELAY_ENTITLEMENT_FAIL_OPEN unset (fail-closed) unset (fail-closed); emergency 1 only
APNS_KEY_ID / APNS_TEAM_ID / APNS_KEY_P8 optional; phone alerts stay off without these same
APNS_BUNDLE_ID sh.wrapper.mobile sh.wrapper.mobile
WRAPPER_RELAY_HOST_TICKET_TTL_MS etc. optional (has defaults) optional (has defaults)
RELEASE_NOTES_INGEST_SECRET random secret, same value as the web app random secret, same value as the web app

RELEASE_NOTES_INGEST_SECRET gates releaseNotes:subscribe (the landing-page release-notes signup). The web app's route handler adds a hashed client address for per-visitor rate limits and sign the call with this secret; without it anyone could call Convex directly and choose their own bucket. When unset (local dev) the check is skipped and only the global and per-email windows apply.

relay: Fly secrets (flyctl secrets set KEY=value --app <app>)

Key wrapper-relay-dev wrapper-relay-prod
CONVEX_URL https://sleek-echidna-539.convex.cloud https://confident-fox-458.convex.cloud
PORT 8080 (in fly.toml) 8080 (in fly.toml)

web: Vercel (Project, then Settings, then Environment Variables)

Vercel scopes: Production = main, Preview = dev + PRs, Development = local vercel dev only. These values are baked at build time):

Key Production Preview Development
NEXT_PUBLIC_CONVEX_URL …confident-fox-458.convex.cloud …sleek-echidna-539.convex.cloud …sleek-echidna-539.convex.cloud
NEXT_PUBLIC_CONVEX_SITE_URL …confident-fox-458.convex.site …sleek-echidna-539.convex.site …sleek-echidna-539.convex.site
NEXT_PUBLIC_APPLE_AUTH_ENABLED true after Apple OAuth is configured true after Apple OAuth is configured false unless testing Apple OAuth
NEXT_PUBLIC_IOS_APP_URL TestFlight public join URL until App Store same, or empty for docs fallback empty uses the mobile viewer guide
NEXT_PUBLIC_IOS_APP_LABEL optional CTA override optional CTA override optional CTA override
NEXT_PUBLIC_POSTHOG_KEY unused for now; empty keeps PostHog web off empty empty
NEXT_PUBLIC_POSTHOG_HOST unused while the key is empty https://telemetry.wrapper.sh https://telemetry.wrapper.sh
RELEASE_NOTES_INGEST_SECRET same value as prod Convex same value as dev Convex optional

NEXT_PUBLIC_* is baked at build time. After creating a TestFlight public link, set Production NEXT_PUBLIC_IOS_APP_URL to https://testflight.apple.com/join/<id> and redeploy the web app. Until that value is set, landing CTAs stay Get iOS viewer with a Beta tag and open the mobile viewer guide.

GitHub Environments (dev, production): backend and relay only

Name Kind dev production
CONVEX_DEPLOY_KEY secret sleek-echidna deploy key confident-fox deploy key
FLY_API_TOKEN secret ✓ ✓
FLY_APP_NAME var wrapper-relay-dev wrapper-relay-prod

One-time setup checklist

  1. Vercel (web): import the repo, set Root Directory = apps/web and Production Branch = main. Add the env vars from the web table above (Production / Preview / Development). If the build fails resolving the bun catalog: versions, set the Install Command to run at the repo root (cd ../.. && bun install).

  2. Convex env: set the backend vars on both deployments and create a deploy key each (Dashboard → Settings → Deploy Keys):

    cd packages/backend
    # dev (against sleek-echidna, e.g. `bunx convex env set --preview-name` or via dashboard)
    bunx convex env set SITE_URL https://<vercel-dev-url>
    # prod (against confident-fox)
    bunx convex env set SITE_URL https://www.wrapper.sh
  3. Fly (relay): two apps plus secrets:

    flyctl apps create wrapper-relay-dev
    flyctl apps create wrapper-relay-prod
    flyctl secrets set CONVEX_URL=https://sleek-echidna-539.convex.cloud --app wrapper-relay-dev
    flyctl secrets set CONVEX_URL=https://confident-fox-458.convex.cloud --app wrapper-relay-prod
    # FLY_API_TOKEN: `flyctl tokens create deploy` -> GitHub env secret (both envs)

    Retire any previous Fly app only after both replacement environments pass their smoke checks and no configuration or traffic references the old app.

  4. GitHub Environments: create dev and production, then fill the table above. Restrict dev deployments to the dev branch and production deployments to main; apply the repository ruleset specified in OPERATIONS.md.

  5. OAuth apps: configure each provider callback against the matching web SITE_URL. Production callbacks are:

    • https://www.wrapper.sh/api/auth/callback/github
    • https://www.wrapper.sh/api/auth/callback/google
    • https://www.wrapper.sh/api/auth/callback/apple

    Put each provider client id/secret in the matching Convex deployment env. For Apple, create a Sign in with Apple-enabled primary App ID (sh.wrapper.mobile), associate a Services ID with www.wrapper.sh, register the exact Apple return URL above, and generate APPLE_CLIENT_SECRET as Apple's signed client-secret JWT. Rotate that JWT before its configured expiration. Never commit the .p8 key or paste it into chat.

    Register this production server-to-server notification URL on the primary App ID:

    https://confident-fox-458.convex.site/api/auth/apple/notifications
    

    Set APPLE_NOTIFICATION_AUDIENCES=sh.wrapper.mobile on the matching Convex deployment. If Apple's tested notification token uses both the primary App ID and Services ID as audiences, provide both as a comma-separated allowlist. The endpoint verifies Apple's RS256 signature, issuer, audience, expiry, and replay id before invalidating sessions or deleting Apple-only accounts.

    Register Autumn and Resend webhooks against the matching Convex .site origin. Production URLs:

    https://confident-fox-458.convex.site/webhook/autumn
    https://confident-fox-458.convex.site/webhook/resend
    

    Verify wrapper.sh in Resend and set RESEND_API_KEY, RESEND_WEBHOOK_SECRET, FROM_EMAIL_ADDRESS, and FROM_EMAIL_ADDRESS_PERSONAL on both Convex deployments. Without RESEND_API_KEY, transactional sends are skipped.

    Generate and pipe the Apple client secret without placing it in shell history:

    export APPLE_TEAM_ID="<10-character-team-id>"
    export APPLE_CLIENT_ID="<services-id>"
    export APPLE_KEY_ID="<sign-in-with-apple-key-id>"
    export APPLE_PRIVATE_KEY_PATH="$HOME/secure/AuthKey_<key-id>.p8"
    
    bun run apple:client-secret |
      (cd packages/backend && bunx convex env set --prod APPLE_CLIENT_SECRET)
    cd packages/backend
    bunx convex env set --prod APPLE_CLIENT_ID "$APPLE_CLIENT_ID"
    bunx convex env set --prod APPLE_NOTIFICATION_AUDIENCES sh.wrapper.mobile

    Repeat against the intended development deployment before enabling the Vercel Preview Apple flag. The generator caps client-secret lifetime at 180 days; schedule rotation before expiry.

  6. Autumn: push config to both (not automatic, the key decides the env):

    cd packages/backend
    bunx atmn push          # sandbox (dev)  -> am_sk_test_ key
    bunx atmn push --prod   # production     -> am_sk_live_ key

    After a price change, move remaining $20 monthly subscribers to $15 or yearly in Autumn so they match the public list price.

  7. Docs: apps/docs is connected to Mintlify at https://docs.wrapper.sh. Keep DNS, TLS, and representative page requests healthy after navigation or domain changes.

  8. iOS TestFlight public beta: upload a Release build (prod Convex and relay), add it to an External testing group, complete Beta App Review, then Enable Public Link. Set Vercel Production NEXT_PUBLIC_IOS_APP_URL to that testflight.apple.com/join/… URL and redeploy. Do not put an Internal-only invite on the website.

Once done, a push to dev updates matching dev backend/relay paths and creates a Vercel Preview. A merge to main updates matching production backend/relay paths and Vercel Production. CLI, docs, and mobile releases use their separate release or hosting processes.

Local development

Four terminals:

# 1) backend (Convex dev deployment + live codegen)
cd packages/backend && bunx convex dev

# 2) relay
cd apps/relay && CONVEX_URL="https://sleek-echidna-539.convex.cloud" bun run dev

# 3) web (optional, for auth/onboarding UI)
cd apps/web && bun run dev            # http://localhost:3000

# 4) cli
cd apps/cli && bun run index.ts auth login
bun run index.ts shell-host

packages/backend/.env.local holds your local Convex + AUTUMN_SECRET_KEY. Point the CLI at local with apps/cli/.env.local (copy from .env.example).

Test the Pro / relay-share gate

  1. In the host shell, press Ctrl+\ then s to share.

  2. As a free user you should see "Relay sharing requires Pro" + a checkout URL and "Press Enter to open the browser" (URL only if Stripe is connected in the Autumn sandbox; otherwise the generic message, still correctly denied). Press Enter to open Stripe checkout, the same way wrapper auth login opens the device-approval URL.

  3. Grant Pro (Stripe test card 4242 4242 4242 4242, or attach pro in the Autumn dashboard), retry → sharing succeeds and a viewer can attach:

    bun run index.ts attach --id <session-id>