Skip to main content

LOCAL_DEV_SETUP — lock-integration-service

Bundle: SERVICE_OVERVIEW · TESTING_STRATEGY · DEPLOYMENT_TOPOLOGY

Cross-cutting: monorepo pnpm workspace 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

ToolVersionPurpose
Node.js20.x LTSRuntime
pnpm9.xWorkspace manager
Docker DesktoplatestPostgres, Pub/Sub emulator
gcloud CLIlatestPub/Sub emulator launcher
git2.40+
Optional: a Generic Wiegand USB encoderHardware-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:

  1. Starts Postgres 15 (docker compose -f services/lock-integration-service/dev/docker-compose.yml up -d postgres).
  2. Starts Pub/Sub emulator (gcloud beta emulators pubsub start --project=melmastoon-dev).
  3. Runs migrations (pnpm --filter lock-integration-service db:migrate).
  4. Seeds dev fixtures (one tenant tnt_dev, two properties, mock vendor adapters for all four vendors).
  5. Launches the API in watch mode (tsx watch src/main.ts) on http://localhost:7090.
  6. Launches the saga-runner in watch mode on http://localhost:7091.
  7. 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_dev with 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.
  • KeyKindPolicy per 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.
  • VendorAdapter rows 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:

  1. Sign in as the dev tenant admin (admin@dev.melmastoon).
  2. Click "Pair this device" → device key generated, public key registered with iam-service (also running locally per pnpm dev:iam).
  3. Pair encoder: select the simulator encoder (or the real USB encoder if one is plugged in) — the desktop main process listens via serialport/node-hid or the in-process simulator.
  4. Mint an offline issuance certificate from the desktop (calls cloud REST against local API).
  5. 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

SymptomCauseFix
serialport build fails on WindowsVS Build Tools missingInstall "Desktop development with C++" workload
Postgres connection refusedDocker Desktop not runningStart Docker Desktop, retry pnpm dev:lock
Pub/Sub emulator port collisionAnother emulator runninggcloud beta emulators pubsub env-init to inspect; kill stale process
Local API returns RLS errorForgot X-Tenant-Id header in requestUse pnpm lock:cli which sets it; or add header in your HTTP client
Offline issuance signature invalidRe-mint cert: pnpm lock:cli mint-offline-cert --device dev_local

10. Cross-references