StellaOps Export Center

Export Center is the StellaOps service that packages reproducible evidence bundles — canonical JSON, Trivy DB, and OCI-style mirrors — with provenance metadata and optional signing, for offline, air-gapped, or mirrored deployments.

Audience: operators producing and verifying exports for Offline Kit and mirror parity, and automation authors driving export jobs through the API or CLI.

Recent changes

Responsibilities

Key components

Profiles at a glance

Profile kinds are stored on ExportProfile.Kind (ExportProfileKind enum: AdHoc=1, Scheduled=2, EventDriven=3, Continuous=4). The output adapter chosen for a profile is identified by an adapter id (IExportAdapter.AdapterId / .Id):

API surface

All routes require an OAuth scope via RequireAuthorization. The three export scopes are defined in StellaOpsScopes (export.viewer, export.operator, export.admin) and surfaced as policies StellaOpsResourceServerPolicies.ExportViewer/ExportOperator/ExportAdmin. The global fallback policy in Program.cs requires export.viewer on every authenticated request; /healthz, /readyz, OpenAPI, build-info, and discovery endpoints are AllowAnonymous.

Core export API (ExportApiEndpoints, base group /v1/exports):

Method & routeScopePurpose
GET /v1/exports/profilesexport.viewerList profiles (filters: status, kind, search, offset, limit; limit clamped 1–100, default 50)
GET /v1/exports/profiles/{profileId:guid}export.viewerGet a profile
POST /v1/exports/profilesexport.operatorCreate a profile (audited)
PUT /v1/exports/profiles/{profileId:guid}export.operatorUpdate a profile (audited)
DELETE /v1/exports/profiles/{profileId:guid}export.adminArchive (soft-delete) a profile (audited)
POST /v1/exports/profiles/{profileId:guid}/runsexport.operatorStart a run (profile must be Active; returns 202 Accepted, 429 on concurrency limits)
GET /v1/exports/runsexport.viewerList runs (filters: profileId, status, trigger, createdAfter, createdBefore, correlationId, offset, limit)
GET /v1/exports/runs/{runId:guid}export.viewerGet a run with its artifacts
POST /v1/exports/runs/{runId:guid}/cancelexport.operatorCancel a queued/running run (audited)
GET /v1/exports/runs/{runId:guid}/artifactsexport.viewerList artifacts for a run
GET /v1/exports/runs/{runId:guid}/artifacts/{artifactId:guid}export.viewerGet artifact metadata
GET /v1/exports/runs/{runId:guid}/artifacts/{artifactId:guid}/downloadexport.viewerStream the artifact file (audited)
GET /v1/exports/runs/{runId:guid}/eventsexport.viewerSSE stream of run events (text/event-stream)
POST /v1/exports/runs/{runId:guid}/verifyexport.viewerVerifies durable run metadata, manifest/signature, and artifact payloads from the Postgres verification artifact store
GET /v1/exports/runs/{runId:guid}/verify/manifestexport.viewerReads the persisted export manifest from the Postgres verification artifact store; returns 404 when no manifest is recorded
GET /v1/exports/runs/{runId:guid}/verify/attestationexport.viewerAttestation status (HasAttestation, AttestationType = DSSE, ManifestDigest)
POST /v1/exports/runs/{runId:guid}/verify/streamexport.viewerStreams verification progress over persisted verification artifacts

Attestation (AttestationEndpoints, group /v1/exports, scope export.viewer): GET /{id}/attestation, GET /attestations/{attestationId}, POST /{id}/attestation/verify.

Promotion attestation (PromotionAttestationEndpoints, group /v1/promotions): POST /attestations (export.operator), GET /attestations/{assemblyId}, GET /{promotionId}/attestations, POST /attestations/{assemblyId}/verify, GET /attestations/{assemblyId}/bundle.

Incidents (IncidentEndpoints, group /v1/incidents): GET /status, GET "", GET /recent, GET /{id} (export.viewer); POST "" activate, PATCH /{id} update, POST /{id}/resolve (export.operator). Non-testing runtime uses PostgresExportIncidentManager with durable incident/update rows and timeline publication.

Risk bundles (RiskBundleEndpoints, group /v1/risk-bundles): GET /providers, GET /jobs, GET /jobs/{jobId} (export.viewer); POST /jobs submit, POST /jobs/{jobId}/cancel (export.operator). Current non-testing runtime uses PostgreSQL-backed job state and lifecycle events (risk_bundle_jobs, risk_bundle_job_events) instead of the previous 501 default. API-submitted jobs create durable Pending records; StellaOps.ExportCenter.Worker leases pending or expired Running jobs and materializes bundles through RiskBundleJob.

Simulation exports (SimulationExportEndpoints, group /v1/exports/simulations): GET "", GET /{exportId}, GET /{simulationId}/stream, GET /{simulationId}/csv (export.viewer); POST "" (export.operator). Current non-testing runtime returns 501 with error_code=exportcenter.simulation_exports.not_implemented.

Audit bundles (AuditBundleEndpoints, group /v1/audit-bundles): POST "" (export.operator); GET "", GET /{bundleId}, GET /{bundleId}/download, GET /{bundleId}/index (export.viewer).

Exception reports (ExceptionReportEndpoints, group /v1/exports/exceptions): POST / (export.operator); GET /, GET /{jobId}, GET /{jobId}/download (export.viewer). Non-testing runtime uses durable Postgres job state and report-content persistence; testing-only in-memory generation remains behind Export:UseInMemoryExceptionReportGenerator.

Lineage evidence packs (LineageExportEndpoints, group /api/v1/lineage): POST /export and POST /export/{packId}/sign (export.operator); GET /export/{packId}, GET /export/{packId}/download, POST /export/{packId}/verify (export.viewer).

Assurance (AssuranceExportEndpoints, group /v1/exports/assurance, export.viewer): GET /profiles, GET /profiles/{profileId}, GET /profiles/{profileId}/readiness. NIS2 SoA (Nis2SoaExportEndpoints, group /v1/exports/nis2/soa): GET /profile (export.viewer), POST /runs (export.operator), GET /runs/{runId:guid}, GET /runs/{runId:guid}/bundle (export.viewer).

Legacy GET/POST /exports and DELETE /exports/{id} are deprecated and now return 410 Gone (LegacyExportEndpointGone); use the /v1/exports/* routes instead.

Persistence

PostgreSQL schema export_center (+ export_center_app for helper functions), auto-migrated on startup when ExportCenter:Database:ApplyMigrationsAtStartup is true (default). Migration 001_initial_schema.sql creates four row-level-security-isolated tables (tenant_id = export_center_app.require_current_tenant()):

Migration 006_verification_artifacts_exception_reports.sql adds durable backend state for the first ExportCenter unsupported-subfeature slice:

Migration 007_timeline_notifications.sql adds durable state for timeline publication:

Migration 008_export_incidents.sql adds durable state for incident management:

Domain enums (Core Domain): ExportRunStatus (Queued=1, Running=2, Completed=3, PartiallyCompleted=4, Failed=5, Cancelled=6); ExportRunTrigger (Manual=1, Scheduled=2, Event=3, Api=4). API-started runs use Trigger = Api and expire 7 days after creation.

Configuration

Root section ExportCenter (ExportCenterOptions): Database.ConnectionString (required; or ConnectionStrings:ExportCenter/ConnectionStrings:Default), Database.ApplyMigrationsAtStartup (default true), ObjectStore (Kind = FileSystem/AmazonS3, RootPath, BucketName, Region), Timeline (Enabled, Endpoint, RequestTimeoutSeconds, Source default stellaops.export-center), Signing (Enabled default true, Algorithm default ES256, KeyId, Provider), and Quotas (MaxConcurrentExports default 10, MaxExportSizeBytes default 1 GiB, DefaultRetentionDays default 90).

Pluginized Runtime Composition

ExportCenter uses the shared plugin probe surface for adapter/data-pack classification:

Compose plugin overlays mount the canonical config, trust, and scratch roots:

PurposeHost pathContainer path
Operator config/data-pack registrydevops/etc/plugins/exportcenter//app/etc/plugins/exportcenter
Adapter trust rootsdevops/etc/certificates/trust-roots/plugins/exportcenter//app/trust-roots/plugins/exportcenter
Probe scratchnamed volume/var/lib/stellaops/plugin-scratch/exportcenter

devops/etc/plugins/exportcenter/registry.yaml is the canonical compose-era registry. devops/etc/plugins/export/registry.yaml is retained as a legacy alias and must not be deleted until all consumers are audited. Host adapters (json:raw, json:policy, mirror:standard, runtime:combined) remain registered by the service. Declarative packs such as assurance.profile-pack and standards-mapping-v1 remain data/config and do not execute code. The first signed mounted ExportCenter adapter profile is the recommended Trivy DB / Trivy Java DB bundle set under /app/plugins/exportcenter/recommended; CRA/PDF/custom regulatory adapters remain future optional producer work and must ship signed bundle artifacts, runtime loader admission, image audit proof, and live probe evidence before they can be treated as loaded plugins.

Current source-coupling state: the WebService project no longer references the CRA or NIS2 adapter implementation assemblies. NIS2 is not an optional mounted adapter in this slice; it is a built-in typed ExportCenter route implemented in Core, with a compatibility type-forwarder assembly for existing consumers.

Telemetry

Meter and activity source StellaOps.ExportCenter. Representative metrics (ExportTelemetry): counters export_runs_total, export_runs_success_total, export_runs_failed_total, export_artifacts_total, export_bytes_total, export_artifact_downloads_total, export_sse_connections_total, export_concurrency_limit_exceeded_total, export_timeline_events_published_total/_failed_total/_deduplicated_total, export_incidents_activated_total/_resolved_total/_escalated_total/_deescalated_total, export_risk_bundle_jobs_submitted_total/_completed_total, export_simulation_exports_total, export_audit_events_total, export_registry_capabilities_probed_total, export_referrer_discovery_method_total, export_referrers_discovered_total, export_referrer_discovery_failures_total; histograms export_run_duration_seconds, export_plan_duration_seconds, export_bundle_size_bytes, export_incident_duration_seconds, export_risk_bundle_job_duration_seconds; up/down counter export_runs_in_progress.

Integrations & dependencies

Operational notes

Remaining truthful unsupported runtime surfaces

Backlog references

Epic alignment

Implementation Status

Delivery Phases

Acceptance Criteria

Key Risks & Mitigations