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
- Logical Separation: System, internal, and customer data must be clearly and instantly distinguishable based on the tenant ID pattern.
- Security by Design: The system must programmatically prevent the accidental or malicious allocation or use of reserved tenant IDs.
- Human Readability: The ID patterns should allow developers and operations staff to immediately recognize the type of tenant, simplifying debugging and maintenance.
- 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 ID | tenants.type | Purpose |
|---|---|---|
00000000-0000-0000-0000-000000000000 | system | Immutable core knowledge base. |
11111111-1111-1111-1111-111111111111 | internal | Internal development & operations sandbox. |
| Standard UUIDv4 | customer, sandbox | All 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
proposedTenantIdmatches a reserved ID, the request MUST be rejected with anHTTP 400or409status 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
CRITICALevent to thesecurity_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
tenantIdis a standard customer tenant, permit ifuser.tenant_idmatches thetenantIdor the user has a global admin role. - If
tenantIdis 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).
- If
- FR-3.3 (Denial): On any access denial, the function MUST throw a specific
AccessDeniedErrorand log aTENANT_ACCESS_VIOLATIONevent withseverity=CRITICALto thesecurity_audit_log.
FR-4: Immutable Audit Trail
A new, dedicated table for security auditing SHALL be created.
- FR-4.1 (Immutability): The
security_audit_logtable 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=CRITICALMUST 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
-
Alter
tenantsTable: A Knex migration will add thetypeanddescriptioncolumns to the existingtenantstable. -
Create
security_audit_logTable: 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
typeanddescriptioncolumns to thetenantstable. - Create a Knex migration for the new
security_audit_logtable. - Update the Knex seed file to correctly populate the reserved tenants.
- Create a Knex migration to add the
- Phase 2: Core Services Implementation
- Create and implement the
tenantManager.jsservice with the revised validation logic. - Create a new
auditService.jsfor logging security events. - Create the
accessControl.jsservice with thecheck_accessguardrail function.
- Create and implement the
- Phase 3: Integration & Refactoring
- Integrate
tenantManager.jsinto the tenant creation workflow. - Integrate the
check_accessguardrail into the base data access layer and/or API middleware to ensure it is called for all data-access operations.
- Integrate
- Phase 4: Observability
- Configure a real-time alerting mechanism (e.g., webhook to Slack/PagerDuty) that triggers on
CRITICALevents in thesecurity_audit_log.
- Configure a real-time alerting mechanism (e.g., webhook to Slack/PagerDuty) that triggers on
6.0 Success Metrics
- Security: 100% of tenant creation attempts using a reserved ID pattern are blocked and logged as a
CRITICALaudit event. - Data Integrity: All new customer tenants are created with a standard UUIDv4 and the
typeof 'customer'. - Access Control: Unauthorized attempts to access data in any tenant (especially reserved ones) are blocked and logged as a
CRITICALaudit event. - Observability: The security team receives real-time alerts for all
CRITICALsecurity events.