8.3 KiB
architecture_excititor_mirrors.md — Excititor Mirror Distribution
Status: Draft (Sprint 7). Complements
docs/ARCHITECTURE_EXCITITOR.mdby describing the mirror export surface exposed byExcititor.WebServiceand the configuration hooks used by operators and downstream mirrors.
0) Purpose
Excititor publishes canonical VEX consensus data. Operators (or StellaOps-managed mirrors) need a deterministic way to sync those exports into downstream environments. Mirror distribution provides:
- A declarative map of export bundles (
json,jsonl,openvex,csaf) reachable via signed HTTP endpoints under/excititor/mirror. - Thin quota/authentication controls on top of the existing export cache so mirrors cannot starve the web service.
- Stable payload shapes that downstream automation can monitor (index → fetch updates → download artifact → verify signature).
Mirror endpoints are intentionally read-only. Write paths (export generation, attestation, cache) remain the responsibility of the export pipeline.
1) Configuration model
The web service reads mirror configuration from Excititor:Mirror (YAML/JSON/appsettings). Each domain groups a set of exports that share rate limits and authentication rules.
Excititor:
Mirror:
Domains:
- id: primary
displayName: Primary Mirror
requireAuthentication: false
maxIndexRequestsPerHour: 600
maxDownloadRequestsPerHour: 1200
exports:
- key: consensus
format: json
filters:
vulnId: CVE-2025-0001
productKey: pkg:test/demo
sort:
createdAt: false # descending
limit: 1000
- key: consensus-openvex
format: openvex
filters:
vulnId: CVE-2025-0001
Root settings
| Field | Required | Description |
|---|---|---|
outputRoot |
– | Filesystem root where mirror artefacts are written. Defaults to the Excititor file-system artifact store root when omitted. |
directoryName |
– | Optional subdirectory created under outputRoot; defaults to mirror. |
targetRepository |
– | Hint propagated to manifests/index files indicating the operator-visible location (for example s3://mirror/excititor). |
signing |
– | Bundle signing configuration. When enabled, the exporter emits a detached JWS (bundle.json.jws) alongside each domain bundle. |
signing supports the following fields:
| Field | Required | Description |
|---|---|---|
enabled |
– | Toggles detached signing for domain bundles. |
algorithm |
– | Signing algorithm identifier (default ES256). |
keyId |
✅ (when enabled) |
Signing key identifier resolved via the configured crypto provider registry. |
provider |
– | Optional provider hint when multiple registries are available. |
keyPath |
– | Optional PEM path used to seed the provider when the key is not already loaded. |
Domain field reference
| Field | Required | Description |
|---|---|---|
id |
✅ | Stable identifier. Appears in URLs (/excititor/mirror/domains/{id}) and download filenames. |
displayName |
– | Human-friendly label surfaced in the /domains listing. Falls back to id. |
requireAuthentication |
– | When true the service enforces that the caller is authenticated (Authority token). |
maxIndexRequestsPerHour |
– | Per-domain quota for index endpoints. 0/negative disables the guard. |
maxDownloadRequestsPerHour |
– | Per-domain quota for artifact downloads. |
exports |
✅ | Collection of export projections. |
Export-level fields:
| Field | Required | Description |
|---|---|---|
key |
✅ | Unique key within the domain. Used in URLs (/exports/{key}) and filenames/bundle entries. |
format |
✅ | One of json, jsonl, openvex, csaf. Maps to VexExportFormat. |
filters |
– | Key/value pairs executed via VexQueryFilter. Keys must match export data source columns (e.g., vulnId, productKey). |
sort |
– | Key/boolean map (false = descending). |
limit, offset, view |
– | Optional query bounds passed through to the export query. |
⚠️ Misconfiguration: invalid formats or missing keys cause exports to be flagged with status in the index response; they are not exposed downstream.
2) HTTP surface
Routes are grouped under /excititor/mirror.
| Method | Path | Description |
|---|---|---|
GET |
/domains |
Returns configured domains with quota metadata. |
GET |
/domains/{domainId} |
Domain detail (auth/quota + export keys). 404 for unknown domains. |
GET |
/domains/{domainId}/index |
Lists exports with exportId, query signature, format, artifact digest, attestation metadata, and size. Applies index quota. |
GET |
/domains/{domainId}/exports/{exportKey} |
Returns manifest metadata (single export). 404 if unknown/missing. |
GET |
/domains/{domainId}/exports/{exportKey}/download |
Streams export content from the artifact store. Applies download quota. |
Responses are serialized via VexCanonicalJsonSerializer ensuring stable ordering. Download responses include a content-disposition header naming the file <domain>-<export>.<ext>.
Error handling
401– authentication required (requireAuthentication=true).404– domain/export not found or manifest not persisted.429– per-domain quota exceeded (Retry-Afterheader set in seconds).503– export misconfiguration (invalid format/query).
3) Rate limiting
MirrorRateLimiter implements a simple rolling 1-hour window using IMemoryCache. Each domain has two quotas:
indexscope →maxIndexRequestsPerHourdownloadscope →maxDownloadRequestsPerHour
0 or negative limits disable enforcement. Quotas are best-effort (per-instance). For HA deployments, configure sticky routing at the ingress or replace the limiter with a distributed implementation.
4) Interaction with export pipeline
Mirror endpoints consume manifests produced by the export engine (MongoVexExportStore). They do not trigger new exports. Operators must configure connectors/exporters to keep targeted exports fresh (see EXCITITOR-EXPORT-01-005/006/007).
Recommended workflow:
- Define export plans at the export layer (JSON/OpenVEX/CSAF).
- Configure mirror domains mapping to those plans.
- Downstream mirror automation:
GET /domains/{id}/index- Compare
exportId/consensusRevision GET /downloadwhen new- Verify digest + attestation
When the export engine runs, it materializes the following artefacts under outputRoot/<directoryName>:
index.json– canonical index listing each configured domain, manifest/bundle descriptors (with SHA-256 digests), and available export keys.<domain>/manifest.json– per-domain summary with export metadata (query signature, consensus/score digests, source providers) and a descriptor pointing at the bundle.<domain>/bundle.json– canonical payload containing serialized consensus, score envelopes, and normalized VEX claims for the matching export definitions.<domain>/bundle.json.jws– optional detached JWS when signing is enabled.
Downstream automation reads manifest.json/bundle.json directly, while /excititor/mirror endpoints stream the same artefacts through authenticated HTTP.
5) Operational guidance
- Track quota utilisation via HTTP 429 metrics (configure structured logging or OTEL counters when rate limiting triggers).
- Mirror domains can be deployed per tenant (e.g.,
tenant-a,tenant-b) with different auth requirements. - Ensure the underlying artifact stores (
FileSystem,S3, offline bundle) retain artefacts long enough for mirrors to sync. - For air-gapped mirrors, combine mirror endpoints with the Offline Kit (see
docs/24_OFFLINE_KIT.md).
6) Future alignment
- Replace manual export definitions with generated mirror bundle manifests once
EXCITITOR-EXPORT-01-007ships. - Extend
/indexpayload with quiet-provenance whenEXCITITOR-EXPORT-01-006adds that metadata. - Integrate domain manifests with DevOps mirror profiles (
DEVOPS-MIRROR-08-001) so helm/compose overlays can enable or disable domains declaratively.