Skip to main content

LOCAL_DEV_SETUP — bff-backoffice-service

Sibling: DEPLOYMENT_TOPOLOGY · TESTING_STRATEGY · DATA_MODEL · SYNC_CONTRACT

1. Prerequisites

ToolVersionNotes
Node.js20.x LTSengines.node = "20.x"
pnpm9.xWorkspace package manager
Docker DesktoplatestFor Postgres + Redis + Pub/Sub emulator
Docker Composev2Bundled
Google Cloud SDKlatestFor impersonating SAs against staging
mkcertlatestLocal TLS for DPoP / cookie / SSE
gh CLIlatestPR helpers
(optional) Electron desktop checkoutlatestTo exercise full Electron ↔ BFF integration locally

2. One-time setup

gh repo clone ghasitech/ghasi-melmastoon
cd ghasi-melmastoon
pnpm install --frozen-lockfile

mkcert -install
mkcert -cert-file ./certs/local.pem -key-file ./certs/local-key.pem \
'backoffice.local.melmastoon.test' localhost

cp services/bff-backoffice-service/.env.example services/bff-backoffice-service/.env.local

Edit .env.local:

NODE_ENV=development
PORT=8083
LOG_LEVEL=debug

DATABASE_URL=postgres://bff_backoffice:bff_backoffice@localhost:5443/bff_backoffice
REDIS_CACHE_URL=redis://localhost:6393/0
REDIS_SESSION_URL=redis://localhost:6394/0
PUBSUB_PROJECT_ID=local
PUBSUB_EMULATOR_HOST=localhost:8085

BFF_BACKOFFICE_PEPPER=dev-pepper-do-not-ship
BFF_BACKOFFICE_SSE_SIGNING_KEY=dev-sse-key-do-not-ship

UPSTREAM_IAM_BASE_URL=http://localhost:8089
UPSTREAM_TENANT_BASE_URL=http://localhost:8094
UPSTREAM_PROPERTY_BASE_URL=http://localhost:8092
UPSTREAM_RESERVATION_BASE_URL=http://localhost:8096
UPSTREAM_INVENTORY_BASE_URL=http://localhost:8095
UPSTREAM_PRICING_BASE_URL=http://localhost:8091
UPSTREAM_HOUSEKEEPING_BASE_URL=http://localhost:8101
UPSTREAM_MAINTENANCE_BASE_URL=http://localhost:8102
UPSTREAM_BILLING_BASE_URL=http://localhost:8098
UPSTREAM_LOCK_BASE_URL=http://localhost:8099
UPSTREAM_AI_BASE_URL=http://localhost:8100
UPSTREAM_NOTIFICATION_BASE_URL=http://localhost:8103
UPSTREAM_SYNC_BASE_URL=http://localhost:8104
UPSTREAM_ANALYTICS_BASE_URL=http://localhost:8105

OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318

3. Stand up dependencies

docker compose -f services/bff-backoffice-service/docker-compose.dev.yml up -d
# Brings up:
# postgres (5443 → 5432) seeded with sample tenants, devices, sessions
# redis-cache (6393 → 6379)
# redis-session (6394 → 6379)
# pubsub-emulator (8085)
# wiremock-iam (8089) — issues dev DPoP-compatible tokens
# wiremock-tenant (8094)
# wiremock-property (8092)
# wiremock-reservation (8096)
# wiremock-inventory (8095)
# wiremock-pricing (8091)
# wiremock-housekeeping (8101)
# wiremock-maintenance (8102)
# wiremock-billing (8098)
# wiremock-lock (8099) — simulates ttlock/salto/assa
# wiremock-ai (8100) — fake AI suggestions
# wiremock-notification (8103)
# wiremock-sync (8104) — simulates sync handshake
# wiremock-analytics (8105)
# otel-collector + jaeger (16686)

4. Run the service

pnpm --filter @ghasi/service-bff-backoffice dev
# starts in watch mode on https://backoffice.local.melmastoon.test:8083

Smoke check:

curl -k https://backoffice.local.melmastoon.test:8083/api/health/ready

# Mint a dev session via the wiremock-iam stub:
pnpm --filter @ghasi/service-bff-backoffice dev:dpop-session
# prints: ACCESS_TOKEN=... REFRESH_TOKEN=... DEVICE_ID=dev_dev_local

# Try a dashboard fetch:
curl -k \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "DPoP: $(pnpm --filter @ghasi/service-bff-backoffice dev:dpop-proof GET https://backoffice.local.melmastoon.test:8083/api/bff/backoffice/v1/dashboard)" \
-H "X-Device-Id: ${DEVICE_ID}" \
-H "X-App-Version: 1.4.2-dev" \
-H "X-App-Platform: linux" \
-H "X-Property-Id: prop_dev_01" \
https://backoffice.local.melmastoon.test:8083/api/bff/backoffice/v1/dashboard?propertyId=prop_dev_01

Trace appears in Jaeger UI at http://localhost:16686.

5. Common commands

CommandPurpose
pnpm --filter @ghasi/service-bff-backoffice devRun with hot reload
pnpm --filter @ghasi/service-bff-backoffice test:unitVitest unit tests
pnpm --filter @ghasi/service-bff-backoffice test:integrationVitest + Testcontainers
pnpm --filter @ghasi/service-bff-backoffice test:contractPact consumer + provider
pnpm --filter @ghasi/service-bff-backoffice test:e2e:localPlaywright against local stack
pnpm --filter @ghasi/service-bff-backoffice lintESLint + import-boundary rules
pnpm --filter @ghasi/service-bff-backoffice typechecktsc --noEmit
pnpm --filter @ghasi/service-bff-backoffice migrateDrizzle migrations
pnpm --filter @ghasi/service-bff-backoffice migrate:make <name>New migration
pnpm --filter @ghasi/service-bff-backoffice seedSeed dev tenants + devices + sessions
pnpm --filter @ghasi/service-bff-backoffice openapiRegenerate OpenAPI
pnpm --filter @ghasi/service-bff-backoffice dev:dpop-sessionMint dev DPoP session
pnpm --filter @ghasi/service-bff-backoffice dev:dpop-proof <method> <url>Sign single DPoP proof
pnpm --filter @ghasi/service-bff-backoffice dev:mfa-attest <scope>Mint dev MFA attestation token

6. Working against real upstreams (staging)

gcloud auth application-default login
gcloud auth print-identity-token --impersonate-service-account=bff-backoffice-sa@melmastoon-stage.iam.gserviceaccount.com

# Override in .env.local:
UPSTREAM_RESERVATION_BASE_URL=https://reservation.staging.melmastoon.internal
# ... etc
USE_GOOGLE_ID_TOKEN=true

7. Working with the real Electron desktop

If you have @ghasi/app-desktop-backoffice checked out:

# In the desktop checkout:
pnpm install
pnpm dev:point-at-local-bff # rewrites the API base URL to https://backoffice.local.melmastoon.test:8083
pnpm electron:dev

The desktop main process will:

  1. Walk through device enrollment against wiremock-iam (one-shot).
  2. Persist refresh token + device key into the OS keychain (keytar).
  3. Sign DPoP proofs for every request.
  4. Open SSE to the local BFF.

You can now exercise full UI ↔ BFF flows.

8. Seed data

pnpm seed creates:

  • 3 tenants (tnt_kabul, tnt_herat, tnt_mazar)
  • 5 properties (3 in tnt_kabul, 1 each in others)
  • 8 operators with varied roles
  • 10 devices distributed across operators
  • 25 reservations (today + tomorrow)
  • 15 housekeeping tasks
  • 6 maintenance work orders
  • 8 active AI suggestions
  • 5 raised alerts
  • Pre-baked DPoP key pairs for dev sessions
  • Pre-baked MFA TOTP secret (pnpm dev:mfa-attest <scope> uses it)

9. Useful local URLs

URLPurpose
https://backoffice.local.melmastoon.test:8083/api/health/readyReadiness
http://localhost:16686Jaeger trace explorer
http://localhost:8089/__adminwiremock-iam admin
http://localhost:8099/__adminwiremock-lock admin (toggle vendor outcomes)
http://localhost:8104/__adminwiremock-sync admin

10. Resetting state

docker compose -f services/bff-backoffice-service/docker-compose.dev.yml down -v
docker compose -f services/bff-backoffice-service/docker-compose.dev.yml up -d
pnpm --filter @ghasi/service-bff-backoffice migrate
pnpm --filter @ghasi/service-bff-backoffice seed

11. Troubleshooting

SymptomLikely causeFix
MELMASTOON.IAM.DPOP_INVALIDLocal DPoP cache staleRestart compose or pnpm dev:dpop-session to mint fresh
MELMASTOON.IAM.SESSION_EXPIREDAccess token TTL 15 min lapsedRe-mint via pnpm dev:dpop-session
MELMASTOON.BFF.BACKOFFICE.MFA_REQUIRED on lock revokeNo MFA token in bodypnpm dev:mfa-attest lock_revoke and pass mfaAttestationToken
MELMASTOON.SYNC.VERSION_BLOCKEDLocal desktop appVersion below floorEdit bff-backoffice-flags Memorystore key to lower floor
MELMASTOON.BFF.UPSTREAM_UNAVAILABLE from lockwiremock-lock configured to failReset via __admin/mappings
Trace missingOTel collector not startedRestart compose; check Jaeger health
Pact verification fails locallyStale broker pactpnpm test:contract -- --pull-pacts
Cookie not set on curlSecure flag rejected on plain HTTPUse https + mkcert cert
keytar errors on Linux desktop devlibsecret missingsudo apt install libsecret-1-dev gnome-keyring