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 Authority first, then calls ISharedTenantsPropagator to upsert the matching shared.tenants row. Propagation failures do not roll back the Authority write: the API returns the tenant result with a Warning header, writes an *.propagation_failed audit event, and records tenant.propagation.outcome=warning on the successful tenant audit event. This keeps the operator able to recover by running devops/compose/scripts/reconcile-tenants.ps1 or devops/compose/scripts/reconcile-tenants.sh.
Startup orphan handling is controlled by Authority:Propagation:OnOrphan:
warn(binary default when the key is unset): emittenant_propagation_orphanwarnings and keep Authority available. Use this during the first upgrade after Sprint 20260518_049.reconcile(compose-stack default since Sprint 20260607_020): upsert missingshared.tenantsrows at startup. The compose stack shipsAUTHORITY_PROPAGATION_ONORPHAN=reconcile(→STELLAOPS_AUTHORITY_AUTHORITY__PROPAGATION__ONORPHAN) so the bootstrap tenants seeded straight intoauthority.tenants(default,installation) and every operator-created tenant converge intoshared.tenantsautomatically on a fresh DB — no manualpsql, satisfying AGENTS.md §2.7. The upsert is idempotent (no-op once converged). This is also what makes the Console global-admin tenant switcher complete (Q4CAT-002), since the switcher readsshared.tenants.fail: fail Authority startup if Authority tenants are missing fromshared.tenants. Use this only after the reconciler has succeeded and the deployment wants drift to be a release gate.
Why
reconcilerather than a forward-only SQL backfill migration (ADR-004)?authority.tenantslives instellaops_authorityandshared.tenantsinstellaops_platform; a single SQL migration cannot cross databases. The Authority startup reconciler (OrphanedTenantHealthCheck→ISharedTenantsPropagator) reads the Authority repository and writes the platform table at the application layer, and is the §2.7-compliant convergence path for this two-database case.
Downstream F11-style slug resolvers, including PostgresEvidenceLockerTenantResolver and sibling resolvers in Scanner, Concelier, Findings, Policy, Notify, and Timeline, depend on this contract: an Authority tenant missing from shared.tenants can authenticate but will resolve to 403/404 in platform-side services.
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:
- Configured tenants expose
complianceProfile: null. - Console admin create/update validates profile payloads and stores the normalized value in tenant
metadata.complianceProfile. - The profile remains in tenant metadata JSONB for v1. This is intentional: NIS2 and CRA support are optional Assurance pack modules, and the core tenant table should not gain a region-specific column for every future pack family.
- Runtime consumers read the source-owned profile through
GET /api/v1/tenants/{tenantId}/compliance-profilewithauthority:tenants.read. The response isTenantComplianceProfileResponsewithschemaVersion,tenantId, and nullableprofile; it excludesoperatorCompliance, deployment posture, channel destinations, signing provider, trust roots, and secret references. - Tenant create/update audit events include
tenant.compliance_profile.*properties for offline regulator filtering. - Authority’s audit sink enriches all tenant-scoped AuthEvent records with the same bounded
tenant.compliance_profile.*projection. The projection includes presence, schema version, profile hash, jurisdiction/regime filters, and NIS2/CRA role fields, but excludes operator settings, channel destinations, trust roots, secret references, and deployment posture.
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:
packId,enabled,mode,claimBoundary,ownerRole,enabledBy,enabledAt,lastReviewedAt, andconfigRevision- operator approvers and review metadata
- tenant retention overrides and channel destinations where the values are operator-owned and non-secret
Sealed/environment fields:
- signing providers, private keys, trust-root bundles, storage roots, public base URLs, endpoint fail-closed posture, schema asset roots, mailbox credentials, SMTP/provider secrets, and network-egress posture
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.
