Local Development Guide
Run the full PinpointPOS stack locally: Spanner emulator, Firebase Auth emulator, all 8 microservices (as Docker containers), and website dev servers.
Prerequisites
- Docker
- Bazel 9
- Node.js + pnpm (for website dev servers)
- Firebase CLI (
npm install -g firebase-tools) - PostgreSQL client (
psql) — for super admin seeding via PgAdapter - Python 3 (for UUID generation)
- OpenSSL (for auto-generating dummy credentials)
Quick Start
# Start everything (infra + services + websites)
./scripts/dev.sh
# Or start selectively:
./scripts/dev.sh infra # Spanner + Firebase emulators
./scripts/dev.sh services # Infra + all 8 microservices + seed super admin
./scripts/dev.sh websites # Website dev servers only
On first run, the script will automatically:
- Generate dummy GCP service account credentials (for Firebase Admin SDK init)
- Start Spanner emulator + create instance/database
- Start Firebase Auth emulator
- Build and load all OCI images into Docker
- Start all 8 microservices as Docker containers
- Create a super admin Firebase user in the admin tenant
- Insert the super admin into the database with
mgmt_super_adminrole
Default Super Admin
The dev script auto-seeds a super admin user:
| Field | Value |
|---|---|
admin@peakpos.co | |
| Password | admin123 |
| Tenant | admin-tenant-local |
| IAM Role | mgmt_super_admin |
Use these credentials to sign in to the Support portal at http://localhost:5174.
What's Running
| Service | Port | URL |
|---|---|---|
| Spanner emulator | 9010 | gRPC |
| Spanner emulator REST | 9020 | http://localhost:9020 |
| Firebase Auth emulator | 9099 | http://localhost:9099 |
| Firebase Emulator UI | 4000 | http://localhost:4000 |
| merchant-api | 8081 | http://localhost:8081 |
| management-api | 8082 | http://localhost:8082 |
| tx-bundler | 8083 | http://localhost:8083 |
| terminal-api | 8084 | http://localhost:8084 |
| terminal-onboarding | 8085 | http://localhost:8085 |
| status | 8086 | http://localhost:8086 |
| email-api | 8087 | http://localhost:8087 |
| sms-api | 8088 | http://localhost:8088 |
| Customer portal | 5173 | http://localhost:5173 |
| Support portal | 5174 | http://localhost:5174 |
| peakpos.co | 5175 | http://localhost:5175 |
Environment Variables
Most env vars match the Terraform config (infra/terraform/microservices.tf). Spanner paths keep the production-style project name pinpoint-payments.
| Variable | Value | Purpose |
|---|---|---|
SPANNER_EMULATOR_HOST | localhost:9010 | Routes PgAdapter to Spanner emulator |
DATABASE_URL | jdbc:cloudspanner:/projects/pinpoint-payments/instances/test/databases/pinpointpos | Spanner JDBC URL |
GOOGLE_CLOUD_PROJECT | pinpoint-payments | Matches production project ID |
FIREBASE_AUTH_EMULATOR_HOST | localhost:9099 | Routes Firebase Admin SDK to emulator |
APP_CHECK_ENABLED | false | Disables App Check locally |
IDENTITY_TENANT_ID | user-tenant-local / admin-tenant-local | Firebase multi-tenant ID |
PGADAPTER_PORT | 9432-9436 | Unique PgAdapter port per service |
GOOGLE_APPLICATION_CREDENTIALS | /tmp/credentials.json | Auto-generated dummy creds in containers (project_id=local for Firebase emulator token verification) |
Management Commands
./scripts/dev.sh # Start everything
./scripts/dev.sh services # Start infra + microservices (Docker containers)
./scripts/dev.sh websites # Start website dev servers
./scripts/dev.sh infra # Start Spanner + Firebase emulators
./scripts/dev.sh stop # Stop everything
./scripts/dev.sh status # Show what's running
./scripts/dev.sh logs <svc> # Tail logs for a service container
Authentication
Firebase Auth runs via the emulator. The super admin is auto-seeded.
- Open the Support portal at http://localhost:5174
- Sign in with
admin@peakpos.co/admin123 - Or open the Firebase Emulator UI at http://localhost:4000 to manage users
# Health check (no auth)
curl http://localhost:8081/health
# Authenticated request
TOKEN="..." # From frontend dev tools or emulator
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8081/api/v1/orgs/{orgId}/stores/{storeId}/products?page=0&size=10"
Build Verification
# Compile check (fastest)
bazel build //apps/microservices/merchant-api:core
# Build all microservices
bazel build //apps/microservices/...
# Unit tests (no emulator needed)
bazel test //apps/microservices/merchant-api:core_test
bazel test //apps/microservices/...
Architecture Notes
- Docker containers with
--network host: All microservices run as Docker containers using--network hostso they can reach the Spanner and Firebase emulators on localhost. Each service gets a uniqueSERVER_PORT. Only the Spanner-backed services get aPGADAPTER_PORT. - PgAdapter is embedded in the Spanner-using microservices (
merchant-api,management-api,terminal-api,terminal-onboarding,tx-bundler,email-api). It translates PostgreSQL wire protocol to Spanner gRPC. Each of these containers gets a uniquePGADAPTER_PORT(9432-9437) to avoid conflicts on the shared host network. Thestatusandsms-apiservices do not use Spanner/PgAdapter. - Schema auto-creation:
SqlDelightDrivers.ensureSchema()creates all tables on first startup using Spanner DDL batch (START BATCH DDL/RUN BATCH). This only works via PgAdapter -> Spanner, not plain PostgreSQL. - IAM seeding: management-api's
IamSeederauto-createsmgmt_super_admin,mgmt_admin, andmgmt_userroles on startup. The dev script then assigns the super admin user tomgmt_super_admin. - OCI images are built by
rules_img(no Dockerfiles).bazel run //apps/microservices/<name>:image_tarballloads each image into Docker. - Firebase multi-tenancy: merchant-api, terminal-api, tx-bundler, and email-api validate Firebase tokens against the
user-tenant-localtenant. management-api validates Firebase tokens against theadmin-tenant-localtenant. terminal-onboarding and sms-api do not use Firebase Auth. - Do NOT use
SPRING_PROFILES_ACTIVE=local— it activates a PostgreSQL DataSource that can't run Spanner DDL. Use the default profile withSPANNER_EMULATOR_HOSTset. - Credentials are auto-generated:
scripts/dummy-credentials.jsonis created on first run with a fresh RSA key. It's gitignored. For local auth emulator compatibility, the generated Firebaseproject_idislocal(so backend token verification matches emulator-issuedaud=localtokens).