Platform Cryptography and Compliance Defaults

Scope

The setup wizard owns installation-scoped cryptography and compliance evidence defaults. It does not write per-tenant compliance overrides.

The merged crypto setup step is titled Cryptography in the UI and accepts both crypto and cryptography as API step IDs during the compatibility window. It captures:

Installation Settings

The wizard and CLI write the following keys to platform.environment_settings.

KeyValuesDefault
Crypto:Regioninternational, eidas, russian, chineseinternational
Crypto:FipsModetrue, falsefalse
Compliance:Nis2:Enabledtrue, falsefalse
Compliance:Dora:Enabledtrue, falsefalse
Compliance:Cra:Enabledtrue, falsefalse

Legacy lowercase setup keys such as crypto.provider and compliance.nis2.enabled are accepted by the setup apply path and converged to the canonical keys above. New clients must write the canonical keys.

Soft-Probe Contract

Compliance toggles are accepted even when adjacent systems are not configured yet. The setup service records warning checks instead of blocking apply:

ToggleWarning conditionRemediation route
NIS2Authority compliance-profile URL is missing/setup/identity-access
DORANotify DORA information-sharing channel is missing/setup/notifications
CRAManufacturer mailbox or intake key is missing/setup/system/cryptography

Warnings must be surfaced as operator remediation messages. They do not change the step apply result to failure.

CLI Surface

The CLI uses the Platform env-settings admin endpoint and does not maintain a parallel local state file for these defaults.

stella crypto provider set <international|eidas|russian|chinese>
stella crypto provider show
stella crypto algorithm set --provider international --signing ecdsa-p256
stella crypto fips enable
stella crypto fips disable
stella crypto status

stella compliance nis2 enable
stella compliance nis2 disable
stella compliance dora enable
stella compliance dora disable
stella compliance cra enable
stella compliance cra disable
stella compliance status

Reads call GET /platform/envsettings/db/. Writes call PUT /platform/envsettings/db/{key} with the canonical key and a string value. Commands include X-Tenant-Id for consistency with the Platform admin API, but the current store is installation-scoped by key.

Per-Tenant Overrides

The Configuration page at /setup/system/cryptography resolves per-tenant compliance-pack state over the installation defaults above.

Persistence stays in platform.tenant_compliance_profile. Migration 069_TenantComplianceProfile.sql created that table as a single profile row per tenant with profile_id constrained to world, fips, gost, sm, kcmvp, or eidas; migration 070_TenantCompliancePackOverrides.sql extended the same row with nullable nis2_enabled, dora_enabled, and cra_enabled columns; migration 074_TenantCompliancePackOverridesCraSplit.sql (OPS-039) split the single CRA column into cra_product_security_enabled and cra_technical_documentation_enabled to match CRA Regulation (EU) 2024/2847 two-instrument conformity model and the frontend assurance-frameworks tiles.

NULL means the tenant has no override for that pack. true and false are explicit tenant overrides. This is intentional: clearing an override must fall back to a true installation default instead of hard-coding false.

Effective keyResolution
Compliance:Nis2:Enabledtenant nis2_enabled when non-null, else installation default, else false
Compliance:Dora:Enabledtenant dora_enabled when non-null, else installation default, else false
Compliance:Cra:ProductSecurity:Enabledtenant cra_product_security_enabled when non-null, else installation default, else false
Compliance:Cra:TechnicalDocumentation:Enabledtenant cra_technical_documentation_enabled when non-null, else installation default, else false

The legacy cra_enabled column is retained for rollback safety but is no longer read by the catalog. Migration 074 fans any historic single-CRA override out into both new columns on apply.

Backend API:

Endpoint shape is generated in Platform API Reference. Relevant operations:

The tenant crypto profile remains the single-value profile_id on the same table and is managed through the compliance-profile operations listed in the generated block above.

The operator-signing projection is deliberately narrower than the general crypto-provider read surface. It derives the canonical decision-signing algorithm through SignatureProviderSelection and returns { profileId, algorithmId, providerName, browserEnrollmentSupported, enrollmentMethod, enrollmentRoute, dataAsOf }. providerName is the exact verification-provider identity to persist beside an enrolled public key, not necessarily the holder that performs private-key signing. Catalog aliases are narrowed to pluginProviderId; profile defaults are default (world), fips.ecdsa.soft (FIPS), ru.bouncycastle.gost (GOST), and cn.sm.soft (SM). The GOST/SM verification adapters expose public-material verification only; private signing stays with the CLI/smartcard/HSM holder selected outside this projection (for example PKCS#11, CryptoPro, or SmRemote). The response exposes no provider health, preference list, secret, or key material. World and FIPS return browser-webcrypto//administration/profile; GOST and SM return cli-smartcard//docs/operator-signing-keys#cli-smartcard. IssuerDirectory performs the final live-registry capability check, so an unmounted or incompatible provider fails closed during enrollment rather than being silently replaced by another route. The dedicated OperatorSigningEnrollmentRead policy accepts the narrow authority:signing-keys.enroll scope (as well as crypto:read or ops.admin), so granting enrollment does not grant the rest of the crypto catalog.

Runtime Plugin Boundary

Current source-closure evidence (2026-06-06) lives under docs/qa/feature-checks/runs/pluginized-compose/platform-regional-crypto-boundary-20260606/. The scoped Platform WebService ProjectReference audit scans src/Platform/StellaOps.Platform.WebService/StellaOps.Platform.WebService.csproj and reports zero optional regional provider implementation violations.

When Crypto:CredentialStore:PrimitiveRouter=plugin is enabled, Platform now uses ConnectorCredentials/RegionalCryptoPluginServiceCollectionExtensions.cs to load required mounted bundles from Crypto:CredentialStore:RegionalPlugins:RootPath (default /app/plugins/crypto) and Crypto:CredentialStore:RegionalPlugins:Profile (default base). Each provider bundle is admitted through the shared StellaOps.Plugin.Security.SignedRuntimePluginAdmission chain before .NET loads any assembly. The configured trust root comes from Crypto:CredentialStore:RegionalPlugins:TrustRootPath, then Crypto:RuntimePlugins:TrustRootPath, then the Platform env overrides, with a default of /app/etc/certificates/trust-roots/plugins/crypto/cosign.pub.

Each provider bundle is expected at <root>/<profile>/<plugin-id>/manifest.json, with the current required IDs:

Plugin IDCredential AEAD capability
com.stellaops.crypto.smcredential-aead:SM4-GCM
com.stellaops.crypto.fipscredential-aead:AES-256-GCM
com.stellaops.crypto.gostcredential-aead:GOST-GCM

The manifest is authoritative for this host seam and must declare id equal to the bundle directory name, module crypto, the selected profile, contract version runtime-bundle.v1, the required credential AEAD capability, a bundle-relative assembly.path, assembly.sha256, and a conventional detached signature at <assembly>.sig (optionally recorded as assembly.signaturePath). entryTypes must identify exactly one concrete public ICryptoCapability when the assembly exposes more than one candidate. Platform validates identity, profile, contract version, declared capability, relative path containment, assembly SHA-256, and detached RSA/SHA-256 signature against the trust root before registering the provider behind the existing ICredentialAeadPluginAdapter bindings.

Platform-scoped acceptance evidence (2026-06-09) lives under docs/qa/feature-checks/runs/pluginized-compose/platform-regional-crypto-admission-20260609/. It covers signed good bundles, bad-signature fail-closed rejection, adjacent regional crypto dispatch tests, and the scoped ProjectReference audit. It does not claim DevOps compose/read-only mount or image-payload audit proof; shared compose and packaging files remain a separate acceptance lane.

All three endpoints require the existing platform.crypto.profile.admin policy (crypto:profile:admin or ops.admin). A tenantId query parameter is optional; when present it must match the envelope-bound tenant or Platform returns 404 tenant_not_found.

Writes and deletes emit platform.update_compliance_pack; audit details carry { tenant_id, pack_id, before, after }. The mutation response carries the same before/after values plus the merged configuration. The resolver also returns soft-prerequisite warnings with remediation routes so the UI can show non-blocking yellow strips for gaps such as missing NIS2 Authority mapping. These warnings are setup-readiness hints only. They are not live proof that NIS2, DORA, or CRA workflows produced compliant evidence.

Each pack also returns workflowReadiness and readinessGaps:

workflowReadinessMeaning
disabledThe effective pack value is false; no workflow evidence is expected.
configuration_requiredThe pack is enabled, but one or more setup prerequisites are missing.
evidence_pendingSetup prerequisites are present, but live upstream evidence still has to prove the workflow.

For NIS2, an enabled pack always carries readiness gaps for live effectiveness evidence and reachability evidence until those upstream workflows provide their own proof. The Platform setup/profile endpoints must not be used as a compliance conclusion or as a substitute for live NIS2 effectiveness, reachability, Notify delivery, or signed export evidence.

CLI mirror:

stella crypto profile set --tenant <id> <world|fips|gost|sm|kcmvp|eidas>
stella crypto profile show --tenant <id>
stella compliance nis2 enable --tenant <id>
stella compliance nis2 disable --tenant <id>
stella compliance dora enable --tenant <id>
stella compliance dora disable --tenant <id>
stella compliance cra enable --tenant <id>
stella compliance cra disable --tenant <id>
stella compliance status --tenant <id>

Without --tenant, stella compliance ... continues to manage the installation defaults in platform.environment_settings. With --tenant, the commands call the override/profile endpoints above and do not write the installation-default keys.

Secret Provider Registry

Where the regional-crypto algorithm routing above decides how a secret is sealed, the secret provider decides where it lives and which backend resolves it. Platform is one of the three services that wire AddRoutingSecretProvider and resolve secret references by URL (the others are StellaOps.Integrations.WebService and StellaOps.ReleaseOrchestrator.WebApi).

Admin / CLI surface:

GET  /api/v1/admin/crypto/secret-providers          # enumerate registry (crypto:read)
GET  /api/v1/admin/crypto/secret-providers/{id}/paths  # browse path NAMES only
GET  /api/v1/admin/setup/crypto/secret-provider/status # backend + KEK fingerprint
POST /api/v1/admin/setup/crypto/master-key/probe       # seal/open probe (crypto:kek:rotate)

stella crypto secret-provider list | status | validate | paths --provider <id>
stella crypto master-key probe | generate | import

These admin endpoints never return secret bytes (path names + non-secret KEK fingerprints only). Provider enumeration is partial-safe: if one configured provider’s non-secret describe probe fails, the endpoint still returns 200, marks only that provider reachable: false, and does not serialize the probe exception or its potentially sensitive detail. See: