Export Center Architecture

Derived from Epic 10 – Export Center and the subsequent export adapter deep dives.

The Export Center is the dedicated service layer that packages StellaOps evidence and policy overlays into reproducible bundles. It runs as a multi-surface API backed by asynchronous workers and format adapters, enforcing Aggregation-Only Contract (AOC) guardrails while providing deterministic manifests, signing, and distribution paths.

Product-facing assurance pack summaries live under NIS2 SoA export, NIS2 KPI dashboard, CRA technical file, and CRA conformity dossier. This document remains the ExportCenter implementation architecture and adapter owner map.

Runtime topology

Pluginized adapter-pack boundary

ExportCenter has canonical config/trust/scratch roots for pluginized runtime composition, and as of commit e35af41bef (2026-06-07, WS1) the signed mounted exporter-bundle loader is implemented: MountedExportAdapterRuntimePluginLoader admits the Trivy-DB / Trivy-Java-DB exporter bundles through a fail-closed admission chain and feeds the survivors into ExportAdapterRegistry. The docker-compose.plugins.exportcenter.yml overlay now mounts the signed recommended exporter bundle root in addition to config/trust/scratch:

PurposeHost pathContainer path
Signed exporter bundledevops/plugins/exportcenter/recommended/<bundle>/ (manifest.json + <assembly>.dll + <assembly>.dll.sig)/app/plugins/exportcenter/recommended/<bundle>
Operator config/data-pack registrydevops/etc/plugins/exportcenter//app/etc/plugins/exportcenter
Adapter trust rootdevops/etc/certificates/trust-roots/plugins/exportcenter/cosign.pub/app/etc/certificates/trust-roots/plugins/exportcenter/cosign.pub
Probe scratchnamed volume exportcenter-plugin-scratch/var/lib/stellaops/plugin-scratch/exportcenter

devops/etc/plugins/exportcenter/registry.yaml is the canonical config path for new composition work. The older devops/etc/plugins/export/registry.yaml file is retained as a legacy alias for unknown consumers and must not be removed until those consumers are audited.

Runtime plugin status is exposed by GET /internal/plugins/status and POST /internal/plugins/probe. Both are mapped AllowAnonymous (Program.cs:128-147) — they bypass the global export.viewer fallback policy, so the /internal/* prefix must be gateway-blocked from external traffic (repeated anonymous probe calls trigger a filesystem plugin-admission scan and are a minor DoS/enumeration vector). The service registers host-owned deterministic adapters by default (json:raw, json:policy, mirror:standard, and runtime:combined) and, when the overlay is applied, the mounted signed Trivy exporters via the loader. Declarative entries such as assurance.profile-pack and standards-mapping-v1 are data packs and cannot execute code. The decided first slice of signed mounted exporters is Trivy DB + Trivy Java DB only (stellaops.exportcenter.trivy-db, stellaops.exportcenter.trivy-java-db); NIS2 is a built-in typed ExportCenter route implemented in Core, and CRA/PDF/custom regulatory adapters are deferred optional bundle-producer work. Until those future optional adapters are staged, they report not-mounted and non-required.

See Mounted exporter-bundle loader below for the full admission chain, config keys, producer, and overlay.

The 2026-06-12 boundary slice landed for NIS2 only (corrected 2026-07-12 against src/):

Mounted exporter-bundle loader (signed bundle admission)

MountedExportAdapterRuntimePluginLoader (src/ExportCenter/StellaOps.ExportCenter/StellaOps.ExportCenter.Core/Adapters/MountedExportAdapterRuntimePluginLoader.cs, commit e35af41bef) is wired into StellaOps.ExportCenter.WebService/Program.cs via AddMountedExportAdapters. It is fail-closed: a bundle is only handed to ExportAdapterRegistry after its assembly clears the full admission chain, and every reject path registers zero adapters (asserted by the 22 loader tests + a live forcing-function; the pre-existing ExportAdapterRegistryTests stayed 20/20).

Admission chain (per bundle directory):

  1. Manifest bindingmanifest.json must declare module=exportcenter, contractVersion=runtime-bundle.v1, the matching profile, capability exportcenter:adapter, and a well-formed assembly descriptor (relative path + sha256); id must equal the bundle directory name.
  2. Payload + per-DLL SHA-256 + path-traversal guard — the manifest payload digest and each DLL are hashed; rooted/escaping paths are rejected.
  3. Detached RSA-PKCS1-SHA256 verification<assembly>.sig is verified via OfflineDevRsaSha256PluginVerifier (AllowUnsigned=false) against the mounted trust root, then the IExportAdapter entry type(s) are activated via ActivatorUtilities.

The loader installs an AssemblyLoadContext.Default.Resolving hook so the transitive StellaOps.* dependencies a mounted adapter carries (which are not on the host’s TPA list) resolve at activation — this is the “default ALC only probes the TPA list” trap, and the commit proves the fix with an integration test using a real absent-from-host transitive dep.

Configuration (ExportCenter:RuntimePlugins):

KeyDefaultPurpose
ExportCenter:RuntimePlugins:RootPath/app/plugins/exportcenterRoot containing profile directories.
ExportCenter:RuntimePlugins:ProfilerecommendedProfile directory (<RootPath>/<Profile>).
ExportCenter:RuntimePlugins:TrustRootPath/app/etc/certificates/trust-roots/plugins/exportcenter/cosign.pubTrust-root public key.
ExportCenter:RuntimePlugins:AllowUnsignedfalseDevelopment-only escape hatch; MUST stay false in production.

Module/contract constants live in MountedExportAdapterTargets (module exportcenter, probe contract exportcenter.adapter.v1, manifest contract runtime-bundle.v1, capability exportcenter:adapter).

Bundle producer: devops/build/package-runtime-plugins.ps1 -Module exportcenter -Profile recommended -UseOfflineDevSigner publishes the StellaOps.ExportCenter.Core assembly (which carries the Trivy adapters) as the signed stellaops.exportcenter.trivy-db and stellaops.exportcenter.trivy-java-db bundles. The generated cosign.pub is git-ignored under the exportcenter trust-root directory.

Opt-in compose overlay: devops/compose/docker-compose.plugins.exportcenter.yml layers read-only mounts of the recommended bundle root + trust root onto export-web and export-worker, and restates the loader defaults (ExportCenter__RuntimePlugins__RootPath/Profile/TrustRootPath, AllowUnsigned=false). Apply with COMPOSE_EXTRA_FILES=docker-compose.plugins.exportcenter.yml ./scripts/compose-cli.ps1 up.

Probe / status surface: ExportAdapterPluginProbeReporter reflects the live registry, so GET /internal/plugins/status and POST /internal/plugins/probe show admitted bundles and surface rejected bundles with their reason (no silent-green).

Live runtime probe is pending. The loader, the 22 tests + live forcing-function, and the overlay are committed (e35af41bef), but a live overlay-up acceptance probe against a running export-web (mount the signed Trivy bundles, confirm /internal/plugins/probe reports them admitted) has not yet been recorded.

Truthful unsupported surface classification

The following Unsupported* services are intentional non-testing runtime behavior, not hidden fallback state. They are acceptable shipped truth because they fail closed instead of fabricating persisted success, but each still maps to a future durable-backend workstream.

Runtime surfaceCurrent serviceCurrent behavior outside TestingClassification
Verification artifact readbackPostgresExportVerificationArtifactStoreReads run metadata, manifest/signature bytes, artifact payloads, expected hashes, content types, and encryption flags from durable Postgres verification tables.Durable for subfeature B as of SPRINT_20260612_011; missing artifacts return 404 or verification errors rather than fabricated success.
Incident management endpointsPostgresExportIncidentManagerPersists incident activation/update/resolution lifecycle state and update history in Postgres, and publishes durable timeline events through PostgresExportNotificationSink.Durable for subfeature C as of SPRINT_20260612_011; testing-only in-memory management stays behind Export:UseInMemoryIncidentManager.
Risk bundle job orchestrationRiskBundleJobHandler + PostgresRiskBundleJobStore + StellaOps.ExportCenter.WorkerNon-testing runtime persists risk-bundle job state and lifecycle events in PostgreSQL (risk_bundle_jobs, risk_bundle_job_events) and no longer returns the previous exportcenter.risk_bundles.not_implemented 501 default.API-submitted jobs are queued as durable pending records; RiskBundleWorker leases them and materializes bundles through the worker-hosted RiskBundleJob.
Simulation exportUnsupportedSimulationReportExporterReturns 501 problem+json for simulation export endpoints with error_code=exportcenter.simulation_exports.not_implemented.ROADMAP; acceptable truthful fail-closed behavior until durable simulation export runtime exists.
Exception report generationExceptionReportGenerator + PostgresExceptionReportJobStoreCreates durable report jobs, stores status/progress and generated report bytes, and supports list/status/download readback across process restarts.Durable for subfeature F as of SPRINT_20260612_011; testing-only in-memory generation stays behind Export:UseInMemoryExceptionReportGenerator.
Timeline publication sinkPostgresExportNotificationSinkPersists timeline notification envelopes, dedupe keys, delivery state, attempt counts, and serialized payloads in export_center.export_timeline_notifications.Durable for subfeature A as of SPRINT_20260612_011; testing-only in-memory publication stays behind Export:UseInMemoryTimelineNotificationSink.
Verification helper artifact storeUnsupportedVerificationArtifactStoreThrows NotSupportedException from generic default verification helper calls when no durable IExportArtifactStore is supplied. Non-testing StellaOps.ExportCenter.WebService registers PostgresExportVerificationArtifactStore instead.Acceptable fail-closed helper default; production runtime now uses durable Postgres verification readback.
Distribution helper repository defaultUnsupportedExportDistributionRepositoryThrows NotSupportedException from generic default helper calls when no durable IExportDistributionRepository is supplied. Non-testing StellaOps.ExportCenter.WebService registers PostgresExportDistributionRepository instead.Acceptable fail-closed helper default; production runtime now uses durable Postgres distribution state.
OCI referrer discovery helper defaultDisabledReferrerDiscoveryServiceReports discovery/capability probing as not configured for generic adapter-only helper registrations. Non-testing StellaOps.ExportCenter.WebService registers OciReferrerDiscoveryService instead.Acceptable helper default; production runtime now uses real OCI referrer discovery.

Integration peers

Gap remediation (EC1–EC10)

Job lifecycle

Profile model note. A canonical export_profiles row has a kind (ExportProfileKind: AdHoc, Scheduled, EventDriven, Continuous) plus a separate JSON format block (ExportFormatOptions.format is an ExportFormat: JsonRaw, JsonPolicy, Ndjson, Csv, Mirror, TrivyDb, TrivyJavaDb) and a scope block. The colon-delimited identifiers below (json:raw, adapter:trivy:db, etc.) are adapter ids (IExportAdapter.AdapterId), not profile kind values — see Adapter responsibilities for the authoritative adapter-id list. Beware the near-collision: the two Trivy adapter ids carry an adapter: prefix (adapter:trivy:db, adapter:trivy:java-db), while the unprefixed trivy:db / trivy:java-db are the profile names used in profiles.mdand overview.md. Assurance profile ids (nis2.*, cra.*, dora.*) belong to the separate Assurance/NIS2 typed routes and registry, not to the generic export_profiles store.

  1. Profile selection. Operator or automation picks a profile mapped (via its format/adapter) to one of the registered adapters (adapter ids json:raw, json:policy, runtime:combined, adapter:trivy:db, adapter:trivy:java-db, mirror:standard, opt-in mirror:delta), or invokes an Assurance profile such as nis2.statement-of-applicability, nis2.effectiveness-report, dora.roi, dora.major-incident-report, dora.info-sharing, cra.technical-file, or cra.conformity-dossier through the Assurance/NIS2 routes, and submits scope selectors. The generic ExportScope supports targetKinds, sourceRefs, tags, namespaces, dateRange, maxItems, sampling, runIds, and excludePatterns (see Core/Planner/ExportScopeModels.cs). DORA RoI can materialize from source-supplied dora-roi.v1 register rows plus an owner approval reference; DORA major-incident and information-sharing can materialize from request-supplied Notify connector inputs; TLPT delegates to EvidenceLocker. See profiles.mdfor profile definitions and configuration fields.
  2. Run creation. POST /v1/exports/profiles/{profileId}/runs validates the profile is Active, enforces concurrency, and writes an export_run. The run is admitted as Running immediately, or Queued when the tenant/profile concurrency limits are exceeded and QueueExcessRuns is enabled (defaults: MaxConcurrentRunsPerTenant=4, MaxConcurrentRunsPerProfile=2, MaxQueueSizePerTenant=10). Over-limit requests without queueing return 429. The run trigger is recorded as Api for this path.
  3. (Roadmap) Adapter execution. In the target design the worker streams data from Findings Ledger and Policy Engine using pagination cursors, and adapters write canonical payloads to staging storage, compute checksums, and emit progress. In the current build run progress (totalItems/processedItems/failedItems/totalSizeBytes) is surfaced through the run record and the SSE stream, but no generic adapter-execution worker host drives it yet (see runtime topology note).
  4. Manifest and provenance emission. Manifest/provenance assembly writes export.json and provenance.json and signs them. Verification readback of these artifacts is served by IExportArtifactStore; outside Testing the WebService registers PostgresExportVerificationArtifactStore, which reads export_center.export_verification_runs and export_center.export_verification_artifacts rather than buffering artifacts in process.
  5. Distribution registration. Distribution artefacts (download URL, OCI reference, object storage path) are tracked as export_distributions rows.
  6. Download & verification. Clients list artifacts (GET /v1/exports/runs/{runId}/artifacts), download them (.../artifacts/{artifactId}/download), and verify runs (POST /v1/exports/runs/{runId}/verify, GET .../verify/manifest, GET .../verify/attestation).

Cancellation (POST /v1/exports/runs/{runId}/cancel) transitions an in-flight run to the Cancelled status (ExportRunStatus.Cancelled) and records an audit entry; cancellation only succeeds from a cancellable state (otherwise 400).

Core components

API surface

All endpoints require Authority-issued JWT (+ DPoP) tokens with the appropriate export scope and tenant claim alignment. The real export scopes (StellaOps.Auth.Abstractions.StellaOpsScopes) are export.viewer (read-only access to runs and bundles), export.operator (create/update profiles, start/cancel runs), and export.admin (archive profiles; administrative control over retention, encryption keys, and scheduling policies) — these map to the StellaOpsResourceServerPolicies.ExportViewer/ExportOperator/ExportAdmin authorization policies. The colon-form scopes (export:run, export:read, export:download, export:profile:manage) used in earlier drafts do not exist. A global authorization fallback policy requires export.viewer on every request (health probes opt out via AllowAnonymous). The default WebService resource-server allowlist is the Authority standard bootstrap tenant default; local compose also sets Authority__ResourceServer__RequiredTenants__0=default so browser console tokens and ExportCenter tenant policy stay aligned.

Assurance export profile registry

ExportCenter exposes a small built-in registry for optional Assurance-pack exports. The registry uses the generic assurance-evidence-export-v1 descriptor so Web and CLI setup flows can discover available profiles without hard-coding NIS2-only or CRA-only concepts.

Profile idFrameworkPackAdapter idPayload schemaCompatibility routes
cra.conformity-dossiercracra.technical-documentationcra:conformity-dossierconformity-dossier-v1stella export conformity-dossier
cra.technical-filecracra.technical-documentationcra:tech-filecra-tech-file-v1stella export cra-tech-file
dora.info-sharingdoradoraexportcenter.dora.info-sharingdora-info-sharing-event-v1stella export dora-info-sharing, stella verify dora-info-sharing
dora.major-incident-reportdoradoraexportcenter.dora.major-incident-reportdora-major-incident-report.v1stella export dora-incident-report, stella verify dora-incident-report
dora.roidoradoraexportcenter.dora.roidora-roi-v1stella export dora-roi, stella verify dora-roi
dora.tlpt-evidence-packdoradoraexportcenter.dora.tlpt-evidence-packtlpt-evidence-pack.v1stella tlpt pack, stella verify tlpt-pack
nis2.effectiveness-reportnis2nis2exportcenter.nis2.effectiveness-reportnis2-effectiveness-report-v1stella export nis2-effectiveness-report, POST /v1/exports/assurance/profiles/nis2.effectiveness-report/runs
nis2.statement-of-applicabilitynis2nis2exportcenter.nis2.soanis2-soa-v1/v1/exports/nis2/soa/*

The DORA profiles are advertised with fail-closed readiness. dora.roi is HTTP-expressible when the request supplies the complete Findings dora-roi.v1 register and owner approval reference; missing source rows or approval keep readiness blocked for that request. The produced ZIP uses the same CLI wrapper manifest/signature shape for deterministic offline HMAC byte parity; HTTP-only approval/source refs are not artifact fields. dora.major-incident-report and dora.info-sharing are HTTP-expressible only when the request supplies the existing Notify connector inputs and channel state; ExportCenter maps JSON-friendly DTOs into Notify contracts and must not invent source workflow data. dora.tlpt-evidence-pack is HTTP-expressible only through the EvidenceLocker TLPT request shape and sealed response; ExportCenter does not assemble or sign the pack locally.

Request-aware readiness transformation is owned by the internal AssuranceProduceReadinessResolver. It starts from the registry’s fail-closed result, replaces only the DORA missing-input reasons actually satisfied by the request, preserves unrelated blockers, and ordinal-sorts the final reason set. AssuranceBundleProduceService retains profile dispatch, bundle production, signing gates, and durable run/artifact persistence; the extraction does not change the public readiness or produce contracts.

Readiness uses sealed ExportCenter configuration only as reason codes. NIS2 SoA readiness checks Authority profile access, signed-bundle storage, signing key/provider, and tenant export trust roots. DORA readiness fails closed on the owning workflow prerequisites listed above; it is a readiness descriptor, not a legal-compliance or regulator-submission workflow. CRA technical-documentation readiness checks signed-export storage, signing key/provider, and tenant export trust roots, and separately reports live-publication preflight (security-mailbox-unverified, intake-key-expired-or-missing, rotation-roles-missing, support-lifecycle-metadata-missing). A CRA profile can therefore be export-ready while livePublicationReady=false.

Worker pipeline

Status. This subsection describes the target generic export pipeline. The shipped StellaOps.ExportCenter.Worker host (see Runtime topology) currently runs the DevPortal-offline job and the durable risk-bundle queue worker; the generic input-resolver -> adapter-host -> content-writer flow below is implemented at the library level (Core/Adapters, Core/Planner) but is not yet driven by an Orchestrator-leased worker loop.

Data model

The schema is PostgreSQL, in the export_center schema, created by the embedded migration 001_initial_schema.sql (StellaOps.ExportCenter.Infrastructure/Db/Migrations/). Migrations are embedded resources (<EmbeddedResource Include="Db\Migrations\**\*.sql" />) and applied on startup by ExportCenterMigrationHostedServiceExportCenterMigrationRunner, which records each applied script (with SHA-256 checksum) in export_center.export_schema_version and refuses to start on a checksum mismatch. Startup application is gated by ExportCenter:Database:ApplyMigrationsAtStartup (default true). This is a service-specific runner, not the shared AddStartupMigrations helper. Every table enables row-level security with an *_isolation policy keyed off export_center_app.require_current_tenant() (app.current_tenant session setting).

Risk-bundle job state is stored by embedded migration 009_risk_bundle_jobs.sql in export_center.risk_bundle_jobs and export_center.risk_bundle_job_events. The WebService opens these rows under the operator/system RLS tenant (00000000-0000-0000-0000-000000000000) and preserves the requested tenant string in tenant_ref, matching the incident-manager pattern for operator-scoped state.

TablePurposeKey columnsNotes
export_profilesProfile definitions.profile_id (PK), tenant_id, name, description, kind (smallint 1–4), status (smallint 1–4), scope_json, format_json, signing_json, schedule, created_at, updated_at, archived_at.kind = AdHoc/Scheduled/EventDriven/Continuous; status = Draft/Active/Paused/Archived. Format/variant lives in format_json (ExportFormatOptions), not kind. Unique (tenant_id, name) where archived_at IS NULL; updated_at maintained by trigger.
export_runsRun state machine and audit info.run_id (PK), profile_id (FK), tenant_id, status (smallint 1–6), trigger (smallint 1–4), correlation_id, initiated_by, total_items, processed_items, failed_items, total_size_bytes, error_json, created_at, started_at, completed_at, expires_at.status = Queued/Running/Completed/PartiallyCompleted/Failed/Cancelled; trigger = Manual/Scheduled/Event/Api. No selectors, policy_snapshot_id, duration_ms, or error_code columns (errors are serialised into error_json).
export_inputsResolved input items per run.input_id (PK), run_id (FK, cascade), tenant_id, kind (smallint 1–8), status (smallint 1–5), source_ref, name, content_hash (sha256), size_bytes, metadata_json, error_json, created_at, processed_at.Enables resumable retries and audit.
export_distributionsDistribution artefacts (and the backing store for the artifact API).distribution_id (PK), run_id (FK, cascade), tenant_id, kind (smallint 1–5 in SQL), status (smallint 1–6), target, artifact_path, artifact_hash (sha256), size_bytes, content_type, metadata_json, error_json, attempt_count, created_at, distributed_at, verified_at.ExportDistributionKind enum spans FileSystem/AmazonS3/Mirror/OfflineKit/Webhook/OciRegistry/AzureBlob/GoogleCloudStorage; the domain model also carries OCI/retention/idempotency fields. The IExportArtifactRepository artifact endpoints project over these rows — there is no separate export_artifacts table.
export_timeline_notificationsDurable timeline notification sink.event_id (PK), tenant_id, tenant_ref, channel, event_type, payload_json, delivery_state, attempt_count, published_at, delivered_at, last_error.Used by PostgresExportNotificationSink; duplicate event ids update the same row and increment attempts.
export_incidentsDurable incident lifecycle state.incident_id (PK), tenant_id, tenant_ref, type, severity, status, summary, description, affected_tenants_json, affected_profiles_json, activated_at, last_updated_at, resolved_at, activated_by, resolved_by, correlation_id, metadata_json.Used by PostgresExportIncidentManager; rows are operator-scope incident records while affected tenants/profiles remain payload fields.
export_incident_updatesDurable incident update trail.(incident_id, update_id) (PK), tenant_id, timestamp, previous_status, new_status, previous_severity, new_severity, message, updated_by.Ordered update history for activate/update/resolve operations; cascades with its incident row.
export_schema_versionMigration bookkeeping.version (PK), script_name, script_checksum, applied_at_utc.Created and written by the migration runner (ExportCenterMigrationRunner), not by 001_initial_schema.sql; used for checksum validation.

There is no export_events table — run timeline frames are produced live by the SSE handler (ExportRunSseEventTypes), not persisted as events. Audit actions are emitted through the unified audit emission pipeline to the Timeline service.

Audit bundles (immutable triage exports)

Audit bundles are a specialized Export Center output: a deterministic, immutable evidence pack for a single subject (and optional time window) suitable for audits and incident response.

The Web Export Center quick action for Export StellaBundle is expected to use this audit-bundle surface directly. On successful completion the UI must carry the canonical bundleId through the /evidence/exports/bundles handoff, not a synthetic export-run placeholder, so the operator lands on the real generated bundle inventory and can immediately download, verify, or inspect provenance.

Adapter responsibilities

Adapters expose structured telemetry events (adapter.start, adapter.chunk, adapter.complete) with record counts and byte totals per chunk. Failures emit adapter.error with reason codes.

Signing and provenance

OCI Referrer Discovery

Mirror bundles automatically discover and include OCI referrer artifacts (SBOMs, attestations, signatures, VEX statements) linked to container images via the OCI 1.1 referrers API.

Discovery Flow

┌─────────────────┐     ┌───────────────────────┐     ┌─────────────────┐
│  MirrorAdapter  │────▶│  IReferrerDiscovery   │────▶│   OCI Registry  │
│                 │     │  Service              │     │                 │
│  1. Detect      │     │  2. Probe registry    │     │  3. Query       │
│     images      │     │     capabilities      │     │     referrers   │
│                 │     │                       │     │     API         │
└─────────────────┘     └───────────────────────┘     └─────────────────┘
                                   │
                                   ▼
                        ┌───────────────────────┐
                        │  Fallback: Tag-based  │
                        │  discovery for older  │
                        │  registries (GHCR)    │
                        └───────────────────────┘

Capability Probing

Before starting referrer discovery, the export flow probes each unique registry to determine capabilities:

Capabilities are cached per registry host with a 1-hour TTL.

Logging at export start:

[INFO] Probing 3 registries for OCI referrer capabilities before export
[INFO] Registry registry.example.com: OCI 1.1 (referrers API supported, version=OCI-Distribution/2.1, probe_ms=42)
[WARN] Registry ghcr.io: OCI 1.0 (using fallback tag discovery, version=registry/2.0, probe_ms=85)

Telemetry Metrics

MetricDescriptionTags
export_registry_capabilities_probed_totalRegistry capability probe operationsregistry, api_supported
export_referrer_discovery_method_totalDiscovery operations by methodregistry, method (native/fallback)
export_referrers_discovered_totalReferrers discoveredregistry, artifact_type
export_referrer_discovery_failures_totalDiscovery failuresregistry, error_type

Artifact Type Mapping

OCI Artifact TypeBundle CategoryExample
application/vnd.cyclonedx+jsonsbomCycloneDX SBOM
application/vnd.spdx+jsonsbomSPDX SBOM
application/vnd.openvex+jsonvexOpenVEX statement
application/vnd.csaf+jsonvexCSAF document
application/vnd.in-toto+jsonattestationin-toto attestation
application/vnd.dsse.envelope+jsonattestationDSSE envelope
application/vnd.slsa.provenance+jsonattestationSLSA provenance

Error Handling

Distribution flows

Observability

Security posture

Deployment considerations

Compliance checklist

Client capability surfacing

The ExportSurfacingClient extends the existing ExportCenterClient to expose backend capabilities that were previously not reachable from CLI/UI consumers.

Surfaced Capabilities

CapabilityInterface MethodRoute
Profile CRUDCreateProfileAsync, UpdateProfileAsync, ArchiveProfileAsyncPOST/PUT/DELETE /v1/exports/profiles
Run LifecycleStartRunAsync, CancelRunAsyncPOST /profiles/{id}/runs, POST /runs/{id}/cancel
Artifact BrowsingListArtifactsAsync, GetArtifactAsync, DownloadArtifactAsyncGET /runs/{id}/artifacts
VerificationVerifyRunAsync, GetManifestAsync, GetAttestationStatusAsyncPOST /runs/{id}/verify, GET .../manifest, GET .../attestation
Assurance ExportsListAssuranceExportProfilesAsync, GetAssuranceExportProfileAsync, GetAssuranceExportReadinessAsyncGET /v1/exports/assurance/profiles, GET /v1/exports/assurance/profiles/{id}, GET /v1/exports/assurance/profiles/{id}/readiness
DiscoveryDiscoverCapabilitiesAsyncLocal capability catalogue

Key Types

TypeLocationPurpose
IExportSurfacingClientClient/IExportSurfacingClient.csInterface for extended operations
ExportSurfacingClientClient/ExportSurfacingClient.csHTTP implementation
ExportSurfacingModels.csClient/Models/DTOs for profile CRUD, artifacts, verification, attestation status, capability discovery
AssuranceExportModels.csClient/Models/DTOs for Assurance profile discovery and readiness

DI Registration

AddExportSurfacingClient(Action<ExportCenterClientOptions>) in ServiceCollectionExtensions.cs — reuses the same ExportCenterClientOptions.

Test Coverage