NIS2 Statement of Applicability Contract v1

Contract ID: CONTRACT-NIS2-SOA-V1-018 Status: Active Owner: Policy Guild Schema version: nis2-soa-v1 Bundle schema version: nis2-soa-bundle-v1 Input contract: nis2-control-register-v1

Part of the Stella Ops Contracts index. Audience: Policy and Export Center implementers who produce and sign SoA bundles, and auditors who verify them offline. The source snapshot is the NIS2 Control Register; the production verifier uses the EU Signing Payload Registry.

Purpose

The NIS2 Statement of Applicability (SoA) export pins a tenant control-register snapshot to an export hash. The responsibilities split across three modules:

Statement of Applicability object (nis2-soa-v1)

The Statement of Applicability is the nis2-soa-v1 payload that is hashed and DSSE-signed. It is the statementOfApplicability member of the surrounding bundle (see Export Bundle).

{
  "schemaVersion": "nis2-soa-v1",
  "tenantId": "tenant-a",
  "generatedAt": "2026-04-30T09:00:00Z",
  "controlRegisterSnapshotHash": "sha256:...",
  "soaExportHash": "sha256:...",
  "evidenceLockerBundleRef": "evidence-locker://bundles/sha256:...",
  "controls": []
}

Grounded in Nis2StatementOfApplicability (Nis2SoaModels.cs, StellaOps.ExportCenter.Adapters.Nis2).

Fields

FieldTypeRequiredNotes
schemaVersionstringyesMust be nis2-soa-v1 (Nis2SoaConstants.SoaSchemaVersion).
tenantIdstringyesTenant that owns the exported register. Lowercased/normalized.
generatedAttimestampyesUTC timestamp; taken from the source snapshot generatedAt, used for deterministic export ordering.
controlRegisterSnapshotHashstringyesCanonical sha256:-prefixed hash of the nis2-control-register-v1 snapshot.
soaExportHashstringyesCanonical hash of the SoA hash payload, excluding the self-referential soaExportHash field.
evidenceLockerBundleRefstringyesContent-addressed EvidenceLocker bundle reference. Defaults to evidence-locker://bundles/{controlRegisterSnapshotHash} when not supplied.
controls[]arrayyesExported control rows derived from the pinned control-register snapshot.

The verified implementation does not emit a coverageSummary object on the SoA payload, and there is no assurance-pack-v1 coverage block. The stella-native vs. operator-supplied distinction is carried per-control by evidenceMode and evidencePointers[].sourceType (see below), not by a top-level summary.

controls[] row (Nis2SoaControlRow)

FieldTypeNotes
controlIdstringStable tenant-visible control id.
thematicAreaintegerNIS2 Article 21 thematic area (1–13).
applicabilitystringRead from control metadata key applicability; defaults to applicable.
art21Refs[]string arrayArticle 21 references, ordinal-sorted.
policyDocRefstringPolicy/module documentation reference.
responsibleRolestringAccountable owner role.
implementingControls[]array{ kind, ref, description, metadata }; ordinal-sorted by kind then ref. description/metadata are dropped when redactInternalOnly is set.
evidencePointers[]array{ ref, sourceType, description, submittedByRole, collectedAt }; ordinal-sorted by sourceType then ref. description is dropped when redactInternalOnly is set.
evidenceModestringstella-native, tenant-supplied, or mixed (from the control register).
lastReviewedISO dateLast review date.
nextReviewISO dateNext review date.
metadataobjectString map, key-sorted; emitted empty when redactInternalOnly is set.

Export Bundle (nis2-soa-bundle-v1)

The DSSE-signed artifact persisted and downloaded by ExportCenter is a bundle, not the bare SoA. The bundle schema version is nis2-soa-bundle-v1 (Nis2SoaConstants.BundleSchemaVersion), distinct from the SoA payload schema version nis2-soa-v1. Grounded in Nis2SoaBundle (Nis2SoaModels.cs) and Nis2SoaExporter.BuildSignedBundle.

MemberTypeNotes
schemaVersionstringnis2-soa-bundle-v1.
tenantComplianceProfileSchemaVersionstringtenant-compliance-profile.v1.
tenantScopeobject{ tenantId, legalEntityName, jurisdictionCode, regulatedRegimes[], nis2SectorCode, nis2Classification }. nis2Classification is essential, important, or not-classified.
controlRegisterSnapshotobjectThe normalized nis2-control-register-v1 snapshot (schemaVersion, tenantId, stellaRelease, generatedAt, controls[]).
statementOfApplicabilityobjectThe nis2-soa-v1 payload above.
manifestobject{ generatedAt, since, entries[] } where each entry is { path, mediaType, sha256 } for control-register-snapshot.json, statement-of-applicability.json, and statement-of-applicability.dsse.json.
signatureobjectDSSE envelope { payloadType, payload, signatures[] }; payloadType is application/vnd.stellaops.nis2.soa.v1+json; each signature is { keyid, alg, sig }.

Determinism Rules

Signing and Verification

Web/ExportCenter Trigger

Endpoints are mapped under /v1/exports/nis2/soa by Nis2SoaExportEndpoints.MapNis2SoaExportEndpoints. The group requires authorization; the read endpoints require the export.viewer scope (StellaOpsScopes.ExportViewer / StellaOpsResourceServerPolicies.ExportViewer) and the run-creation endpoint requires the export.operator scope (StellaOpsScopes.ExportOperator).

Method + routeScope policyPurpose
GET /v1/exports/nis2/soa/profileexport.viewerDiscover the active profile (profileId = nis2:statement-of-applicability, schemaVersion = nis2-soa-bundle-v1, status, runEndpoint, requiredScope, description).
POST /v1/exports/nis2/soa/runsexport.operatorCreate a signed run.
GET /v1/exports/nis2/soa/runs/{runId:guid}export.viewerRead the persisted run.
GET /v1/exports/nis2/soa/runs/{runId:guid}/bundleexport.viewerDownload the signed bundle.

Configuration (Export:Nis2Soa)

Bound to Nis2SoaExportOptions:

CLI (offline)

stella nis2-soa (Nis2SoaCommandGroup) produces a deterministic, locally HMAC-signed bundle for air-gapped review. Options:

The CLI runs offline verification on the generated bundle and exits non-zero (VerificationFailed) if it does not verify. This local HMAC path is for air-gapped review only; it is not the production eIDAS/EU signing path.