Tenant Compliance Profile v1
Version: tenant-compliance-profile.v1 (constant: TenantComplianceProfileValidator.SchemaVersion) Owner: Authority Status: implemented (validator, read routes, audit enrichment, and Graph consumer projection are all shipped and covered by Authority/Graph tests)
Purpose
The tenant compliance profile is the single tenant-scoped source for regulatory regime filtering across DORA, NIS2, CRA, and optional Assurance pack workflows. It is nullable so non-regulated tenants do not inherit regulatory defaults.
Audience: Authority and Graph implementers consuming the profile, and operators who configure a tenant’s regulatory posture through the Console Admin tenant API.
Schema
{
"lei": "5493001KJTIIGC8Y1R12",
"legalEntityName": "Example Bank SE",
"jurisdictionCode": "DE",
"responsibilityLocus": "shared",
"regulatedRegimes": ["dora", "nis2"],
"dora": {
"isFinancialEntity": true,
"isSignificantEntity": false,
"consolidatedAssetsBucket": "50b-100b",
"ictThirdPartyOf": ["529900T8BM49AURSDO55"]
},
"nis2": {
"sectorCode": "K",
"isEssential": true,
"isImportant": false
},
"cra": {
"actsAsManufacturer": false,
"actsAsImporter": false
}
}
Fields:
lei: optional for non-DORA tenants; required whenregulatedRegimesincludesdora. Validation uses the offline ISO 17442 / ISO 7064 mod-97 checksum and does not call live LEI registries.legalEntityName: operator-supplied legal name for export rendering.jurisdictionCode: ISO 3166-1 alpha-2 jurisdiction code, uppercase.responsibilityLocus: optional normalized locus for downstream scoping:customer,shared, orstella.regulatedRegimes: sorted set ofcra,dora, andnis2.dora: DORA entity flags, TLPT-significance support, and directictThirdPartyOfLEI references.nis2: NACE Rev. 2.1 sector code plus essential/important flags. The essential and important flags are mutually exclusive.cra: customer/operator product-role flags. Stella’s own CRA manufacturer decisions remain documented separately.
Persistence
Authority stores the normalized profile under tenant metadata key complianceProfile. The value is absent when no profile is configured. This is the v1 tenant-catalog persistence decision: Assurance packs are optional modules, so the core tenant row keeps a generic metadata JSONB column rather than adding a Europe-specific top-level column.
The metadata value is still durable tenant-catalog state. It is validated by Authority create/update flows, read through the typed Authority runtime route, and carried into tenant-scoped audit events for regulator and auditor filtering.
Runtime Read Boundary
Authority is the source of truth for this profile. Graph and EU reporting consumers must consume it through a typed source-service client rather than copying tenant metadata into Graph persistence.
Authority exposes three read routes, grouped under /api/v1/tenants and all guarded by the same authority:tenants.read scope plus a tenant-header filter (TenantHeaderFilter). All three are GET-only; the profile itself is written through the Console Admin tenant create/update API (see Validation), not through this group.
GET /api/v1/tenants/{tenantId}/compliance-profile- response:
TenantComplianceProfileResponse(schemaVersion=tenant-compliance-profile.v1,tenantId,profile)
- response:
GET /api/v1/tenants/{tenantId}/assurance-packs- response:
TenantAssurancePackReadinessResponse(schemaVersion=tenant-assurance-pack-readiness.v1)
- response:
GET /api/v1/tenants/{tenantId}/submission-authorizations?purpose={purpose}&channelId={channelId}- response:
TenantSubmissionAuthorizationResponse(schemaVersion=tenant-submission-authorization.v1)
- response:
The Authority-side client contract is ITenantComplianceProfileClient with TenantComplianceProfileLookupRequest.
Common request handling for all three routes:
- The
{tenantId}path value is normalized (trimmed, lower-cased). A blank value returns400 invalid_tenant_id. - When a tenant header is present and does not match the path tenant, the route fails closed with
403 Forbidden. - The tenant is resolved from either the in-process Authority tenant catalog (
IAuthorityTenantCatalog, which carries an optional typedComplianceProfile) or persisted tenant metadata (ITenantRepository.GetBySlugAsync). When neither source has the tenant the route returns404 tenant_not_found.
{
"schemaVersion": "tenant-compliance-profile.v1",
"tenantId": "tenant-a",
"profile": null
}
For regulated tenants, profile contains the normalized tenant-compliance-profile.v1 payload. For non-regulated tenants, profile is null. Responses must not include tenant operator settings, deployment posture, secret references, channel destinations, or signing/trust-root configuration. The compliance-profile read route also emits an authority.tenant_compliance_profile.read AuthEvent carrying only the bounded, non-secret fields listed under Audit Enrichment.
The Assurance pack readiness route returns only enabled pack descriptors and Authority-owned readiness booleans/reason codes. status is one of: disabled (no enabled packs — the enabled-pack list is empty), blocked (at least one enabled pack has unmet readiness reason codes), or configured (all enabled packs are ready). Per-pack readiness exposes authorityProfileConfigured, operatorApproverConfigured, and retentionPolicyReviewed booleans plus a sorted reasonCodes list drawn from authority-profile-missing, operator-approver-missing, and retention-policy-unreviewed. It must not echo channel destinations, mailbox credentials, private keys, trust roots, or sealed deployment posture.
The submission-authorization route projects runtime-safe operator submission state for a given purpose (and optional channelId). It resolves the regime from the purpose (dora-incident, dora-roi-distribution, dora-info-sharing -> dora; nis2-csirt -> nis2; enisa-cra -> cra) and returns auto-submit / approval / fresh-auth flags, the operator-settings hash and revision, the active approvers (identity and action metadata only), and a sorted reasonCodes list (e.g. purpose-unsupported, submission-channel-missing, purpose-channel-mismatch, channel-disabled, auto-submit-disabled, approver-missing). It must not echo channel destinations or secrets.
Graph declares its consumer boundary in eu-runtime-api-contracts-v1.mdas:
- request:
TenantComplianceProfileLookupRequest - response:
TenantComplianceProfileResponse - nullable non-regulated tenants:
profileisnull
Note that the Graph-side TenantComplianceProfileResponse is a deliberately reduced projection: it adds a sourceOfTruth: "Authority" field and its profile is a TenantComplianceProfileRecord carrying only lei, legalEntityName, jurisdictionCode, regulatedRegimes, and responsibilityLocus. The dora, nis2, and cra sub-objects are not re-exposed across the Graph boundary.
Validation
TenantComplianceProfileValidator.Validate runs on the Console Admin tenant create/update flows (POST / PATCH tenant in ConsoleAdminEndpointExtensions, which both call the validator before persisting). The read routes above also re-run the same validator when deserializing stored metadata, so a profile that fails validation is normalized to null rather than surfaced. A fully empty profile (no LEI, name, jurisdiction, locus, regimes, or regime sections) validates as null (absent), not as an error.
Validation rules:
- LEI length and character set (
^[0-9A-Z]{18}[0-9]{2}$), trailing check digits, and ISO 7064 mod-97-10 checksum (remainder must equal 1). - DORA-regulated tenants must include a valid LEI and jurisdiction.
- Regime-specific sections must match
regulatedRegimes: a section present without its regime listed is an error, andnis2/cralisted without their section is an error. regulatedRegimesvalues are lower-cased, de-duplicated, restricted tocra/dora/nis2(unsupported values error), and re-ordered to the fixedcra, dora, nis2sequence.jurisdictionCodeis upper-cased and must match^[A-Z]{2}$(ISO 3166-1 alpha-2 shape; the validator checks the two-letter shape, not membership in the bundled code list).responsibilityLocus, when supplied, must becustomer,shared, orstella.- NIS2
sectorCodemust be a NACE section letter (A–U, hard-coded) or a class code matching^[0-9]{2}(\.[0-9]{1,2})?$;isEssentialandisImportantcannot both be true. dora.ictThirdPartyOfentries are upper-cased and must each pass the same LEI checksum; valid entries are de-duplicated and sorted.
Offline Reference Data
Deterministic reference assets are bundled under:
devops/runtime-assets/eu-compliance/lei-prefix-validation.v1.jsondevops/runtime-assets/eu-compliance/iso-3166-alpha2.v1.jsondevops/runtime-assets/eu-compliance/nace-rev2.1-sections.v1.json
These assets pin and document the validation policy for offline replay; they are not loaded by the Authority validator at runtime. The Validate implementation uses an inline ISO 7064 mod-97 checksum, a two-letter jurisdictionCode regex, and a hard-coded set of NACE section letters plus a class-code regex. The LEI asset is explicit about this: it pins checksum-only behavior (validationMode: iso-17442-checksum-only), ships an empty knownIssuerPrefixes list by design, and intentionally does not claim to be a live GLEIF/LOU registry snapshot. If the validator’s embedded constants and these assets ever diverge, the embedded constants are authoritative for runtime validation.
Replay And Schema Pinning
The schema is pinned by the tenant-compliance-profile.v1 identifier. Exporters must record this version alongside any downstream DORA, NIS2, or CRA evidence so offline replay can resolve the exact field contract without fetching live regulator metadata.
Audit Enrichment
AuthorityAuditSink enriches every tenant-scoped AuthEvent audit record with bounded, non-secret compliance profile fields when the tenant resolves in the repository (by slug, or by GUID fallback). Enrichment is skipped when the record already carries tenant.compliance_profile.* properties, so a record is never double-tagged. The full enrichment field set is:
tenant.compliance_profile.schema_versiontenant.compliance_profile.present
and, when a profile is present (present == "true"):
tenant.compliance_profile.hashtenant.compliance_profile.leitenant.compliance_profile.jurisdictiontenant.compliance_profile.responsibility_locustenant.compliance_profile.regulated_regimes(space-joined)tenant.compliance_profile.nis2.sector_code,.nis2.is_essential,.nis2.is_important(only when thenis2section exists)tenant.compliance_profile.cra.acts_as_manufacturer,.cra.acts_as_importer(only when thecrasection exists)
The hash is a SHA-256 digest of the normalized profile payload, emitted with a sha256: prefix and lower-case hex. There is no dora.* enrichment field set; DORA flags are not echoed into audit beyond the shared lei / jurisdiction / regulated_regimes fields.
The authority.tenant_compliance_profile.read event emitted by the compliance-profile read route carries a smaller, fixed subset (schema_version, present, lei, jurisdiction, responsibility_locus, regulated_regimes) and, because those properties are already present, is not re-enriched by the sink.
Audit records do not include operatorCompliance settings, channel destinations, trust roots, secret references, or deployment posture.
