3.3 KiB
ADR 0020: Organization Is The SaaS Tenant Root
Status
Accepted.
Context
The Hub is moving from a single-organization deployment assumption toward a
SaaS shape. The existing permission model already supports team principals via
PermissionGrant(PROJECT, TEAM, role), but without a tenant root the data model
cannot safely answer:
- which customer owns a project;
- which organization a team belongs to;
- whether a team grant crosses customer boundaries;
- which external directory sync produced a Feishu department/user-group membership;
- where future org admin, billing, audit, and platform break-glass controls attach.
Retrofitting this after projects, teams, runs, and audit records grow would force tenant assumptions into every caller.
Decision
Introduce Organization as the SaaS tenant root.
Customer-side collaboration data is organization-scoped:
Organization
OrganizationMembership
Team
TeamMembership
Project
ExternalDirectoryConnection
PermissionGrant
User remains global. A user can belong to multiple organizations through
OrganizationMembership. Project-level authorization still uses
PermissionGrant and PermissionAuthorizer; the authorizer derives the
organization from the requested resource, then resolves teams and external
principals in that organization scope.
Team belongs to exactly one organization. Project belongs to exactly one
organization. A PermissionGrant that grants a TEAM principal on a PROJECT
resource is valid only when the team and project are in the same organization.
This invariant is pinned in Spec.System.Organization.
External directory membership is scoped through ExternalDirectoryConnection:
ExternalDirectoryConnection(
organizationId,
provider,
providerTenantId,
source
)
ExternalPrincipalMembership points at a connection rather than carrying a
free-floating source string. source names a sync pipeline; it is not the
tenant identity.
The SaaS platform control plane is separate from customer project permissions.
READ/EDIT/MANAGE remains the customer-side project permission lattice.
Platform staff access, tenant suspension, billing, and break-glass access are a
separate control plane and must not be modeled as project MANAGE.
Consequences
- Existing single-org data is backfilled into a default organization during migration.
Team.slugbecomes organization-scoped instead of globally unique.- Actor resolution is organization-aware: direct teams and synchronized external principals only expand inside the resource's organization.
- Direct
USERgrants remain possible because a global user can collaborate across organizations when explicitly granted. - Future org admin UI can be built on this model without changing the authorization core.
Open Questions / Deferred
- Full platform admin UI, billing, and tenant suspension workflows are deferred. Production safety limits and workload brakes are no longer deferred; ADR-0022 defines them without treating a workload brake as tenant suspension.
- Whether direct
USERproject grants should require activeOrganizationMembershipis deferred; explicit direct grants remain allowed for now. - Provider-specific directory semantics beyond Feishu are deferred, but the connection model leaves room for them.