Adds accounting-service PATCH/archive, fiscal helpers, COA templates and setup status, plus SuperApp Accounting UI (DS, scoreboard, masters, vouchers, ledger, ops modules) with session refresh and HTTPS public API URLs. Co-authored-by: Cursor <cursoragent@cursor.com>
2.1 KiB
Multi-Tenant Architecture
Architecture only. Status of onboarding delivery → progress.md
Tenant Model
A Tenant is a workspace/organization on the platform. Users may belong to multiple tenants; users.current_tenant_id selects the active workspace.
Lifecycle
draft → pending_activation → active → suspended / archived
Legacy values inactive / deleted remain for compatibility.
Operational Roles (Core memberships)
platform_admin, tenant_owner, tenant_admin, tenant_editor, tenant_viewer
Membership status: active / invited / disabled. Every active tenant must retain at least one active tenant_owner.
Tenant Resolution Order
- Header
X-Tenant-ID - Header
X-Tenant-Slug - Subdomain from Host +
PLATFORM_BASE_DOMAIN - Custom domain from Host
- Authenticated user's
current_tenant_id(when headers/host do not resolve)
Results land in request.state.tenant_id / request.state.tenant_slug. Tenant-aware endpoints use require_tenant / get_tenant_resolution.
Domains
- Auto subdomain:
{slug}.{PLATFORM_BASE_DOMAIN}(verified when provisioned) - Custom domain: stored with
verification_status=pendinguntil DNS verification ships - SSL expansion for tenant hosts: ADR-009
White-Label
Tenant branding fields live on tenants. Public tenant-site/theme resolution uses host-based tenant detection. Platform defaults remain env/theme.config.json (ADR-008).
Entitlement
EntitlementService.check_feature_access(tenant_id, feature_key) — Redis cache then DB. Disabled tenant, inactive subscription, or missing plan feature → deny. Custom access overrides plan.
Hard Rules
- No cross-tenant queries.
- Every business write is tenant-scoped.
- Authorization for workspace actions uses Core memberships (ADR-007).