Skip to content

Developer Setup

Complete setup guide for new developers joining the Reggie team.

Prerequisites

Install these once on your machine:

brew install --cask orbstack      # Docker runtime (lightweight alternative to Docker Desktop)
brew install python@3.11          # Python for the API
brew install libpq && brew link libpq --force  # PostgreSQL client
brew install uv                   # Python package manager (used by SST for Lambda packaging)
brew install cloudflared          # Cloudflare tunnel for shareable dev links

You also need Node.js 20+ and npm (install via nvm or brew install node).

Quick Start (3 steps)

# 1. Clone and install
git clone https://github.com/getreggie/reggie.git
cd reggie
make setup

# 2. Set up your personal dev stage (one-time, interactive)
make setup-dev-stage

# 3. Start developing
make dev

That's it. Step 2 asks for your name and sets everything up automatically. Step 3 starts the full development environment with a shareable public URL.


Step-by-Step Guide

Step 1: Clone and Install

git clone https://github.com/getreggie/reggie.git
cd reggie
make setup    # Installs npm deps, Python venv, generates .env files

This creates: - node_modules/ for frontend dependencies - backend/venv/ for Python dependencies - .env files from .env.example templates

Step 2: Set Up Your Dev Stage

You need AWS credentials for this step. Get them from AWS IAM Identity Center (SSO).

# Set AWS credentials
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_SESSION_TOKEN="..."

# Run the interactive setup
make setup-dev-stage

The wizard will:

======================================
  REGGIE DEV STAGE SETUP
======================================

What's your first name? joe

  Stage name: joe-dev
  AWS identity: arn:aws:sts::947782089822:assumed-role/.../joe
  Fetching Cloudflare credentials from AWS...
  Cloudflare credentials loaded from SSM.

Copying secrets from staging to joe-dev...

  ✓  ClerkPublishableKey
  ✓  ClerkSecretKey
  ✓  ClerkJwtKey
  ✓  ClerkWebhookSecret
  ✓  R2Endpoint
  ✓  R2AccessKeyId
  ✓  R2SecretAccessKey
  ✓  StripeSecretKey
  ✓  StripeWebhookSecret
  ✓  ResendApiKey
  ✓  DvlaApiKey
  ✓  SecretKey

======================================
  SETUP COMPLETE
======================================

  Your stage: joe-dev
  Saved to:   .sst-stage

  Start developing:
    make dev

You only need to do this once. Your stage name is saved to .sst-stage (gitignored).

Step 3: Start Developing

make dev

This: 1. Starts your local Docker Postgres 2. Runs database migrations 3. Deploys lightweight Lambda stubs to AWS (~30 seconds) 4. Starts the SST multiplexer with tabs for: - Backend (FastAPI via Live proxy) - Web frontend (Next.js on localhost:3000) - Admin frontend (Next.js on localhost:3001)

You'll see an API URL like https://abc123.execute-api.eu-west-2.amazonaws.com — this is your personal API endpoint backed by your local machine.

Offline Fallback

If you don't have AWS credentials or need to work offline:

make dev-local    # Fully local: Docker Postgres + uvicorn + Next.js

This runs everything on localhost with no cloud dependency.


Sharing Your Work

Share with a teammate (via tunnel)

# In a separate terminal while make dev is running
make tunnel       # Shares your local API via a public URL
make tunnel-web   # Shares your local frontend via a public URL

cloudflared gives you a temporary public URL instantly. Send it to a colleague — they can access your running local instance. No account or configuration needed.

Share via SST (automatic)

When using make dev (which runs sst dev), the API Gateway URL is already a real public endpoint. Your colleague can hit it directly — the Lambda stub proxies requests to your machine via SST's Live mode.


Cleaning Up

When you're done with a dev stage (e.g., leaving the project, starting fresh):

sst remove --stage joe-dev   # Removes all AWS resources for your stage
rm .sst-stage                # Removes the persisted stage name

Lambda stubs cost fractions of a penny, but it's good practice to clean up unused stages.


Useful Commands

Command Description
make dev Start development (sst dev if configured, local otherwise)
make dev-local Force local-only development (no AWS)
make dev-stop Stop all running dev servers
make dev-status Check health of all services
make test-backend Run backend tests (SQLite, fast)
make test-backend-pg Run backend tests against PostgreSQL
make tunnel Share local API via public URL
make tunnel-web Share local frontend via public URL
make setup-dev-stage Set up your personal dev stage (one-time)
sst remove --stage <name> Clean up a dev stage

How It Works (Under the Hood)

This section explains the mechanisms behind the developer experience for long-term maintainability.

Architecture Overview

reggie/
  apps/
    web/                  # Next.js frontend (CloudFront + Lambda@Edge in prod)
    admin/                # Next.js admin dashboard (same)
  services/
    api/                  # FastAPI API (Lambda ZIP in prod, uvicorn locally)
    valuation/            # ML inference (Lambda container image)
    scraper/              # Web scraping (scheduled tasks)
  packages/
    api-client/           # TypeScript API client + generated types
    shared-python/        # Shared Python: models, schemas, database, config
  backend/                # Legacy path: tests, alembic migrations, venv
  sst.config.ts           # Infrastructure-as-code (SST v4)

Why sst dev Instead of make dev-local

Aspect make dev-local make dev (sst dev)
Backend uvicorn on localhost:8000 uvicorn on localhost, proxied via Lambda stub
Frontend Next.js on localhost:3000/3001 Same, via SST multiplexer
Database Local Docker Postgres Same (SST dev: block points to localhost)
Public URL None (localhost only) Real API Gateway URL
Shareable Only via make tunnel Automatic (Lambda stub URL)
AWS resources None Lambda stubs + API Gateway (pennies, no VPC)
Requires Docker Docker + AWS credentials

We chose sst dev as the default because it gives every developer a real public URL that teammates can access, while still running all code locally for fast iteration.

How sst dev Live Mode Works

When you run make dev (which calls sst dev --stage joe-dev):

  1. SST deploys a Lambda stub function to AWS. This is a tiny wrapper, not your actual code.
  2. SST creates an API Gateway V2 that routes all requests to the stub.
  3. SST opens a WebSocket connection from your machine to AWS AppSync.
  4. When someone hits the API Gateway URL, the stub publishes the request via AppSync.
  5. Your local machine receives the request, runs your FastAPI code, and publishes the response back.
  6. The stub receives the response and returns it to the caller.

Your code never leaves your machine. The Lambda stub is just a relay.

Why No VPC for Dev Stages

The previous architecture used ECS Fargate, which required a VPC + Cluster + ALB per stage. This hit the AWS 5-VPC limit and cost ~$30/month per idle stage.

With the Lambda architecture: - The API Lambda doesn't need a VPC — it calls external APIs (Clerk, Stripe, R2) over the public internet and invokes the Valuation Lambda via AWS SDK. - The Valuation Lambda is a pure function — it receives input and returns output, no database or network access needed. - In sst dev, Lambda stubs don't need a VPC either — they just relay to your machine. - VPC is only created for production and staging where RDS lives.

This is controlled in sst.config.ts:

const vpc = isDeployed ? new sst.aws.Vpc("ReggieVpc") : undefined;

How Secrets Work

SST manages secrets per-stage in AWS SSM Parameter Store (encrypted). Each stage (staging, production, joe-dev) has its own set of 12 secrets.

The problem: A new dev stage has no secrets. The staging stage has the right values, but reading them requires the Cloudflare provider (because staging's SST config loads it for DNS).

The solution: A shared read-only Cloudflare API token is stored in SSM. The setup script fetches it automatically — developers never need to create a Cloudflare account.

Developer                        AWS
   |                              |
   |  1. make setup-dev-stage     |
   |  2. AWS creds (already set)  |
   |                              |
   |--- GET /reggie/shared/cf-token -->|  SSM Parameter Store
   |<-- read-only CF token ---------|
   |                              |
   |--- sst secret get (staging) -->|  SST reads staging secrets
   |<-- 12 secret values ----------|  (uses CF token to load provider)
   |                              |
   |--- sst secret set (joe-dev) ->|  SST writes to joe-dev
   |<-- done ----------------------|
   |                              |
   |  3. Saves "joe-dev" to       |
   |     .sst-stage (local file)  |

After setup, the CF token is gone (never written to disk). Daily make dev uses only AWS credentials — the Cloudflare provider is conditionally excluded for dev stages.

The Cloudflare Provider Conditional

In sst.config.ts:

providers: {
  aws: { region: "eu-west-2" },
  ...(isDeployed ? { cloudflare: "6.13.0" } : {}),
},

This means: - production/staging: Cloudflare provider loads, SST can create DNS records (api.getreggie.io, etc.) - dev stages: Cloudflare provider is NOT loaded, no token needed

This single line is what makes make dev work without a Cloudflare account.

Two Cloudflare Tokens, Two Purposes

Token Permissions Stored in Used by
Developer token Zone:DNS:Read, Zone:Zone:Read AWS SSM (/reggie/shared/cloudflare-api-token) make setup-dev-stage — automatic, in-memory only
CI/CD token Zone:DNS:Edit, Zone:Zone:Read GitHub Actions secrets Production/staging deploys

The developer token is read-only (least privilege). It can read the staging configuration but cannot create or modify DNS records. This is intentional: developers only need to read secrets during one-time setup.

The CI/CD token has edit permissions because production/staging deploys need to create DNS records like api.getreggie.io.

SSM Parameter Paths

Parameter Type Purpose
/reggie/shared/cloudflare-api-token SecureString Read-only Cloudflare API token for developer tooling
/reggie/shared/cloudflare-account-id String Cloudflare account identifier (not secret)

Connection Pooling on Lambda vs Local

The database connection strategy adapts to the runtime:

# packages/shared-python/shared/database.py
_pool_size = int(os.environ.get("CONNECTION_POOL_SIZE", "10"))

if _pool_size == 0:
    # Lambda: NullPool — one connection per request, closed after.
    # RDS Proxy handles pooling externally when we add it later.
    engine = create_engine(url, poolclass=NullPool)
else:
    # Local dev / ECS: standard connection pool.
    engine = create_engine(url, pool_size=_pool_size, ...)

Lambda sets CONNECTION_POOL_SIZE=0 via the SST config. Local dev uses the default pool of 10. This is explicit configuration rather than runtime detection.

CORS on Lambda vs Local

CORS is handled at different layers depending on the environment:

  • Lambda: API Gateway V2 handles CORS (preflight responses don't invoke Lambda, saving cost)
  • Local dev: FastAPI's CORSMiddleware handles CORS
# services/api/app/main.py
if not os.environ.get("CORS_HANDLED_BY_GATEWAY"):
    app.add_middleware(CORSMiddleware, ...)

Lambda sets CORS_HANDLED_BY_GATEWAY=true via SST config.


Admin Reference

Rotating the Developer Cloudflare Token

If the token is compromised or expired:

  1. Create a new read-only token in Cloudflare Dashboard:
  2. My Profile > API Tokens > Create Custom Token
  3. Zone - DNS: Read (Specific zone: getreggie.io)
  4. Zone - Zone: Read (Specific zone: getreggie.io)

  5. Update SSM:

    aws ssm put-parameter \
      --name "/reggie/shared/cloudflare-api-token" \
      --type SecureString \
      --value "new-token" \
      --region eu-west-2 \
      --overwrite
    

  6. Existing developer stages are unaffected — the token is only used during initial setup.

Rotating the CI/CD Cloudflare Token

  1. Create a new read-write token in Cloudflare Dashboard:
  2. Zone - DNS: Edit (Specific zone: getreggie.io)
  3. Zone - Zone: Read (Specific zone: getreggie.io)

  4. Update GitHub Actions secret CLOUDFLARE_API_TOKEN

Adding SST Secrets

If a new secret is added to sst.config.ts:

  1. Set it for staging: npx sst secret set NewSecret "value" --stage staging
  2. Set it for production: npx sst secret set NewSecret "value" --stage production
  3. Add it to the SECRETS array in scripts/setup-dev-stage.sh
  4. Existing developer stages need the secret added manually: npx sst secret set NewSecret "value" --stage joe-dev

Next Steps