SBOM Sources Architecture

Design architecture for the SBOM Sources subsystem in the Stella Ops release control plane — a unified configuration and management layer for every SBOM ingestion pathway. Audience: backend engineers wiring ingestion, frontend engineers building the Sources Manager, and operators configuring registry/repository scan sources. For the owning service and its durable registry-source state, see the SBOM Service architecture.

Verified against commit 942a18727c (2026-09-26). Re-verify with: grep -nE 'Map(Get|Post|Put|Delete|Group)\(' src/SbomService/StellaOps.SbomService/Services/RegistrySourceEndpointExtensions.cs

Status: Mixed live contract and design specification.

  • Live: registry-source management at /api/v1/registry-sources (section 4.0), with durable registry-source definitions, paired path/tag mask records, run history, immediate mask-change scans, and the nightly forced refresh run by SbomService’s own hosted service. Registry credentials resolve through the Integrations owner API.
  • Design only, not implemented at this commit: the generic /api/v1/sources API (4.1-4.3), the Zastava and Git webhook endpoints (4.4), the source.* domain events (5), credential rotation (6), the sbom_source_* metrics (7.1), the sources:* scopes (10), and the SbomService:Sources:* configuration (11). No route, event, instrument, scope, or option with those names exists in src/.

1. Overview

The SBOM Sources subsystem provides a unified configuration and management layer for all SBOM ingestion pathways: Zastava (registry webhooks), Docker (direct image scans), CLI (external submissions), and Git (repository scans).

1.1 Problem Statement

Current State:

Target State:

2. Source Types Taxonomy

TypeTrigger ModeTargetAuth PatternDiscovery
ZastavaWebhook (push)Registry imagesRegistry creds + webhook secretFrom webhook payload
DockerScheduled/ManualSpecific imagesRegistry credentialsTag patterns
CLIExternal submissionAny SBOMAPI tokenN/A (receives SBOMs)
GitWebhook/ScheduledRepository codePAT/SSH + webhook secretBranch patterns

3. Health Signals

Each source maintains the following health signals:

SignalDescriptionMetric
statusCurrent operational stateactive, paused, error, disabled, pending
lastRunAtTimestamp of most recent runISO-8601 UTC
lastRunStatusStatus of most recent runsucceeded, failed, partial-success
consecutiveFailuresCount of consecutive failed runsInteger, resets on success
currentHourScansScans executed in rate-limit windowInteger, resets hourly

4. API Contract

4.0 Live Registry-Source API

The implemented registry-source surface is:

GET    /api/v1/registry-sources
POST   /api/v1/registry-sources
GET    /api/v1/registry-sources/{sourceId}
PUT    /api/v1/registry-sources/{sourceId}
DELETE /api/v1/registry-sources/{sourceId}
POST   /api/v1/registry-sources/test
POST   /api/v1/registry-sources/{sourceId}/test
POST   /api/v1/registry-sources/{sourceId}/trigger
POST   /api/v1/registry-sources/{sourceId}/pause
POST   /api/v1/registry-sources/{sourceId}/resume
GET    /api/v1/registry-sources/{sourceId}/runs
GET    /api/v1/registry-sources/{sourceId}/discover/repositories
GET    /api/v1/registry-sources/{sourceId}/discover/tags/{repository}
GET    /api/v1/registry-sources/{sourceId}/discover/images
POST   /api/v1/registry-sources/{sourceId}/discover-and-scan
GET    /api/v1/registry-sources/{sourceId}/images
GET    /api/v1/registry-sources/{sourceId}/images/sbom

The console calls this surface through the router frontdoor at /api/v1/registry-sources. The gateway has no static row for it: SbomService publishes these endpoints when it registers as Router microservice sbomservice, and the gateway dispatches by published endpoint. Reads require sbom:read, writes sbom:write.

Registry-source filters are stored as maskRecords[]. Each record has pathRegex, tagRegex, optional name, and enabled. A mask includes an image only when both regexes match the image repository/path and tag. Invalid regexes are rejected during create/update. Enabled masks make the source publish-capable, so create/update/test requires an AuthRef or credential reference with registry pull plus push/write_registry access. If an existing source has enabled masks but no write credential reference, connection tests return success=false, new runs fail with the credential error, and the source detail Schedule Health panel shows the latest scheduled failure from run history. Adding a source with enabled masks starts a mask-added run immediately; changing enabled masks starts a mask-updated run when enabled masks remain. Mask records do not own separate schedules: deleting a mask removes it from automatic mask-change/nightly selection, deleting all enabled masks does not create an immediate mask run, and the nightly refresh skips sources without enabled masks. Nightly refresh runs use nightly-refresh and force Scanner submission so SBOM-derived vulnerability status can refresh after feed updates.

4.1 Source CRUD Endpoints (design, not implemented)

The following generic routes remain the target multi-source API:

GET    /api/v1/sources                    List sources with filtering/pagination
POST   /api/v1/sources                    Create new source
GET    /api/v1/sources/{sourceId}         Get source details
PUT    /api/v1/sources/{sourceId}         Update source configuration
DELETE /api/v1/sources/{sourceId}         Delete source

4.2 Operational Endpoints (design, not implemented)

POST   /api/v1/sources/{sourceId}/test    Test source connection
POST   /api/v1/sources/{sourceId}/trigger Manual scan trigger
POST   /api/v1/sources/{sourceId}/pause   Pause source (with reason)
POST   /api/v1/sources/{sourceId}/resume  Resume paused source

4.3 Run History Endpoints (design, not implemented)

GET    /api/v1/sources/{sourceId}/runs              List run history (paginated)
GET    /api/v1/sources/{sourceId}/runs/{runId}      Get run details

4.4 Webhook Endpoints (design, not implemented)

SbomService has no inbound webhook route. IRegistryWebhookService is registered in DI but no endpoint calls it; see SBOM Service architecture.

POST   /api/v1/webhooks/zastava/{sourceId}   Zastava registry webhook
POST   /api/v1/webhooks/git/{sourceId}       Git repository webhook

5. Domain Events (design, not implemented)

EventPayloadWhen Emitted
source.created{ sourceId, sourceType, tenantId }New source registered
source.updated{ sourceId, changedFields[] }Source configuration updated
source.deleted{ sourceId }Source removed
source.paused{ sourceId, reason, pausedBy }Source paused
source.resumed{ sourceId, resumedBy }Source resumed
source.run.started{ runId, sourceId, trigger }Run initiated
source.run.completed{ runId, sourceId, status, metrics }Run finished

6. Credential Lifecycle (AuthRef Pattern)

All source credentials use the AuthRef pattern for secure storage:

  1. Storage: Credentials are never inline in source configs. At this commit SbomService resolves registry credential references through the Integrations owner API (SbomService:Integrations:*); the vault-backed rotation flow below is design
  2. Reference: Sources hold authRef identifiers pointing to vault entries
  3. Rotation: Rotate-on-demand API; old refs invalidated, new ref issued
  4. Audit: All credential access logged with source context

For live registry-source masks, the AuthRef is required before the source is saved or treated as publish-ready. The service mirrors the accepted authRefUri into the registry discovery credentialRef field so discovery and later OCI SBOM-referrer publish work use the same credential reference. The credential reference may use Stella Ops AuthRef forms such as authref:... or authref://...; raw secret values must not be stored in the source row.

6.1 Supported Credential Types

Source TypeCredential Types
ZastavaRegistry auth (basic/token), webhook secret
DockerRegistry auth (basic/token/ECR/GCR/ACR)
CLIAPI token, OIDC identity
GitPAT, SSH key, webhook secret

7. Telemetry Schema

7.1 Metrics (design, not implemented)

No sbom_source_* instrument exists; the live SbomService instruments are listed in architecture.md.

MetricTypeLabelsDescription
sbom_source_runs_totalCountersource_type, status, triggerTotal runs by type and outcome
sbom_source_run_duration_secondsHistogramsource_typeRun execution time
sbom_source_items_scanned_totalCountersource_typeItems processed per run
sbom_source_connection_test_duration_secondsHistogramsource_typeConnection test latency
sbom_source_active_countGaugesource_type, statusActive sources by type

7.2 Structured Logs

All source operations emit structured logs including:

8. Module Ownership Matrix

ComponentOwner ModuleInterface
Source entity persistenceSbomServicePostgreSQL + Repository
Credential resolution (live)IntegrationsRegistry-credentials owner API
Webhook signature verificationSbomServiceHMAC validation (service registered, no route)
Scheduled trigger dispatch (live)SbomServiceRegistrySourceNightlyRefreshHostedService
Scan job executionScannerJob queue interface
UI configurationWebSources Manager feature

9. UI Information Architecture

9.1 Navigation Placement

SectionRoutePurpose
Sources List/sourcesPrimary view with filtering, status overview, bulk actions
Source Detail/sources/{id}Configuration view, run history, health metrics
Add Source Wizard/sources/newMulti-step creation flow
Edit Source/sources/{id}/editModify existing source configuration

9.2 Wizard Flow Architecture

The Add/Edit Source Wizard follows a 6-step progressive disclosure pattern:

┌─────────────────────────────────────────────────────────────────────────────┐
│  Step 1: Source Type Selection                                              │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │
│  │  Zastava    │ │   Docker    │ │    CLI      │ │    Git      │          │
│  │  (Webhook)  │ │  (Scanner)  │ │ (Receiver)  │ │ (Repository)│          │
│  └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘          │
└─────────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────────┐
│  Step 2: Basic Information                                                  │
│  • Name (required, unique per tenant)                                       │
│  • Description (optional)                                                   │
│  • Tags (multi-select, for filtering)                                       │
│  • Metadata key-value pairs (optional)                                      │
└─────────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────────┐
│  Step 3: Type-Specific Configuration (varies by source type)                │
│                                                                             │
│  Zastava:                      Docker:                                      │
│  • Registry URL                • Registry URL                               │
│  • Registry type dropdown      • Image references (multi-add)               │
│  • Repository filters          • Tag patterns (include/exclude)             │
│  • Tag patterns                • Platform selection                         │
│  • Webhook path (generated)    • Scan options (analyzers, reachability)     │
│                                                                             │
│  CLI:                          Git:                                         │
│  • Allowed tools list          • Provider (GitHub/GitLab/Gitea/etc.)        │
│  • Allowed CI systems          • Repository URL                             │
│  • Validation rules            • Branch patterns (include/exclude)          │
│  • Attribution requirements    • Trigger modes (push/PR/tag/scheduled)      │
│  • Max SBOM size limit         • Scan paths and exclusions                  │
└─────────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────────┐
│  Step 4: Credentials (AuthRef Pattern)                                      │
│  • Credential type selector (per source type)                               │
│  • Create new credential vs. select existing                                │
│  • Inline credential entry (stored via Authority vault)                     │
│  • Webhook secret generation (for Zastava/Git)                              │
│  • Test credential validity button                                          │
└─────────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────────┐
│  Step 5: Schedule (Optional - for Docker/Git scheduled triggers)            │
│  • Enable scheduled scans toggle                                            │
│  • Cron expression builder or preset selector                               │
│  • Timezone picker                                                          │
│  • Rate limit (max scans per hour)                                          │
│  • Next run preview                                                         │
└─────────────────────────────────────────────────────────────────────────────┘
                                    ↓
┌─────────────────────────────────────────────────────────────────────────────┐
│  Step 6: Review & Test Connection                                           │
│  • Configuration summary (read-only)                                        │
│  • Test Connection button with status indicator                             │
│  • Error details expansion if test fails                                    │
│  • Create Source / Save Changes button                                      │
│  • Webhook endpoint display (for Zastava/Git - copy to clipboard)           │
└─────────────────────────────────────────────────────────────────────────────┘

9.3 Wizard Step Validation

StepRequired FieldsValidation
1 - TypesourceTypeMust select one type
2 - Basic InfonameUnique name, 3-100 chars
3 - ConfigVaries by typeType-specific required fields
4 - CredentialsauthRef (if required)Valid credential for source type
5 - ScheduleNone (optional step)Valid cron if enabled
6 - ReviewNoneConnection test recommended

9.4 Source List Page Components

┌─────────────────────────────────────────────────────────────────────────────┐
│  SBOM Sources                                               [+ Add Source]  │
├─────────────────────────────────────────────────────────────────────────────┤
│  Status Cards:                                                              │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐                       │
│  │ Active   │ │ Paused   │ │ Error    │ │ Pending  │                       │
│  │   12     │ │    3     │ │    2     │ │    1     │                       │
│  └──────────┘ └──────────┘ └──────────┘ └──────────┘                       │
├─────────────────────────────────────────────────────────────────────────────┤
│  Filters: [Type ▼] [Status ▼] [Tags ▼] [Search: ________]                  │
├─────────────────────────────────────────────────────────────────────────────┤
│  □ │ Name          │ Type    │ Status   │ Last Run   │ Next Run │ Actions │
│  ──┼───────────────┼─────────┼──────────┼────────────┼──────────┼─────────│
│  □ │ DockerHub Prd │ Zastava │ ● Active │ 5 min ago  │ -        │ ⋮       │
│  □ │ Harbor Dev    │ Zastava │ ● Active │ 12 min ago │ -        │ ⋮       │
│  □ │ Nightly Scans │ Docker  │ ● Active │ 2h ago     │ 02:00 AM │ ⋮       │
│  □ │ CI Pipeline   │ CLI     │ ⏸ Paused │ 1 day ago  │ -        │ ⋮       │
│  □ │ Monorepo      │ Git     │ ⚠ Error  │ Failed     │ -        │ ⋮       │
├─────────────────────────────────────────────────────────────────────────────┤
│  Showing 1-5 of 18                      [< Prev] [1] [2] [3] [4] [Next >]   │
└─────────────────────────────────────────────────────────────────────────────┘

9.5 Row Actions Menu

ActionIconAvailability
View Details👁Always
Edit✏️Always
Trigger Scan▶Active sources
Test Connection🔌Always
Pause⏸Active sources
Resume▶Paused sources
Delete🗑Always (with confirmation)

9.6 Source Detail Page Layout

The live SBOM Sources detail view includes an Images and SBOMs section for registry sources. Operators can query matched image rows by repository/path, tag, or digest through GET /api/v1/registry-sources/{sourceId}/images, then open a selected image’s SBOM review through GET /api/v1/registry-sources/{sourceId}/images/sbom. The review shows the scan-submission/SBOM status and any resolved artifact-association graph returned by SBOM Service.

┌─────────────────────────────────────────────────────────────────────────────┐
│  ← Back to Sources                                                          │
├─────────────────────────────────────────────────────────────────────────────┤
│  Docker Hub Production                      [Trigger] [Test] [Edit] [⋮]    │
│  Type: Zastava │ Status: ● Active │ Created: Dec 15, 2025                   │
├─────────────────────────────────────────────────────────────────────────────┤
│  [Overview] [Configuration] [Run History] [Metrics]                         │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  Configuration:                                                             │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │ Registry URL:     https://registry-1.docker.io                        │ │
│  │ Registry Type:    Docker Hub                                          │ │
│  │ Webhook Path:     /api/v1/webhooks/zastava/src-abc123                 │ │
│  │ Repository Filter: myorg/*, prod-*                                    │ │
│  │ Tag Filter:        v*, latest (excluding: *-dev, *-test)              │ │
│  │ Analyzers:         OS, Node.js, Python, Go                            │ │
│  │ Reachability:      Enabled                                            │ │
│  │ VEX Lookup:        Enabled                                            │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
│  Run History:                                                               │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │ Run ID  │ Trigger  │ Status    │ Started    │ Duration │ Items       │ │
│  │ run-456 │ Webhook  │ ✓ Success │ 5 min ago  │ 45s      │ 3/3         │ │
│  │ run-455 │ Webhook  │ ✓ Success │ 12 min ago │ 38s      │ 1/1         │ │
│  │ run-454 │ Manual   │ ✓ Success │ 1h ago     │ 2m 15s   │ 12/12       │ │
│  │ run-453 │ Webhook  │ ⚠ Partial │ 2h ago     │ 1m 30s   │ 4/5         │ │
│  │ run-452 │ Schedule │ ✗ Failed  │ 3h ago     │ 12s      │ 0/0         │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│  [Load More]                                                                │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

10. Security Considerations

ConcernMitigation
Credential exposureAuthRef pattern - never inline, always reference vault
Webhook forgeryHMAC signature verification for all webhooks
Unauthorized accessScope-based authorization; live routes use sbom:read / sbom:write (the sources:* scopes do not exist)
Secret loggingAudit logging excludes credential values
Webhook secret rotationRotate-on-demand API endpoint

11. Configuration

Live registry-source settings bind from SbomService:RegistrySources (DefaultPageSize, MaxPageSize, MaxRunHistoryLimit, NightlyRefreshEnabled, NightlyRefreshHourUtc, NightlyRefreshMinuteUtc, NightlyRefreshPollSeconds) and SbomService:RegistrySourceRunRetention; see architecture.md.

Design-only settings for the generic sources API (not bound by any code at this commit; prefix SBOM_SbomService__Sources__):

VariableDefaultDescription
MaxSourcesPerTenant100Maximum sources per tenant
DefaultMaxScansPerHour60Default rate limit per source
RunHistoryRetentionDays90Run history retention period (the live sweep keeps the newest 500 runs per source instead; see retention-policy.md)
WebhookSignatureAlgorithmHMAC-SHA256Webhook signature algorithm
ConnectionTestTimeoutSeconds30Connection test timeout