Skip to main content

Helix Platform Monorepo

Version: 1.0.0
Status: Foundation Complete - Ready for Service Implementation
Architecture: Microservices in Nx Monorepo with CLS & WorkOS RBAC


Overview

Helix is CleverChain's unified, multi-tenant microservices platform that serves as an "operating system" layer for all CleverChain products (CleverScreen, KIRA, VERA). This monorepo contains all backend microservices, shared libraries, database schemas, and infrastructure code.

Key Principles

The monorepo is a DEVELOPMENT structure.
Containers contain PRODUCTION artifacts.

  • CLS Architecture: Continuation-Local Storage (nestjs-cls) for context throughout request lifecycle
  • WorkOS RBAC: Roles managed by WorkOS, included in JWT tokens (NOT stored in our databases)
  • 3-Tier Databases: Physical tenant isolation with jurisdiction-based database servers
  • No Hardcoded Config: Service URLs and credentials from environment variables

Current State

✅ Phase 1,2,3: Foundation Complete

Infrastructure

  • ✅ Nx monorepo structure
  • ✅ Docker Compose configuration
  • ✅ Terraform infrastructure modules
  • ✅ TypeScript configuration with path aliases
  • ✅ ESLint & Prettier setup

Database Schemas

  • Tier 1 (Central DB): Platform metadata, NO PII, NO roles
  • Tier 2 (Tenant DB): Shared tenant data WITH PII, physical isolation
  • Tier 3 (Product DBs): Service-specific schemas (when services built)

Shared Libraries (14 libraries)

  • Types - Platform type definitions
  • Interfaces - Core interface shapes
  • Middleware - 5 middleware with CLS (Auth, Tenant, Product, Logging, RateLimit)
  • Guards - 4 guards with WorkOS RBAC (JwtAuth, Roles, Scopes, TenantAccess)
  • Decorators - CLS extractors (CurrentUser, TenantId)
  • Pipes - Validators (UUID, Pagination)
  • Database - Centralized service for all 3 tiers
  • Migrations - Unified migrators (Tenant & Product)
  • Utils - Crypto, date, retry, circuit-breaker
  • Constants - Error codes, defaults, rate limits
  • Validators - Zod schemas (minimal shared)
  • Filters - Exception handling (HTTP, Prisma)
  • Prisma Central - Tier 1 client library
  • Prisma Tenant - Tier 2 client library

🚧 Phase 4: Services (Next)

Microservices to be implemented:

  • API Gateway
  • Auth Service
  • Tenant Service
  • Admin Service
  • File Service
  • KIRA Service (AI assistant)
  • VERA Service (report generation)
  • CleverScreen Service (compliance screening)

Architecture Highlights

CLS (Continuation-Local Storage)

All context stored in CLS, not request object. Works everywhere:

  • HTTP requests
  • WebSocket connections
  • Cron jobs
  • Background workers
// Anywhere in your code
const auth = cls.get('auth');
const tenant = cls.get('tenant');
const product = cls.get('product');

WorkOS RBAC Integration

Roles and permissions from JWT tokens ONLY:

  • No role storage in databases
  • AuthenticationMiddleware validates JWT with JWKS
  • Verifies sessions via WorkOS API
  • session.organization_id determines tenant
// JWT payload
{
"sub": "user_01HXYZ",
"org_id": "org_01ABC",
"role": "tenant_admin",
"permissions": ["tenant:write", "user:read"]
}

3-Tier Database Architecture

Tier 1: Central DB

  • Platform metadata
  • NO PII (GDPR compliant)
  • NO roles (WorkOS manages)
  • Tenant-organization mapping
  • Database credentials storage (Tier 2 & 3)

Tier 2: Tenant Databases

  • One database per tenant: tenant_{uuid}_shared
  • Full user profiles WITH PII
  • Files, notifications, team settings
  • Product access control
  • Multi-jurisdiction support (UK, EU, US servers)

Tier 3: Product Databases

  • One database per tenant-product: tenant_{uuid}_{product}
  • Product-specific business data
  • Service-owned schemas
  • Independent evolution

Prerequisites

  • Node.js: >= 18.0.0
  • npm: >= 9.0.0
  • Docker: For local development
  • PostgreSQL: 14+ (for local dev)
  • Redis: For caching
  • WorkOS Account: For authentication

Getting Started

1. Clone & Install

git clone <repository-url>
cd HelixCore
npm install

2. Environment Setup

cp .env.example .env
# Edit .env with your values

Required environment variables:

# Central Database
CENTRAL_DATABASE_URL=postgresql://user:pass@localhost:5432/helix_central

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379

# WorkOS
WORKOS_API_KEY=sk_your_api_key
WORKOS_CLIENT_ID=client_your_client_id

# Service URLs (for inter-service communication)
FILE_SERVICE_URL=http://file-service:3004
ADMIN_SERVICE_URL=http://admin-service:3003

3. Database Setup

# Initialize databases
./scripts/init-db.sh

# Generate Prisma clients
npm run prisma:generate:central
npm run prisma:generate:tenant

# Run migrations
cd prisma/central && npx prisma migrate deploy
cd prisma/tenant && npx prisma migrate deploy

4. Start Development

# Start infrastructure (PostgreSQL, Redis)
docker-compose up -d postgres redis

# Build shared libraries
npm run build

# Start a service (when implemented)
nx serve auth-service

Project Structure

HelixCore/
├── apps/ # Microservices (to be implemented)
│ └── .gitkeep
├── libs/shared/ # Shared libraries (14 libraries)
│ ├── constants/ # Error codes, defaults, rate limits
│ ├── database/ # DatabaseService (Tier 1/2/3 access)
│ ├── decorators/ # CLS extractors (CurrentUser, TenantId)
│ ├── filters/ # Exception filters
│ ├── guards/ # Auth guards (CLS-based)
│ ├── interfaces/ # Interface definitions
│ ├── middleware/ # 5 middleware with CLS
│ ├── migrations/ # Tenant & Product migrators
│ ├── pipes/ # Validation pipes
│ ├── types/ # Type definitions
│ ├── utils/ # Crypto, retry, circuit-breaker
│ └── validators/ # Zod schemas (minimal)
├── prisma/
│ ├── central/ # Tier 1: Central DB schema
│ │ ├── schema.prisma
│ │ └── migrations/
│ └── tenant/ # Tier 2: Tenant DB schema
│ ├── schema.prisma
│ └── migrations/
├── terraform/ # Infrastructure as Code
│ ├── global/ # Global resources
│ ├── environments/ # Per-environment config
│ └── modules/ # Reusable modules
├── scripts/ # Utility scripts
│ └── init-db.sh
├── project-context/ # Architecture documentation
├── docker-compose.yml # Local development
├── nx.json # Nx workspace config
├── tsconfig.base.json # TypeScript path aliases
└── package.json # Unified dependencies

Shared Libraries

Core Libraries

@helix/shared/types - Type definitions

import { UserRole, ServiceStatus, ProductType } from '@helix/shared/types';

@helix/shared/interfaces - Interface shapes

import { RequestContext, TenantBase, HealthCheckResponse } from '@helix/shared/interfaces';

@helix/shared/database - Database service (all 3 tiers)

import { DatabaseService } from '@helix/shared/database';

const tenant = await db.central().tenant.findUnique({ where: { id } });
const member = await db.tenant().teamMember.findFirst({ where: { workosUserId } });
const threads = await db.product().thread.findMany({ where: { createdBy } });

Middleware (CLS Architecture)

import {
AuthenticationMiddleware, // JWT validation with JWKS
TenantContextMiddleware, // Loads Tier 2 DB credentials
ProductContextMiddleware, // Loads Tier 3 DB credentials
RequestLoggingMiddleware, // UUID generation, timing
RateLimitMiddleware, // Role-based throttling
} from '@helix/shared/middleware';

Guards (WorkOS RBAC)

import {
JwtAuthGuard,
RolesGuard,
Roles,
ScopesGuard,
RequireScopes,
TenantAccessGuard,
} from '@helix/shared/guards';

@Roles('tenant_admin')
@RequireScopes('tenant:write')
@UseGuards(JwtAuthGuard, RolesGuard, ScopesGuard)
async updateTenant() {}

Decorators & Pipes

import { CurrentUser, TenantId } from '@helix/shared/decorators';
import { UuidValidationPipe, PaginationPipe } from '@helix/shared/pipes';

@Get()
async findAll(
@CurrentUser() auth: any,
@TenantId() tenantId: string,
@Query(PaginationPipe) pagination: PaginationParams,
) {
return this.service.findAll(tenantId, pagination);
}

Utilities

import {
encrypt,
decrypt,
retry,
CircuitBreaker,
getCurrentTimestamp,
} from '@helix/shared/utils';

Migrations

import { TenantMigrator, ProductMigrator } from '@helix/shared/migrations';

// Deploy: migrate all tenant databases
await tenantMigrator.migrateAll();

// Provision: migrate single tenant
await tenantMigrator.migrateOne(tenantId);

Database Access Pattern

Tier 1: Central DB (Static Connection)

const tenant = await db.central().tenant.findUnique({
where: { workosOrganizationId: orgId },
include: { tenantDatabase: true }
});

Tier 2: Tenant DB (Dynamic per Tenant)

// Context from CLS (set by TenantContextMiddleware)
const member = await db.tenant().teamMember.findFirst({
where: { workosUserId: userId }
});

const hasAccess = await db.tenant().userProductAccess.findFirst({
where: {
teamMember: { workosUserId: userId },
productType: 'kira',
revokedAt: null
}
});

Tier 3: Product DB (Dynamic per Product)

// Context from CLS (set by ProductContextMiddleware)
// Product client registered during service bootstrap
const threads = await db.product<KiraPrismaClient>().thread.findMany({
where: { createdBy: userId }
});

Available Commands

Development

# Install dependencies
npm install

# Build all libraries
npm run build

# Build specific library
nx build shared-database

# Lint all code
npm run lint

# Format code
npm run format

Database

# Generate Prisma clients
npm run prisma:generate:central
npm run prisma:generate:tenant

# Run Central DB migrations
cd prisma/central && npx prisma migrate deploy

# Migrate all tenant databases (deployment)
npm run migrate:tenants

# Migrate all product databases (deployment)
npm run migrate:products

# Migrate specific tenant (provisioning)
npm run migrate:tenant -- --tenant-id=abc123

Docker

# Start infrastructure
docker-compose up -d

# Stop infrastructure
docker-compose down

# View logs
docker-compose logs -f

Architecture Decisions

Why CLS?

Traditional request object attachment only works in HTTP context. CLS works everywhere:

  • ✅ HTTP controllers
  • ✅ WebSocket gateways
  • ✅ Cron jobs
  • ✅ Background workers
  • ✅ Any async context

Why WorkOS for RBAC?

Single source of truth for roles and permissions:

  • ✅ No role synchronization between systems
  • ✅ Roles in signed JWT tokens (tamper-proof)
  • ✅ No database queries for authorization
  • ✅ Faster permission checks
  • ✅ Enterprise SSO integration

Why 3-Tier Databases?

Physical tenant isolation with performance:

  • Tier 1: Fast tenant selection (minimal data, no PII)
  • Tier 2: Physical PII isolation per tenant
  • Tier 3: Product autonomy and independent evolution

Why Separate Database Credentials?

Multi-jurisdiction compliance:

  • UK tenants → UK database servers
  • EU tenants → EU database servers
  • US tenants → US database servers

Separate host, port, username, password fields (not encrypted blob) for:

  • ✅ Credential rotation
  • ✅ Different servers per tier
  • ✅ Future-proof jurisdiction support

WorkOS Integration

JWT Validation Flow

  1. Extract JWT from Authorization header
  2. Verify signature with JWKS (cached indefinitely, rotates every 5 years)
  3. Decode claims - role, permissions, org_id, session_id
  4. Validate session via WorkOS API (cached 30 min)
  5. Get user data from WorkOS API (cached 30 min, NO PII in JWT)
  6. Store in CLS - auth, session, user

Organization Switching

WorkOS handles organization switching:

  • User switches org in WorkOS UI
  • New session created with different organization_id
  • Our middleware reads session.organization_id
  • TenantContextMiddleware finds tenant by workosOrganizationId
  • User now in different tenant context

Multi-Tenant Database Strategy

Provisioning Flow

New Tenant:

  1. Create Tenant record in Central DB
  2. Provision Tier 2 database on chosen jurisdiction server
  3. Store credentials in TenantDatabase (host, port, name, username, password)
  4. Run Tier 2 migrations via TenantMigrator
  5. User created in Tier 2 TeamMember table

New Product:

  1. Create TenantProduct record
  2. Provision Tier 3 database on product-specific server
  3. Store credentials in ProductDatabase
  4. Run Tier 3 migrations via ProductMigrator
  5. Grant user access via UserProductAccess (zero-trust, explicit grants)

Migration Strategy

Like Laravel's php artisan tenants:migrate:

# Deploy (all databases)
npm run migrate:central # Tier 1 (once)
npm run migrate:tenants # Tier 2 (all tenants)
npm run migrate:products # Tier 3 (all products)

# Provision (single database)
await tenantMigrator.migrateOne(tenantId);
await productMigrator.migrateOne(productId);

Service Communication

Services communicate via environment-configured URLs:

Local (Docker Compose):

kira-service:
environment:
- FILE_SERVICE_URL=http://file-service:3004

Production (ECS/Terraform):

environment = [
{
name = "FILE_SERVICE_URL"
value = "http://file-service.helix.local:3004"
}
]

In Code:

const fileServiceUrl = process.env.FILE_SERVICE_URL;
// NO hardcoded ports or URLs

Security Model

Authentication

  • ✅ WorkOS JWT with JWKS signature verification
  • ✅ Session validation (prevents token reuse after logout)
  • ✅ User data from WorkOS API (always fresh)
  • ✅ Comprehensive caching (performance without security compromise)

Authorization

  • ✅ Roles from JWT tokens (NO database storage)
  • ✅ Scopes from JWT tokens (NO database queries)
  • ✅ TenantMember verification (access control)
  • ✅ UserProductAccess (explicit product grants)

Tenant Isolation

  • ✅ Physical database separation
  • ✅ Middleware verifies membership
  • ✅ Guards enforce context exists
  • ✅ No cross-tenant data leakage

Documentation

Core Documents

Quick Reference


Contributing

Code Standards

  • TypeScript strict mode
  • No any types
  • CLS for context (not req.user)
  • WorkOS for roles (no database storage)
  • Environment variables for config
  • Service-specific validators in services

Before Committing

npm run lint
npm run format
npm run test

Next Steps

  1. Implement core services (HLX-6 through HLX-13)
  2. Set up GitHub Actions CI/CD
  3. Deploy to AWS ECS via Terraform
  4. Configure WorkOS production environment

Support


License

Proprietary - CleverChain Limited


Last Updated: 2025-10-22
Foundation Status: ✅ Complete
Next Phase: Microservice Implementation