NIS2 Control Register
Audience: Policy implementers, Assurance/compliance engineers, and operators authoring tenant NIS2 registers. Scope: The Policy-owned Assurance control-register model and its NIS2 pack adapter — authoring workflow, evidence modes, runtime read/publish APIs, lifecycle events, and persistence.
Policy owns the Assurance control-register model, validation, lifecycle event stream, and PostgreSQL persistence. NIS2 is the first Assurance pack adapter over that model, not a Stella Ops-wide regional product assumption. The compatibility contract is NIS2 Control Register v1, the generic contract is Assurance Control Register v1, and the seed templates live under NIS2 control templates.
Product-facing NIS2 pack navigation lives in NIS2 Control Register. This guide remains the Policy implementation and source-contract guide.
The control register is an operator-facing aid for NIS2-obligated customers. Stella supplies Policy, evidence, export, and dashboard primitives; the regulated customer/operator remains responsible for applicability decisions, tenant evidence, regulator submissions, and legal conclusions.
Operating Boundary
- Use all thirteen thematic areas. A published tenant register must include an applicable or not-applicable decision for every area. This is enforced by the publish endpoint:
POST /api/policy/control-register/nis2/snapshotfails closed with HTTP 422SOA_INCOMPLETE(listing the missing areas) when any area lacks an explicitmetadata.applicabilitydecision. - Areas 8, 10, and 13 are not Stella-native telemetry domains. Keep them in the register with explicit not-applicable metadata and tenant-supplied evidence pointers.
- Do not claim Stella itself is the customer’s NIS2 regulated entity. Stella evidence supports the customer’s control register and Statement of Applicability.
- Preserve offline replay. Evidence refs must resolve to tenant-controlled or Stella-generated artifacts that can be verified without a live external service.
Authoring Workflow
- Select the area template from docs/templates/nis2.
- Copy the
complianceMappings.controls[]entry into the tenant Policy document. - Replace placeholder roles, evidence refs, review dates, and metadata with tenant-specific values.
- Keep
controlId,thematicArea,art21Refs,policyDocRef,responsibleRole,implementingControls[],evidencePointers[],lastReviewed, andnextReviewaligned with the contract. - For tenant-supplied evidence, use
evidencePointers[].sourceType: tenant-suppliedand record the role that submitted or approved the evidence. - Validate and approve the Policy document through the normal Policy lifecycle.
- Publish a control-register snapshot before downstream SoA export tooling consumes it.
Authoring Example
Add complianceMappings to a Policy document when a policy pack needs to map gates, scopes, metrics, or runbooks to NIS2 Article 21 coverage:
version: "1.0"
complianceMappings:
controls:
- controlId: NIS2-08-SUPPLIER-EVIDENCE
thematicArea: 8
art21Refs: ["21(2)(d)", "21(2)(e)"]
policyDocRef: docs/modules/policy/guides/nis2-control-register.md#supplier-evidence
responsibleRole: vendor-risk-owner
implementingControls:
- kind: gate
ref: policy.gates.supplier-assurance
- kind: runbook
ref: runbooks/vendor-evidence-review
evidencePointers:
- ref: tenant-evidence://supplier/quarterly-attestation
sourceType: tenant-supplied
submittedByRole: vendor-risk-owner
collectedAt: 2026-04-30T10:00:00Z
lastReviewed: 2026-04-30
nextReview: 2026-07-30
rules:
- name: Supplier Assurance Gate
action: block
Evidence Modes
Policy supports three control-level evidence modes:
stella-native: all pointers come from Stella telemetry or generated evidence.tenant-supplied: all pointers are customer/operator evidence.mixed: both Stella-native and tenant-supplied pointers are present.
When evidenceMode is omitted, Policy derives it from evidencePointers[].sourceType.
Runtime Read API
Policy exposes the live tenant-scoped NIS2 compatibility register at GET /api/policy/control-register/nis2 with policy:read. Tenant identity comes from the envelope-bound tenant claim; an optional tenantId query value must match that resolved tenant.
The endpoint reads the Policy IControlRegisterRepository and returns:
- exactly thirteen
thematicAreas[]summaries - detailed
controls[]withresponsibleRole,ownerRole, review dates, computedreviewStatus, evidence refs, tenant override refs, audit event ids, and mapping hashes items[]as the Graph/Web projection over the same source rows- deterministic
totaland nullablecursor
Supported filters are thematicArea, evidenceMode, reviewStatus, and responsibleRole. Pagination uses limit plus opaque cursor, with deterministic ordering by thematicArea then controlId. Pass asOfDate=yyyy-MM-dd when tests, exports, or replay need stable review-status evaluation; otherwise Policy uses the service TimeProvider UTC date.
Tenant override refs are derived from control metadata keys such as tenantOverrideRef or tenantOverrideRefs. Downstream Graph, Web, and export consumers must call this API or a typed client over it instead of reading Policy persistence tables.
Policy also exposes the framework-aware Assurance projection at GET /api/policy/control-register/frameworks/nis2. This route returns assurance-control-register-v1 with frameworkId: nis2, packId: nis2, generic areaOrDomain fields, externalRefs, tenantEvidenceMode, and the same tenant guards and pagination semantics. Unknown framework ids fail closed with HTTP 404 and code UNKNOWN_FRAMEWORK; consumers must treat that as pack configuration drift, not as an empty register.
Applicability Decisions
Use metadata.applicability to make the decision audit-friendly:
metadata:
applicability: not-applicable-to-stella-native-telemetry
applicabilityReason: Stella Ops does not operate the customer's facilities, workforce controls, or training program.
tenantEvidenceRequired: "true"
Use applicability: applicable when Stella evidence or mixed Stella/tenant evidence can support the control. Use not-applicable-to-stella-native-telemetry only when the customer still owns the thematic area and must provide evidence outside Stella telemetry.
Supplier Evidence
Supplier and third-party evidence normally maps to area 5. If a supplier artifact also supports training, HR, physical, or other non-native customer controls, keep a separate area-specific control row and use a tenant evidence pointer rather than expanding supplier data into Stella’s native telemetry model.
Supplier evidence should record:
- supplier or service scope
- control period
- attestation or audit artifact ref
- owner role
- review date and next review date
- any accepted exception and expiry
Review And Publishing
lastReviewedis the date the control owner accepted the mapping and evidence set.nextReviewmust be on or afterlastReviewedand should follow the tenant review cadence.control:registeredandcontrol:updatedevents are emitted when active mappings change.control:archivedis emitted when a control is removed from active register reads.control:snapshot_publishedis emitted when a deterministic snapshot is stored for SoA/export consumption.- Events include tenant id, control id when applicable, actor id, correlation id, previous/new/snapshot hashes,
frameworkId,packId, evidence-mode metadata, and deterministic JSON details. - When EvidenceLocker regulatory persistence is configured, Policy projects lifecycle events to
evidence.eu.control.lifecycle_recordedaudit rows using the Policy event id. The projection stores only hashes, event refs, control id, event type, and correlation id. - The Console
/compliance/nis2/control-registerpage reads the live Policy API when available and uses explicit fixture mode only when the API is unavailable.
Snapshots
Snapshots are built with Nis2ControlRegisterSnapshotBuilder. The builder sorts controls by thematic area and control id, sorts nested arrays, serializes canonical JSON, and emits sha256: hashes. PublishSnapshotAsync stores the NIS2 compatibility snapshot JSON with:
content_hashsoa_export_hashevidence_locker_bundle_ref
PublishSnapshotAsync is reachable from the runtime API via POST /api/policy/control-register/nis2/snapshot (scope policy:operate). The endpoint reads the tenant’s active controls, enforces the SoA completeness gate (every thematic area needs an explicit applicability decision; otherwise HTTP 422 SOA_INCOMPLETE), builds the deterministic snapshot, persists it, emits control:snapshot_published, and returns the contentHash / soaExportHash. The Console “Publish SoA snapshot” action (NIS2 control-register page) drives this endpoint and surfaces a 13-area completeness indicator that blocks publish until every area carries a decision. The optional request body accepts stellaRelease, evidenceLockerBundleRef, soaExportHash, and correlationId; omitted hashes default to the content-addressed snapshot hash.
Sprint 040 records the Policy-side content-addressed references. CLI/API rendering of the SoA export consumes those rows in downstream sprint 043.
EvidenceLocker regulatory artifact-ledger projection records two hash-only artifacts for a published snapshot: nis2-control-register-snapshot keyed by content_hash, and nis2-statement-of-applicability keyed by soa_export_hash. Their source refs point back to the Policy snapshot/event and the EvidenceLocker bundle ref; snapshot JSON remains Policy-owned.
For module-neutral consumers, AssuranceControlRegisterSnapshotBuilder projects the same rows or snapshot controls into assurance-control-register-v1 with frameworkId and packId while preserving the stored NIS2 snapshot contract.
Persistence
The Policy persistence baseline 001_v1_policy_baseline.sql creates (folded in from the pre-1.0 014_nis2_control_register.sql, which is archived under Migrations/_archived/pre_1.0/mig061/ and is not embedded):
policy.control_register_entries(baseline line 2399)policy.control_register_events(line 2439)policy.control_register_snapshots(line 2467)
The migration is embedded in StellaOps.Policy.Persistence and applied by the service startup migration hook already registered for Policy.
