LOCAL_DEV_SETUP — lock-integration-service
Bundle: SERVICE_OVERVIEW · TESTING_STRATEGY · DEPLOYMENT_TOPOLOGY
Cross-cutting: monorepo
pnpmworkspace setup; Node 20.x LTS; Docker Desktop; Electron 30+ for desktop integration.
A developer should be able to clone the monorepo, run one command, and have a fully working lock-integration-service with mock vendor adapters and a paired Electron desktop within 10 minutes. No real vendor network required for day-to-day work.
1. Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Node.js | 20.x LTS | Runtime |
| pnpm | 9.x | Workspace manager |
| Docker Desktop | latest | Postgres, Pub/Sub emulator |
gcloud CLI | latest | Pub/Sub emulator launcher |
git | 2.40+ | — |
| Optional: a Generic Wiegand USB encoder | — | Hardware-in-the-loop validation |
For Electron development on Windows: Visual Studio Build Tools (for serialport/node-hid native builds). On macOS: Xcode CLT. On Linux: build-essential + libudev-dev.
2. Bring-up (one command)
From the monorepo root:
pnpm install
pnpm dev:lock
This runs services/lock-integration-service/scripts/dev.ts, which:
- Starts Postgres 15 (
docker compose -f services/lock-integration-service/dev/docker-compose.yml up -d postgres). - Starts Pub/Sub emulator (
gcloud beta emulators pubsub start --project=melmastoon-dev). - Runs migrations (
pnpm --filter lock-integration-service db:migrate). - Seeds dev fixtures (one tenant
tnt_dev, two properties, mock vendor adapters for all four vendors). - Launches the API in watch mode (
tsx watch src/main.ts) onhttp://localhost:7090. - Launches the saga-runner in watch mode on
http://localhost:7091. - Launches the webhook receiver on
http://localhost:7092.
3. Mock vendor adapters
The simulators from TESTING_STRATEGY §3 double as dev-mode adapters. Activated by env:
LOCK_VENDOR_MODE=simulator
LOCK_VENDOR_SIMULATOR_LATENCY_MS=80
LOCK_VENDOR_SIMULATOR_FAILURE_RATE=0.02
Switch a single vendor to live sandbox by overriding per-vendor:
LOCK_VENDOR_TTLOCK_MODE=sandbox
LOCK_VENDOR_TTLOCK_CLIENT_ID=...
LOCK_VENDOR_TTLOCK_CLIENT_SECRET_REF=projects/.../secrets/dev-ttlock-sandbox/versions/latest
Sandbox mode requires a Google ADC profile with access to the dev Secret Manager project (melmastoon-dev), set up by gcloud auth application-default login.
4. Seeded fixtures
The seed script creates:
- Tenant
tnt_devwith 2 properties:ppt_dev_silk(TTLock + GenericWiegand on-prem mix),ppt_dev_pamir(Salto only). - 12 rooms across both properties, each with at least one
LockDevice. KeyKindPolicyper property (preferred orders covering the dev vendor mix).- 3 staff users with active shifts → triggers seeded master keys.
- 4 reservations spanning past, current, future windows.
VendorAdapterrows for all four vendors with simulator credentials.
5. CLI helpers
A pnpm lock:cli command wraps common workflows:
# issue a credential against the dev API
pnpm lock:cli issue --reservation rsv_dev_1 --kind mobile_app
# revoke
pnpm lock:cli revoke --credential key_dev_42 --reason checkout
# simulate a vendor webhook for a credential
pnpm lock:cli sim-webhook --vendor ttlock --kind issued --credential key_dev_42
# inspect inbox/outbox
pnpm lock:cli inbox tail
pnpm lock:cli outbox tail
6. Pairing the dev Electron desktop
pnpm --filter @melmastoon/desktop dev
In the running Electron window:
- Sign in as the dev tenant admin (
admin@dev.melmastoon). - Click "Pair this device" → device key generated, public key registered with
iam-service(also running locally perpnpm dev:iam). - Pair encoder: select the simulator encoder (or the real USB encoder if one is plugged in) — the desktop main process listens via
serialport/node-hidor the in-process simulator. - Mint an offline issuance certificate from the desktop (calls cloud REST against local API).
- Local SQLite mirror initialized via first sync pull.
You can now toggle airplane mode (or pnpm dev:lock:offline which blocks the cloud port) to test offline issuance flows.
7. Useful environment variables
NODE_ENV=development
PORT=7090
DATABASE_URL=postgres://lock_app:lock_app@localhost:5432/lock_dev
PUBSUB_PROJECT=melmastoon-dev
PUBSUB_EMULATOR_HOST=localhost:8085
LOCK_VENDOR_MODE=simulator
OFFLINE_ISSUANCE_SIGNING_KEY=local:./dev/keys/offline-signing-ed25519.pem
LOG_LEVEL=debug
LOG_PRETTY=true
TENANT_RLS_ENFORCEMENT=strict # surface RLS misses immediately
SAGA_RETRY_MAX=3 # tighter for fast feedback
8. Tests
pnpm --filter lock-integration-service test # unit + application + adapter (simulator)
pnpm --filter lock-integration-service test:integration # Testcontainers
pnpm --filter lock-integration-service test:contract # adapter contract suite
LIVE_VENDOR=true pnpm --filter lock-integration-service test:vendor:ttlock # opt-in real sandbox
9. Common issues
| Symptom | Cause | Fix |
|---|---|---|
serialport build fails on Windows | VS Build Tools missing | Install "Desktop development with C++" workload |
| Postgres connection refused | Docker Desktop not running | Start Docker Desktop, retry pnpm dev:lock |
| Pub/Sub emulator port collision | Another emulator running | gcloud beta emulators pubsub env-init to inspect; kill stale process |
| Local API returns RLS error | Forgot X-Tenant-Id header in request | Use pnpm lock:cli which sets it; or add header in your HTTP client |
| Offline issuance signature invalid | Re-mint cert: pnpm lock:cli mint-offline-cert --device dev_local |