Data Isolation Architecture Declaration
The foundation of multi-tenant white-label: every new V109 table must carry tenant_id — one physical database, strict logical isolation.
Rule 1: every new table must carry tenant_id
From V109 on, every new uc_* table must include tenant_id TEXT NOT NULL DEFAULT 'aifeng'. Tables without this column must not be merged. uc_tenant_v109 / uc_tenant_cfg_v109 lead by example.
Rule 2: every query must filter by tenant
All reads/writes carry WHERE tenant_id = %(tenant)s (or a project-key filter) at the SQL layer; cross-tenant full scans are forbidden. Code review and acceptance scripts both check this rule.
Rule 3: seeds and imports are idempotent
All seed data is tagged with data_origin and uses INSERT ... ON CONFLICT idempotency; repeated runs neither duplicate rows nor overwrite manually changed state fields.
Rule 4: legacy data is never touched
Legacy v32/v33/v34 tables and data are never deleted or modified; new features always use versioned uc_*_v109 tables. Rollback = disable the route + keep the tables.
Tables created by this module
| Table | tenant_id | data_origin |
|---|---|---|
uc_tenant_v109 | TEXT NOT NULL DEFAULT 'aifeng' | v109_tenant_seed |
uc_tenant_cfg_v109 | TEXT NOT NULL DEFAULT 'aifeng' | v109_tenant_seed |
Isolation promise when cloned for peers
Each peer customer = one project (tenant_id/project_key). Their customer, conversation and order rows all hang under their own tenant_id; deleting a peer project deletes only that tenant_id's rows and never touches other tenants. Production-grade Row-Level Security (RLS) is queued as a Human-Gate item.
⚠ Human-Gate: plan changes, payment activation, domain binding, object-storage connection and production RLS policies all require the boss's step-by-step approval.