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.

Status: Mixed live contract and design specification. The generic /api/v1/sources surface remains the long-term multi-source target. Registry-source management is live today at /api/v1/registry-sources through SBOM Service minimal APIs, with durable registry-source definitions, paired path/tag mask records, run history, immediate mask-change scans, and nightly forced refresh runs.

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 router maps that path to sbomservice.stella-ops.local before the generic /api/v1/{service} catch-all.

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

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

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

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

4.4 Webhook Endpoints

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

5. Domain Events

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 stored in Authority vault, never inline in source configs
  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

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 storageAuthorityAuthRef vault API
Webhook signature verificationSbomServiceHMAC validation
Scheduled trigger dispatchSchedulerCron job registration
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 ScanActive sources
Test Connection🔌Always
PauseActive sources
ResumePaused 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 (sources:read, sources:write, sources:admin)
Secret loggingAudit logging excludes credential values
Webhook secret rotationRotate-on-demand API endpoint

11. Configuration

Environment variables (prefix SBOM_SbomService__Sources__):

VariableDefaultDescription
MaxSourcesPerTenant100Maximum sources per tenant
DefaultMaxScansPerHour60Default rate limit per source
RunHistoryRetentionDays90Run history retention period
WebhookSignatureAlgorithmHMAC-SHA256Webhook signature algorithm
ConnectionTestTimeoutSeconds30Connection test timeout