Authority Tenant Model

Authority tenant records define authentication boundaries and tenant-scoped metadata used by downstream Stella Ops services.

Platform-Side Propagation Contract

Authority is the source of truth for tenant lifecycle writes. Platform-side tenant resolution uses shared.tenants as the local directory for services that store tenant-scoped rows in the platform database.

Every successful console-admin tenant create, update, suspend, or resume writes the Authority row and appends a tenants catalog change in the same transaction. That is the whole mutation. shared.tenants is Platform’s projection of that catalog, written by platform-web over platform-web’s own connection, so the mutation has no second half that can partially fail: there is no Warning header, no *.propagation_failed audit event and no tenant.propagation.outcome audit property.

This replaced a cross-database write on 2026-09-08 (SPRINT_20260722_016 AUTH-25). Authority used to upsert shared.tenants in stellaops_platform over a connection derived from its own credentials, with Authority:Propagation:OnOrphan and the reconcile-tenants scripts as the recovery path. All of that is deleted. It was the cross-service database write ADR-039 forbids, and once Authority moved to its own authority_admin role — which has REVOKE CONNECT on every sibling database — it could only ever return 42501: permission denied for database "stellaops_platform".

Convergence is now the ordinary catalog-replication path (catalog-replication/v1): platform-web drains GET {authority}/catalog-changes/tenants under the catalog:replicate machine scope into its own database and projects the result into shared.tenants. Two operational consequences:

The flip is config-only on Catalog:Replication:Tenants — the same section Policy, Evidence, Notify and Release Orchestrator already use. Its two prerequisites are that platform-web has a StellaOps auth client (it comes from the Doctor:Registration block) and that the client holds catalog:replicate. Enabling replication without an auth client refuses to start and names the cause, rather than 401-looping once per poll with a table that quietly stops converging.

Downstream slug resolvers in Scanner, Concelier, Findings, Policy, Notify and Timeline depend on this contract: an Authority tenant missing from the catalog can authenticate but will resolve to 403/404 in platform-side services. A consumer that has been flipped to catalog replication reads its OWN replica rather than shared.tenants, and an unreadable or never-drained replica is a loud fault rather than a null answer about the tenant.

Compliance Profile

Authority exposes an optional complianceProfile on tenant views and console admin tenant summaries. The contract is tenant-compliance-profile.v1.

Profiles are nullable. Tenants without DORA, NIS2, or CRA obligations should leave the field absent rather than carrying empty regime flags.

Current persistence:

Validation is offline-only. LEIs are checked with the ISO 17442 checksum and no live registry lookup. Jurisdiction and NACE reference data are bundled in devops/runtime-assets/eu-compliance/.

Assurance Pack Enablement

Authority stores tenant/operator Assurance pack enablement under tenant.settings.operatorCompliance.assurancePacks. This is not a Europe-specific tenant model; it is the shared setup surface for optional packs such as NIS2 evidence support and CRA technical documentation support.

Tenant/operator fields:

Sealed/environment fields:

Runtime consumers can read the Authority-owned readiness projection through GET /api/v1/tenants/{tenantId}/assurance-packs. NIS2 SoA live-export readiness is reported as reason codes over the tenant profile and operator approver state. CRA product-publication readiness is represented separately from local CRA technical-file export readiness so mailbox/key publication preflight does not block local evidence exports. The route accepts authority:tenants.read, policy:read, or policy:audit, but remains tenant-self: X-StellaOps-TenantId is required and must match {tenantId}.

Runtime Submission Authorization Projection

Runtime services that need to gate operator-owned submissions read GET /api/v1/tenants/{tenantId}/submission-authorizations?purpose=<purpose>[&channelId=<channel>] with authority:tenants.read and the matching X-StellaOps-TenantId header. The response contract is tenant-submission-authorization.v1.

The projection is intentionally narrow. It returns the tenant id, requested purpose, optional channel id, derived regime, operator settings hash, optional pack config revision, autoSubmitEnabled, approvalRequired, freshAuthRequired, active approver summaries for the matching regime/actions, and stable fail-closed reason codes. For purpose=dora-incident, Authority derives regime dora and accepts active approvers with either submit or autoSubmitApproval; both actions are fresh-auth-gated by the normalized operator-compliance settings.

The projection never returns channel destinations, channel credentials, secret refs, signing providers, private keys, trust roots, storage roots, public base URLs, endpoint fail-closed posture, sealed/offline mode, or network-egress posture. Cross-tenant path/header mismatches are forbidden before a projection is returned.

Persistence Decision

The earlier EU compliance sprint requested a dedicated nullable JSON column on the tenant catalog. The modular Assurance-pack design supersedes that as a v1 requirement: metadata.complianceProfile is the durable Authority-owned profile slot. A dedicated column should be reconsidered only if query performance or cross-service reporting requires it, and that would be a generic pack-profile storage decision rather than a NIS2/CRA-specific schema change.