Skip to main content

Local Dev Setup

:::info Source Sourced from services/tenant-service/LOCAL_DEV_SETUP.md in the documentation repo. :::

1. Prerequisites

ToolVersion
Node.js20 LTS
pnpm9.x
Docker24.x

2. Quick Start

cd services/tenant-service
cp .env.example .env.local
pnpm install
docker compose -f docker-compose.dev.yml up -d # postgres, redis, nats
pnpm db:migrate
pnpm seed
pnpm dev

Service listens on http://localhost:3002.

3. Required Dependencies

ServicePortPurpose
postgres5432Schema tenant; has ltree extension
redis6379Policy bundle cache, authz decision cache
nats4222Events
identity-service (stub)3001Seed user lookup

4. Environment Variables (.env.example)

PORT=3002
LOG_LEVEL=debug
ENV=dev

DATABASE_URL=postgres://tenant:tenant@localhost:5432/tenant
DATABASE_POOL_MAX=20
DATABASE_RLS_ENFORCED=true

REDIS_URL=redis://localhost:6379/2
NATS_URL=nats://localhost:4222
NATS_STREAM=TENANT

POLICY_BUNDLE_SIGN_KMS_KEY_ID=alias/ghasi-tenant-policy-signer
POLICY_BUNDLE_REFRESH_SECONDS=60

DYNAMIC_GROUP_REEVAL_INTERVAL_MS=900000 # 15 min

FEATURE_FLAG_SERVICE_URL=http://localhost:3100

5. Seed Data

pnpm seed creates:

TenantSlugTypeRegionPlan
ACME Corpacmeorgusbusiness
Globexglobexorgeuteam
SoloAuthorsoloproviderusmarketplace-provider

Plus org units (Engineering, Sales), roles (from system defaults + custom CustomerSuccess), memberships (linking seeded users from identity-service).

6. Useful Commands

pnpm dev
pnpm test
pnpm test:integration # Testcontainers
pnpm policy:lint # Lint all ABAC predicates for tenant scope
pnpm policy:bundle # Build + sign policy bundle
pnpm seed
pnpm db:migrate
pnpm authz:check # CLI: POST /authz/check with seeded creds

7. Two-Tenant Isolation Test

pnpm test:isolation

Asserts:

  • User in acme cannot read globex org units / roles / memberships.
  • System role (tenantId: null) never grants access without ABAC ctx.tenant_id match.
  • Dynamic group query {member_of: "globex"} inside acme returns empty.

8. Debugging

  • Policy debugger: POST /api/v1/authz/explain returns decision tree with every predicate evaluated.
  • Dynamic group simulator: POST /api/v1/tenants/{tid}/dynamic-groups/{id}/simulate returns current members without persisting.