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¶
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:
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):
- SST deploys a Lambda stub function to AWS. This is a tiny wrapper, not your actual code.
- SST creates an API Gateway V2 that routes all requests to the stub.
- SST opens a WebSocket connection from your machine to AWS AppSync.
- When someone hits the API Gateway URL, the stub publishes the request via AppSync.
- Your local machine receives the request, runs your FastAPI code, and publishes the response back.
- 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:
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:
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:
- Create a new read-only token in Cloudflare Dashboard:
- My Profile > API Tokens > Create Custom Token
- Zone - DNS: Read (Specific zone:
getreggie.io) -
Zone - Zone: Read (Specific zone:
getreggie.io) -
Update SSM:
-
Existing developer stages are unaffected — the token is only used during initial setup.
Rotating the CI/CD Cloudflare Token¶
- Create a new read-write token in Cloudflare Dashboard:
- Zone - DNS: Edit (Specific zone:
getreggie.io) -
Zone - Zone: Read (Specific zone:
getreggie.io) -
Update GitHub Actions secret
CLOUDFLARE_API_TOKEN
Adding SST Secrets¶
If a new secret is added to sst.config.ts:
- Set it for staging:
npx sst secret set NewSecret "value" --stage staging - Set it for production:
npx sst secret set NewSecret "value" --stage production - Add it to the
SECRETSarray inscripts/setup-dev-stage.sh - Existing developer stages need the secret added manually:
npx sst secret set NewSecret "value" --stage joe-dev
Next Steps¶
- CLAUDE.md -- critical patterns and conventions
- Architecture overview -- SST resource map
- Add your first endpoint -- tutorial
- ADR-004 -- why we split into services