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:

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.

The Authority-side client contract is ITenantComplianceProfileClient with TenantComplianceProfileLookupRequest.

Common request handling for all three routes:

{
  "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:

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:

Offline Reference Data

Deterministic reference assets are bundled under:

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:

and, when a profile is present (present == "true"):

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.