Skip to main content

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:

  1. Generate dummy GCP service account credentials (for Firebase Admin SDK init)
  2. Start Spanner emulator + create instance/database
  3. Start Firebase Auth emulator
  4. Build and load all OCI images into Docker
  5. Start all 8 microservices as Docker containers
  6. Create a super admin Firebase user in the admin tenant
  7. Insert the super admin into the database with mgmt_super_admin role

Default Super Admin

The dev script auto-seeds a super admin user:

FieldValue
Emailadmin@peakpos.co
Passwordadmin123
Tenantadmin-tenant-local
IAM Rolemgmt_super_admin

Use these credentials to sign in to the Support portal at http://localhost:5174.

What's Running

ServicePortURL
Spanner emulator9010gRPC
Spanner emulator REST9020http://localhost:9020
Firebase Auth emulator9099http://localhost:9099
Firebase Emulator UI4000http://localhost:4000
merchant-api8081http://localhost:8081
management-api8082http://localhost:8082
tx-bundler8083http://localhost:8083
terminal-api8084http://localhost:8084
terminal-onboarding8085http://localhost:8085
status8086http://localhost:8086
email-api8087http://localhost:8087
sms-api8088http://localhost:8088
Customer portal5173http://localhost:5173
Support portal5174http://localhost:5174
peakpos.co5175http://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.

VariableValuePurpose
SPANNER_EMULATOR_HOSTlocalhost:9010Routes PgAdapter to Spanner emulator
DATABASE_URLjdbc:cloudspanner:/projects/pinpoint-payments/instances/test/databases/pinpointposSpanner JDBC URL
GOOGLE_CLOUD_PROJECTpinpoint-paymentsMatches production project ID
FIREBASE_AUTH_EMULATOR_HOSTlocalhost:9099Routes Firebase Admin SDK to emulator
APP_CHECK_ENABLEDfalseDisables App Check locally
IDENTITY_TENANT_IDuser-tenant-local / admin-tenant-localFirebase multi-tenant ID
PGADAPTER_PORT9432-9436Unique PgAdapter port per service
GOOGLE_APPLICATION_CREDENTIALS/tmp/credentials.jsonAuto-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.

  1. Open the Support portal at http://localhost:5174
  2. Sign in with admin@peakpos.co / admin123
  3. 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 host so they can reach the Spanner and Firebase emulators on localhost. Each service gets a unique SERVER_PORT. Only the Spanner-backed services get a PGADAPTER_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 unique PGADAPTER_PORT (9432-9437) to avoid conflicts on the shared host network. The status and sms-api services 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 IamSeeder auto-creates mgmt_super_admin, mgmt_admin, and mgmt_user roles on startup. The dev script then assigns the super admin user to mgmt_super_admin.
  • OCI images are built by rules_img (no Dockerfiles). bazel run //apps/microservices/<name>:image_tarball loads each image into Docker.
  • Firebase multi-tenancy: merchant-api, terminal-api, tx-bundler, and email-api validate Firebase tokens against the user-tenant-local tenant. management-api validates Firebase tokens against the admin-tenant-local tenant. 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 with SPANNER_EMULATOR_HOST set.
  • Credentials are auto-generated: scripts/dummy-credentials.json is created on first run with a fresh RSA key. It's gitignored. For local auth emulator compatibility, the generated Firebase project_id is local (so backend token verification matches emulator-issued aud=local tokens).