Per Permission Standards, which names this file. Part of the module spec.
P02b-1 Step 3 producer implemented — 2026-10-12. The completed-provisioning domain translator runs only during owner completion, adds no endpoint or permission, and inserts a pending outbox row under the provisioning announcement. Existing command reachability is unchanged. The integration-event catalogue owns the flow.
ADR-0046 P02b-2 complete and merged — 2026-10-13. All four steps completed both independent review rounds. The merge closeout owns CI/merge status. The API-owned dispatcher and terminal audit add no permission key or HTTP endpoint. P02b-3's targeted replay is internal; Phase 03 separately decides operator permission and routes. Neither grants a tenant administrator bypass-role access.
P02d-4 Step 2 implemented — 2026-10-03. Both review rounds and focused fix verification pass. Site bootstrap is a marked GET/HEAD request, audit Off and without a permission key or grant. It uses matching host scope and READ ONLY; normal reads add no audit row. Public configuration/settings ports remain internal and unrouted. Independent rejected- assertion auditing remains unchanged. The public contract owns allowlisting and lifecycle/locale checks.
P02d-3 Step 1 implemented — 2026-10-02; both review rounds passed.
ITenantSettingsAccessor is internal and unrouted, uses trusted ambient
scope and adds no HTTP endpoint or permission key. Public admission belongs to
P02d-4. The module contract owns its scope.
P02d-2 Step 2 writers — 2026-10-02. The locale and branding commands are implemented unrouted tenant-wide operations, with no registered permission or organization override. Their eventual identity-backed permission admission remains Phase 03. This note grants no HTTP or Hub-internal reachability for the new commands.
No permission keys yet. The matrix below is a forward declaration in the
{module}.{resource}.{action} form with the closed action set of
Permission Standards. Registration runs
through IModule.RegisterPermissions(IPermissionRegistry), and neither type
exists in backend/src yet; the catalogue lands with the Identity module in
Phase 03, together with Role,
Permission and the lighting-up of the AuthorizationBehavior shell.
Six write handlers exist without identity-backed authorization, for two different reasons. Four contextual verification queries are explicitly audit Off. P02d-2 adds no HTTP or Hub-internal route.
ProvisionTenantCommand has nothing for a permission check to read: it runs with
an unresolved tenant context by construction — that is what lets it announce
the tenant it is creating — and attributes the write to UserId.SystemActor,
because provisioning precedes any membership in the tenant being provisioned.
CreateOrganizationCommand, MapHostToTenantCommand and the three P02d-2 writers do
run resolved, so the first argument does not transfer to them. What stands in for
authorization is the same thing for these writers: reachability. None of the six
writers has an HTTP route today; the seeder is their trusted production caller.
Phase 02c owns the planned Hub-internal
routes: tenant creation invokes ProvisionTenantCommand, which also creates the default
organization, and host mapping invokes MapHostToTenantCommand. It adds no direct
CreateOrganizationCommand endpoint. That surface takes learnstack-hub realm tokens
and no others. Resolved writers take
their tenant from the context and never from the request, so a caller cannot name
another tenant even without a permission check; the database refuses the write.
MapHostToTenantCommand is the one to gate first. It writes the row that
decides whose data an anonymous request sees, which makes it the highest-value
write in the module — and the reason the matrix above gives HostMapping its own
resource with no write: pointing a hostname at a tenant is an admin-scope
act, so tenancy.hostmapping.admin is the key that will govern it rather than any
grant inside the everyday tenant-admin role. tenancy.tenant.admin governs
provisioning and tenancy.organization.write the second organization. All three
are registered with the rest in Phase 03.
| Resource | read | write | delete | admin | Default role grants |
|---|---|---|---|---|---|
Tenant |
✓ | ✓ | – | ✓ | tenant-admin: read+write; platform operator: admin |
HostMapping |
✓ | – | – | ✓ | platform operator: admin. No write: pointing a hostname at a tenant is an admin-scope act, and a write grant would put the resolution index inside the everyday tenant-admin role |
Organization |
✓ | ✓ | ✓ | ✓ | tenant-admin: all; org-admin: read+write (own) |
TenantDomain |
✓ | ✓ | ✓ | – | tenant-admin: all |
TenantSetting |
✓ | ✓ | ✓ | – | tenant-admin: all; org-admin: own organization only |
TenantLocale |
✓ | ✓ | ✓ | – | tenant-admin: all |
TenantFeatureFlag |
✓ | ✓ | ✓ | – | tenant-admin: all |
Tenant has no delete: deprovisioning has no owning phase, and
Database Standards § GRANT matrix records that
the widening it needs is an ADR's to make, not a migration's.