Skip to main content

Functional Specification: Tenant ID Management

1.0 Executive Summary

1.1 Purpose

This document specifies the functional requirements for ChainAlign's Tenant ID Management system. The purpose is to establish a robust, secure, and scalable system for generating and validating tenant identifiers, which are fundamental to the platform's multi-tenant architecture.

1.2 Problem Statement

As ChainAlign evolves, a clear distinction is needed between customer tenants, internal development/testing tenants, and the core system knowledge base. Without a formal management system, there is a risk of accidentally allocating reserved IDs, leading to data collisions, security vulnerabilities, and difficulties in debugging and operational management.

1.3 Solution Overview

The solution is to implement a centralized Tenant ID Manager Service within the backend. This service will enforce a strict UUID-based namespace strategy to logically separate different tenant types. It will include server-side guardrails to prevent the creation of tenants with reserved IDs and provide a clear, auditable method for tenant ID generation.


2.0 Core Design Principles

  1. Logical Separation: System, internal, and customer data must be clearly and instantly distinguishable based on the tenant ID pattern.
  2. Security by Design: The system must programmatically prevent the accidental or malicious allocation or use of reserved tenant IDs.
  3. Human Readability: The ID patterns should allow developers and operations staff to immediately recognize the type of tenant, simplifying debugging and maintenance.
  4. Scalability & Uniqueness: The system must guarantee UUID uniqueness and be capable of supporting thousands of customer tenants without collision.

3.0 Detailed Functional Requirements

FR-1: UUID Namespace Strategy (Revised)

The system SHALL use UUIDs for all tenant IDs, with specific, fully-qualified UUIDs reserved for internal and system use.

Tenant IDtenants.typePurpose
00000000-0000-0000-0000-000000000000systemImmutable core knowledge base.
11111111-1111-1111-1111-111111111111internalInternal development & operations sandbox.
Standard UUIDv4customer, sandboxAll other tenants (customers, partners, etc.).

Key Requirement: The system SHALL NOT use artificial UUID prefix ranges (e.g., aaaa...). The tenants.type column is the single source of truth for all classification and access control logic.

FR-2: Allocation Guardrail (Creation-Time)

The system SHALL implement a server-side guardrail to validate all new tenant ID allocations.

  • FR-2.1: When a new tenant is created, a function validate_tenant_allocation(proposedTenantId, requestor) SHALL be called.
  • FR-2.2: If proposedTenantId matches a reserved ID, the request MUST be rejected with an HTTP 400 or 409 status and a structured JSON error:
    {
    "error": "TENANT_ID_RESERVED",
    "message": "This tenant ID is reserved for internal use and cannot be created."
    }
  • FR-2.3: The blocked attempt MUST be logged as a CRITICAL event to the security_audit_log (see FR-4).

FR-3: Runtime Access Guardrail (DAL)

A Data Access Layer (DAL) guardrail SHALL be implemented to enforce tenant data access policies at runtime.

  • FR-3.1: All DAL methods that access tenant-scoped data MUST call a check_access(user, tenantId) function before executing a query.
  • FR-3.2 (Logic):
    • If tenantId is a standard customer tenant, permit if user.tenant_id matches the tenantId or the user has a global admin role.
    • If tenantId is a reserved system/internal tenant, permit only if the user possesses a role explicitly allowed to access that tenant type (e.g., SYSTEM_ADMIN, INTERNAL_DEV).
  • FR-3.3 (Denial): On any access denial, the function MUST throw a specific AccessDeniedError and log a TENANT_ACCESS_VIOLATION event with severity=CRITICAL to the security_audit_log.

FR-4: Immutable Audit Trail

A new, dedicated table for security auditing SHALL be created.

  • FR-4.1 (Immutability): The security_audit_log table MUST be treated as append-only. Application logic should prevent updates or deletes.
  • FR-4.2 (Tamper-Proofing): Each log entry SHALL contain a SHA256 hash of critical fields (occurred_at, event_type, tenant_id, actor.user_id) to allow for tamper detection.
  • FR-4.3 (Alerting): All events logged with severity=CRITICAL MUST trigger a real-time alert to the security team (e.g., via PagerDuty or Slack).

4.0 API & Database Design

4.1 Database Schema Changes

  1. Alter tenants Table: A Knex migration will add the type and description columns to the existing tenants table.

  2. Create security_audit_log Table: A new Knex migration will create the immutable audit log table.

    Knex Migration Example (create_security_audit_log.js):

    exports.up = async function(knex) {
    await knex.schema.createTable('security_audit_log', (table) => {
    table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
    table.timestamp('occurred_at').notNullable().defaultTo(knex.fn.now());
    table.text('severity').notNullable(); // e.g., 'INFO','WARN','CRITICAL'
    table.text('event_type').notNullable(); // e.g., TENANT_CREATED, TENANT_ACCESS_VIOLATION
    table.jsonb('actor'); // {user_id, username, roles}
    table.uuid('tenant_id');
    table.jsonb('request_payload'); // sanitized
    table.jsonb('context'); // e.g., caller IP, request id
    table.text('immutable_hash').notNullable();
    table.index('event_type');
    });
    };

    exports.down = async function(knex) {
    await knex.schema.dropTableIfExists('security_audit_log');
    };

4.2 Service Implementation Examples

Tenant Validation (Node.js):

// backend/src/services/tenantManager.js
const { insertAudit } = require('../services/auditService');
const RESERVED_IDS = ["00000000-0000-0000-0000-000000000000", "11111111-1111-1111-1111-111111111111"];

async function validate_tenant_allocation(proposedTenantId, requestor) {
if (RESERVED_IDS.includes(proposedTenantId)) {
await insertAudit({
severity: 'CRITICAL',
event_type: 'TENANT_ALLOCATION_ATTEMPT_BLOCKED',
actor: { user_id: requestor.id, username: requestor.name },
tenant_id: proposedTenantId,
// ... other fields
});
const err = new Error('This tenant ID is reserved for internal use.');
err.code = 'TENANT_ID_RESERVED';
err.httpStatus = 400;
throw err;
}
}

Access Control (Node.js):

// backend/src/services/accessControl.js
async function check_access(user, tenantId) {
if (user.roles && user.roles.includes('SYSTEM_ADMIN')) return true;

const isReserved = RESERVED_IDS.includes(tenantId);
if (!isReserved) {
if (user.tenant_id === tenantId) return true;
} else {
const allowed = ['SYSTEM_ADMIN', 'INTERNAL_DEV'];
if (user.roles && user.roles.some(r => allowed.includes(r))) return true;
}

// DENY: audit and throw
await insertAudit({
severity: 'CRITICAL',
event_type: 'TENANT_ACCESS_VIOLATION',
actor: { user_id: user.id },
tenant_id: tenantId
});
const err = new Error('Access denied for tenant');
err.code = 'TENANT_ACCESS_DENIED';
err.httpStatus = 403;
throw err;
}

5.0 Implementation Plan (Revised)

  • Phase 1: Database Setup
    • Create a Knex migration to add the type and description columns to the tenants table.
    • Create a Knex migration for the new security_audit_log table.
    • Update the Knex seed file to correctly populate the reserved tenants.
  • Phase 2: Core Services Implementation
    • Create and implement the tenantManager.js service with the revised validation logic.
    • Create a new auditService.js for logging security events.
    • Create the accessControl.js service with the check_access guardrail function.
  • Phase 3: Integration & Refactoring
    • Integrate tenantManager.js into the tenant creation workflow.
    • Integrate the check_access guardrail into the base data access layer and/or API middleware to ensure it is called for all data-access operations.
  • Phase 4: Observability
    • Configure a real-time alerting mechanism (e.g., webhook to Slack/PagerDuty) that triggers on CRITICAL events in the security_audit_log.

6.0 Success Metrics

  • Security: 100% of tenant creation attempts using a reserved ID pattern are blocked and logged as a CRITICAL audit event.
  • Data Integrity: All new customer tenants are created with a standard UUIDv4 and the type of 'customer'.
  • Access Control: Unauthorized attempts to access data in any tenant (especially reserved ones) are blocked and logged as a CRITICAL audit event.
  • Observability: The security team receives real-time alerts for all CRITICAL security events.