37 KiB
Executable File
# API & CLI Reference
Purpose – give operators and integrators a single, authoritative spec for REST/GRPC calls and first‑party CLI tools (stella-cli, zastava, stella).
Everything here is source‑of‑truth for generated Swagger/OpenAPI and the --help screens in the CLIs.
## 0 Quick Glance
| Area | Call / Flag | Notes |
|---|---|---|
| Scan entry | POST /scan |
Accepts SBOM or image; sub‑5 s target |
| Delta check | POST /layers/missing |
<20 ms reply; powers delta SBOM feature |
| Rate‑limit / quota | — | Headers X‑Stella‑Quota‑Remaining, X‑Stella‑Reset on every response |
| Policy I/O | GET /policy/export, POST /policy/import |
YAML now; Rego coming |
| Policy lint | POST /policy/validate |
Returns 200 OK if ruleset passes |
| Auth | POST /connect/token (OpenIddict) |
Client‑credentials preferred |
| Health | GET /healthz |
Simple liveness probe |
| Attestation * | POST /attest (TODO Q1‑2026) |
SLSA provenance + Rekor log |
| CLI flags | --sbom-type --delta --policy-file |
Added to stella |
* Marked TODO → delivered after sixth month (kept on Feature Matrix “To Do” list).
## 1 Authentication
Stella Ops uses OAuth 2.0 / OIDC (token endpoint mounted via OpenIddict).
POST /connect/token
Content‑Type: application/x-www-form-urlencoded
grant_type=client_credentials&
client_id=ci‑bot&
client_secret=REDACTED&
scope=stella.api
Successful response:
{
"access_token": "eyJraWQi...",
"token_type": "Bearer",
"expires_in": 3600
}
Tip
– pass the token via
Authorization: Bearer <token>on every call.
## 2 REST API
### 2.0 Obtain / Refresh Offline‑Token
POST /token/offline
Authorization: Bearer <admin‑token>
| Body field | Required | Example | Notes |
|---|---|---|---|
expiresDays |
no | 30 |
Max 90 days |
{
"jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6...",
"expires": "2025‑08‑17T00:00:00Z"
}
Token is signed with the backend’s private key and already contains
"maxScansPerDay": {{ quota_token }}.
### 2.1 Scan – Upload SBOM or Image
POST /scan
| Param / Header | In | Required | Description |
|---|---|---|---|
X‑Stella‑Sbom‑Type |
header | no | trivy-json-v2, spdx-json, cyclonedx-json; omitted ➞ auto‑detect |
?threshold |
query | no | low, medium, high, critical; default critical |
| body | body | yes | Either SBOM JSON or Docker image tarball/upload URL |
Every successful /scan response now includes:
| Header | Example |
|---|---|
X‑Stella‑Quota‑Remaining |
129 |
X‑Stella‑Reset |
2025‑07‑18T23:59:59Z |
X‑Stella‑Token‑Expires |
2025‑08‑17T00:00:00Z |
Response 200 (scan completed):
{
"digest": "sha256:…",
"summary": {
"Critical": 0,
"High": 3,
"Medium": 12,
"Low": 41
},
"policyStatus": "pass",
"quota": {
"remaining": 131,
"reset": "2025-07-18T00:00:00Z"
}
}
Response 202 – queued; polling URL in Location header.
### 2.2 Delta SBOM – Layer Cache Check
POST /layers/missing
Content‑Type: application/json
Authorization: Bearer <token>
{
"layers": [
"sha256:d38b...",
"sha256:af45..."
]
}
Response 200 — <20 ms target:
{
"missing": [
"sha256:af45..."
]
}
Client then generates SBOM only for the missing layers and re‑posts /scan.
### 2.3 Policy Endpoints (preview feature flag: scanner.features.enablePolicyPreview)
All policy APIs require scanner.reports scope (or anonymous access while auth is disabled).
Fetch schema
GET /api/v1/policy/schema
Authorization: Bearer <token>
Accept: application/schema+json
Returns the embedded policy-schema@1 JSON schema used by the binder.
Run diagnostics
POST /api/v1/policy/diagnostics
Content-Type: application/json
Authorization: Bearer <token>
{
"policy": {
"format": "yaml",
"actor": "cli",
"description": "dev override",
"content": "version: \"1.0\"\nrules:\n - name: Quiet Dev\n environments: [dev]\n action:\n type: ignore\n justification: dev waiver\n"
}
}
Response 200:
{
"success": false,
"version": "1.0",
"ruleCount": 1,
"errorCount": 0,
"warningCount": 1,
"generatedAt": "2025-10-19T03:25:14.112Z",
"issues": [
{ "code": "policy.rule.quiet.missing_vex", "message": "Quiet flag ignored: rule must specify requireVex justifications.", "severity": "Warning", "path": "$.rules[0]" }
],
"recommendations": [
"Review policy warnings and ensure intentional overrides are documented."
]
}
success is false when blocking issues remain; recommendations aggregate YAML ignore rules, VEX include/exclude hints, and vendor precedence guidance.
Preview impact
POST /api/v1/policy/preview
Authorization: Bearer <token>
Content-Type: application/json
{
"imageDigest": "sha256:abc123",
"findings": [
{ "id": "finding-1", "severity": "Critical", "source": "NVD" }
],
"policy": {
"format": "yaml",
"content": "version: \"1.0\"\nrules:\n - name: Block Critical\n severity: [Critical]\n action: block\n"
}
}
Response 200:
{
"success": true,
"policyDigest": "9c5e...",
"revisionId": "preview",
"changed": 1,
"diffs": [
{
"findingId": "finding-1",
"baseline": {"findingId": "finding-1", "status": "Pass"},
"projected": {
"findingId": "finding-1",
"status": "Blocked",
"ruleName": "Block Critical",
"ruleAction": "Block",
"score": 5.0,
"configVersion": "1.0",
"inputs": {"severityWeight": 5.0}
},
"changed": true
}
],
"issues": []
}
- Provide
policyto preview staged changes; omit it to compare against the active snapshot. - Baseline verdicts are optional; when omitted, the API synthesises pass baselines before computing diffs.
- Quieted verdicts include
quietedByandquietflags; score inputs now surface reachability/vendor trust weights (reachability.*,trustWeight.*).
OpenAPI: the full API document (including these endpoints) is exposed at /openapi/v1.json and can be fetched for tooling or contract regeneration.
### 2.4 Scanner – Queue a Scan Job (SP9 milestone)
POST /api/v1/scans
Authorization: Bearer <token with scanner.scans.enqueue>
Content-Type: application/json
{
"image": {
"reference": "registry.example.com/acme/app:1.2.3"
},
"force": false,
"clientRequestId": "ci-build-1845",
"metadata": {
"pipeline": "github",
"trigger": "pull-request"
}
}
| Field | Required | Notes |
|---|---|---|
image.reference |
no* | Full repo/tag (registry/repo:tag). Provide either reference or digest (sha256:…). |
image.digest |
no* | OCI digest (e.g. sha256:…). |
force |
no | true forces a re-run even if an identical scan (scanId) already exists. Default false. |
clientRequestId |
no | Free-form string surfaced in audit logs. |
metadata |
no | Optional string map stored with the job and surfaced in observability feeds. |
* At least one of image.reference or image.digest must be supplied.
Response 202 – job accepted (idempotent):
HTTP/1.1 202 Accepted
Location: /api/v1/scans/2f6c17f9b3f548e2a28b9c412f4d63f8
{
"scanId": "2f6c17f9b3f548e2a28b9c412f4d63f8",
"status": "Pending",
"location": "/api/v1/scans/2f6c17f9b3f548e2a28b9c412f4d63f8",
"created": true
}
scanIdis deterministic – resubmitting an identical payload returns the same identifier with"created": false.- API is cancellation-aware; aborting the HTTP request cancels the submission attempt.
- Required scope:
scanner.scans.enqueue.
Response 400 – validation problem (Content-Type: application/problem+json) when both image.reference and image.digest are blank.
### 2.5 Scanner – Fetch Scan Status
GET /api/v1/scans/{scanId}
Authorization: Bearer <token with scanner.scans.read>
Accept: application/json
Response 200:
{
"scanId": "2f6c17f9b3f548e2a28b9c412f4d63f8",
"status": "Pending",
"image": {
"reference": "registry.example.com/acme/app:1.2.3",
"digest": null
},
"createdAt": "2025-10-18T20:15:12.482Z",
"updatedAt": "2025-10-18T20:15:12.482Z",
"failureReason": null
}
Statuses: Pending, Running, Succeeded, Failed, Cancelled.
### 2.6 Scanner – Stream Progress (SSE / JSONL)
GET /api/v1/scans/{scanId}/events?format=sse|jsonl
Authorization: Bearer <token with scanner.scans.read>
Accept: text/event-stream
When format is omitted the endpoint emits Server-Sent Events (SSE). Specify format=jsonl to receive newline-delimited JSON (application/x-ndjson). Response headers include Cache-Control: no-store and X-Accel-Buffering: no so intermediaries avoid buffering the stream.
SSE frame (default):
id: 1
event: pending
data: {"scanId":"2f6c17f9b3f548e2a28b9c412f4d63f8","sequence":1,"state":"Pending","message":"queued","timestamp":"2025-10-19T03:12:45.118Z","correlationId":"2f6c17f9b3f548e2a28b9c412f4d63f8:0001","data":{"force":false,"meta.pipeline":"github"}}
JSONL frame (format=jsonl):
{"scanId":"2f6c17f9b3f548e2a28b9c412f4d63f8","sequence":1,"state":"Pending","message":"queued","timestamp":"2025-10-19T03:12:45.118Z","correlationId":"2f6c17f9b3f548e2a28b9c412f4d63f8:0001","data":{"force":false,"meta.pipeline":"github"}}
sequenceis monotonic starting at1.correlationIdis deterministic ({scanId}:{sequence:0000}) unless a custom identifier is supplied by the publisher.timestampis ISO‑8601 UTC with millisecond precision, ensuring deterministic ordering for consumers.- The stream completes when the client disconnects or the coordinator stops publishing events.
### 2.7 Scanner – Assemble Report (Signed Envelope)
POST /api/v1/reports
Authorization: Bearer <token with scanner.reports>
Content-Type: application/json
Request body mirrors policy preview inputs (image digest plus findings). The service evaluates the active policy snapshot, assembles a verdict, and signs the canonical report payload.
Response 200:
{
"report": {
"reportId": "report-9f8cde21aab54321",
"imageDigest": "sha256:7dbe0c9a5d4f1c8184007e9d94dbe55928f8a2db5ab9c1c2d4a2f7bbcdfe1234",
"generatedAt": "2025-10-23T15:32:22Z",
"verdict": "blocked",
"policy": {
"revisionId": "rev-42",
"digest": "8a0f72f8dc5c51c46991db3bba34e9b3c0c8e944a7a6d0a9c29a9aa6b8439876"
},
"summary": { "total": 2, "blocked": 1, "warned": 1, "ignored": 0, "quieted": 0 },
"verdicts": [
{
"findingId": "library:pkg/openssl@1.1.1w",
"status": "Blocked",
"ruleName": "Block vendor unknowns",
"ruleAction": "block",
"notes": "Unknown vendor telemetry — medium confidence band.",
"score": 19.5,
"configVersion": "1.0",
"inputs": {
"severityWeight": 50,
"trustWeight": 0.65,
"reachabilityWeight": 0.6,
"baseScore": 19.5,
"trustWeight.vendor": 0.65,
"reachability.unknown": 0.6,
"unknownConfidence": 0.55,
"unknownAgeDays": 5
},
"quietedBy": null,
"quiet": false,
"unknownConfidence": 0.55,
"confidenceBand": "medium",
"unknownAgeDays": 5,
"sourceTrust": "vendor",
"reachability": "unknown"
},
{
"findingId": "library:pkg/zlib@1.3.1",
"status": "Warned",
"ruleName": "Runtime mitigation required",
"ruleAction": "warn",
"notes": "Runtime reachable unknown — mitigation window required.",
"score": 18.75,
"configVersion": "1.0",
"inputs": {
"severityWeight": 75,
"trustWeight": 1,
"reachabilityWeight": 0.45,
"baseScore": 33.75,
"reachability.runtime": 0.45,
"warnPenalty": 15,
"unknownConfidence": 0.35,
"unknownAgeDays": 13
},
"quietedBy": null,
"quiet": false,
"unknownConfidence": 0.35,
"confidenceBand": "medium",
"unknownAgeDays": 13,
"sourceTrust": "NVD",
"reachability": "runtime"
}
],
"issues": []
},
"dsse": {
"payloadType": "application/vnd.stellaops.report+json",
"payload": "eyJyZXBvcnQiOnsicmVwb3J0SWQiOiJyZXBvcnQtOWY4Y2RlMjFhYWI1NDMyMSJ9fQ==",
"signatures": [
{
"keyId": "scanner-report-signing",
"algorithm": "hs256",
"signature": "MEQCIGHscnJ2bm9wYXlsb2FkZXIAIjANBgkqhkiG9w0BAQsFAAOCAQEASmFja3Nvbk1ldGE="
}
]
}
}
- The
reportobject omits null fields and is deterministic (ISO timestamps, sorted keys) while surfacingunknownConfidence,confidenceBand, andunknownAgeDaysfor auditability. dssefollows the DSSE (Dead Simple Signing Envelope) shape;payloadis the canonical UTF-8 JSON andsignatures[0].signatureis the base64 HMAC/Ed25519 value depending on configuration.- Full offline samples live at
samples/policy/policy-report-unknown.json(request + response) andsamples/api/reports/report-sample.dsse.json(envelope fixture) for tooling tests or signature verification.
Response 404 – application/problem+json payload with type https://stellaops.org/problems/not-found when the scan identifier is unknown.
Tip
– poll
Locationfrom the submission call untilstatustransitions away fromPending/Running.
# Example import payload (YAML)
version: "1.0"
rules:
- name: Ignore Low dev
severity: [Low, None]
environments: [dev, staging]
action: ignore
Validation errors come back as:
{
"errors": [
{
"path": "$.rules[0].severity",
"msg": "Invalid level 'None'"
}
]
}
# Preview response excerpt
{
"success": true,
"policyDigest": "9c5e...",
"revisionId": "rev-12",
"changed": 1,
"diffs": [
{
"baseline": {"findingId": "finding-1", "status": "pass"},
"projected": {"findingId": "finding-1", "status": "blocked", "ruleName": "Block Critical"},
"changed": true
}
]
}
### 2.4 Attestation (Planned – Q1‑2026)
POST /attest
| Param | Purpose |
|---|---|
| body (JSON) | SLSA v1.0 provenance doc |
| Signed + stored in local Rekor mirror |
Returns 202 Accepted and Location: /attest/{id} for async verify.
### 2.8 Runtime – Ingest Observer Events (SCANNER-RUNTIME-12-301)
POST /api/v1/runtime/events
Authorization: Bearer <token with scanner.runtime.ingest>
Content-Type: application/json
| Requirement | Details |
|---|---|
| Auth scope | scanner.runtime.ingest |
| Batch size | ≤ 256 envelopes (scanner.runtime.maxBatchSize, configurable) |
| Payload cap | ≤ 1 MiB serialized JSON (scanner.runtime.maxPayloadBytes) |
| Rate limits | Per-tenant and per-node token buckets (default 200 events/s tenant, 50 events/s node, burst 200) – excess returns 429 with Retry-After. |
| TTL | Runtime events retained 45 days by default (scanner.runtime.eventTtlDays). |
Request body
{
"batchId": "node-a-2025-10-20T15:03:12Z",
"events": [
{
"schemaVersion": "zastava.runtime.event@v1",
"event": {
"eventId": "evt-2f9c02b8",
"when": "2025-10-20T15:03:08Z",
"kind": "ContainerStart",
"tenant": "tenant-alpha",
"node": "cluster-a/node-01",
"runtime": { "engine": "containerd", "version": "1.7.19" },
"workload": {
"platform": "kubernetes",
"namespace": "payments",
"pod": "api-7c9fbbd8b7-ktd84",
"container": "api",
"containerId": "containerd://bead5...",
"imageRef": "ghcr.io/acme/api@sha256:deadbeef"
},
"process": { "pid": 12345, "entrypoint": ["/start.sh", "--serve"] },
"loadedLibs": [
{ "path": "/lib/x86_64-linux-gnu/libssl.so.3", "inode": 123456, "sha256": "abc123..." }
],
"posture": { "imageSigned": true, "sbomReferrer": "present" },
"delta": { "baselineImageDigest": "sha256:deadbeef" },
"evidence": [ { "signal": "proc.maps", "value": "libssl.so.3@0x7f..." } ],
"annotations": { "observerVersion": "1.0.0" }
}
}
]
}
Responses
| Code | Body | Notes |
|---|---|---|
202 Accepted |
{ "accepted": 128, "duplicates": 2 } |
Batch persisted; duplicates are ignored via unique eventId. |
400 Bad Request |
Problem+JSON | Validation failures – empty batch, duplicate IDs, unsupported schema version, payload too large. |
429 Too Many Requests |
Problem+JSON | Per-tenant/node rate limit exceeded; Retry-After header emitted in seconds. |
Persisted documents capture the canonical envelope (payload field), tenant/node metadata, and set an automatic TTL on expiresAt. Observers should retry rejected batches with exponential backoff honouring the provided Retry-After hint.
## 3 StellaOps CLI (stellaops-cli)
The new CLI is built on System.CommandLine 2.0.0‑beta5 and mirrors the Concelier backend REST API.
Configuration follows the same precedence chain everywhere:
- Environment variables (e.g.
API_KEY,STELLAOPS_BACKEND_URL,StellaOps:ApiKey) appsettings.json→appsettings.local.jsonappsettings.yaml→appsettings.local.yaml- Defaults (
ApiKey = "",BackendUrl = "", cache folders under the current working directory)
Authority auth client resilience settings
| Setting | Environment variable | Default | Purpose |
|---|---|---|---|
StellaOps:Authority:Resilience:EnableRetries |
STELLAOPS_AUTHORITY_ENABLE_RETRIES |
true |
Toggle Polly wait-and-retry handlers for discovery/token calls |
StellaOps:Authority:Resilience:RetryDelays |
STELLAOPS_AUTHORITY_RETRY_DELAYS |
1s,2s,5s |
Comma/space-separated backoff sequence (HH:MM:SS) |
StellaOps:Authority:Resilience:AllowOfflineCacheFallback |
STELLAOPS_AUTHORITY_ALLOW_OFFLINE_CACHE_FALLBACK |
true |
Reuse cached discovery/JWKS metadata when Authority is temporarily unreachable |
StellaOps:Authority:Resilience:OfflineCacheTolerance |
STELLAOPS_AUTHORITY_OFFLINE_CACHE_TOLERANCE |
00:10:00 |
Additional tolerance window added to the discovery/JWKS cache lifetime |
See docs/dev/32_AUTH_CLIENT_GUIDE.md for recommended profiles (online vs. air-gapped) and testing guidance.
| Command | Purpose | Key Flags / Arguments | Notes |
|---|---|---|---|
stellaops-cli scanner download |
Fetch and install scanner container | --channel <stable|beta|nightly> (default stable)--output <path>--overwrite--no-install |
Saves artefact under ScannerCacheDirectory, verifies digest/signature, and executes docker load unless --no-install is supplied. |
stellaops-cli scan run |
Execute scanner container against a directory (auto-upload) | --target <directory> (required)--runner <docker|dotnet|self> (default from config)--entry <image-or-entrypoint>[scanner-args...] |
Runs the scanner, writes results into ResultsDirectory, emits a structured scan-run-*.json metadata file, and automatically uploads the artefact when the exit code is 0. |
stellaops-cli scan upload |
Re-upload existing scan artefact | --file <path> |
Useful for retries when automatic upload fails or when operating offline. |
stellaops-cli db fetch |
Trigger connector jobs | --source <id> (e.g. redhat, osv)--stage <fetch|parse|map> (default fetch)`--mode <resume |
init |
stellaops-cli db merge |
Run canonical merge reconcile | — | Calls POST /jobs/merge:reconcile; exit code 0 on acceptance, 1 on failures/conflicts |
stellaops-cli db export |
Kick JSON / Trivy exports | --format <json|trivy-db> (default json)--delta--publish-full/--publish-delta--bundle-full/--bundle-delta |
Sets { delta = true } parameter when requested and can override ORAS/bundle toggles per run |
stellaops-cli auth <login|logout|status|whoami> |
Manage cached tokens for StellaOps Authority | auth login --force (ignore cache)auth statusauth whoami |
Uses StellaOps.Auth.Client; honours StellaOps:Authority:* configuration, stores tokens under ~/.stellaops/tokens by default, and whoami prints subject/scope/expiry |
stellaops-cli auth revoke export |
Export the Authority revocation bundle | --output <directory> (defaults to CWD) |
Writes revocation-bundle.json, .json.jws, and .json.sha256; verifies the digest locally and includes key metadata in the log summary. |
stellaops-cli auth revoke verify |
Validate a revocation bundle offline | --bundle <path> --signature <path> --key <path>--verbose |
Verifies detached JWS signatures, reports the computed SHA-256, and can fall back to cached JWKS when --key is omitted. |
stellaops-cli offline kit pull |
Download the latest offline kit bundle and manifest | --bundle-id <id> (optional)--destination <dir>--overwrite--no-resume |
Streams the bundle + manifest from the configured mirror/backend, resumes interrupted downloads, verifies SHA-256, and writes signatures plus a .metadata.json manifest alongside the artefacts. |
stellaops-cli offline kit import |
Upload an offline kit bundle to the backend | <bundle.tgz> (argument)--manifest <path>--bundle-signature <path>--manifest-signature <path> |
Validates digests when metadata is present, then posts multipart payloads to POST /api/offline-kit/import; logs the submitted import ID/status for air-gapped rollout tracking. |
stellaops-cli offline kit status |
Display imported offline kit details | --json |
Shows bundle id/kind, captured/imported timestamps, digests, and component versions; --json emits machine-readable output for scripting. |
stellaops-cli config show |
Display resolved configuration | — | Masks secret values; helpful for air‑gapped installs |
stellaops-cli runtime policy test |
Ask Scanner.WebService for runtime verdicts (Webhook parity) | --image/-i <digest> (repeatable, comma/space lists supported)--file/-f <path>--namespace/--ns <name>--label/-l key=value (repeatable)--json |
Posts to POST /api/v1/scanner/policy/runtime, deduplicates image digests, and prints TTL/policy revision plus per-image columns for signed state, SBOM referrers, quieted-by metadata, confidence, and Rekor attestation (uuid + verified flag). Accepts newline/whitespace-delimited stdin when piped; --json emits the raw response without additional logging. |
POST /api/v1/scanner/policy/runtime responds with one entry per digest. Each result now includes:
policyVerdict(pass|warn|fail|error),signed, andhasSbomReferrersparity with the webhook contract.confidence(0-1 double) derived from canonicalPolicyPreviewServiceevaluation andquieted/quietedByflags for muted findings.rekorblock carryinguuid,url, and the attestor-backedverifiedboolean when Rekor inclusion proofs have been confirmed.metadata(stringified JSON) capturing runtime heuristics, policy issues, evaluated findings, and timestamps for downstream audit.
When running on an interactive terminal without explicit override flags, the CLI uses Spectre.Console prompts to let you choose per-run ORAS/offline bundle behaviour.
Runtime verdict output reflects the SCANNER-RUNTIME-12-302 contract sign-off (quieted provenance, confidence band, attestation verification). CLI-RUNTIME-13-008 now mirrors those fields in both table and --json formats.
Startup diagnostics
stellaops-clinow loads Authority plug-in manifests during startup (respectingAuthority:Plugins:*) and surfaces analyzer warnings when a plug-in weakens the baseline password policy (minimum length 12 and all character classes required).- Follow the log entry’s config path and raise
passwordPolicy.minimumLengthto at least 12 while keepingrequireUppercase,requireLowercase,requireDigit, andrequireSymbolset totrueto clear the warning; weakened overrides are treated as actionable security deviations.
Logging & exit codes
- Structured logging via
Microsoft.Extensions.Loggingwith single-line console output (timestamps in UTC). --verbose / -vraises log level toDebug.- Command exit codes bubble up: backend conflict →
1, cancelled viaCTRL+C→130, scanner exit codes propagate as-is.
Artifact validation
- Downloads are verified against the
X-StellaOps-Digestheader (SHA-256). WhenStellaOps:ScannerSignaturePublicKeyPathpoints to a PEM-encoded RSA key, the optionalX-StellaOps-Signatureheader is validated as well. - Metadata for each bundle is written alongside the artefact (
*.metadata.json) with digest, signature, source URL, and timestamps. - Retry behaviour is controlled via
StellaOps:ScannerDownloadAttempts(default 3 with exponential backoff). - Successful
scan runexecutions create timestamped JSON artefacts insideResultsDirectoryplus ascan-run-*.jsonmetadata envelope documenting the runner, arguments, timing, and stdout/stderr. The artefact is posted back to Concelier automatically.
Trivy DB export metadata (metadata.json)
stellaops-cli db export --format trivy-db (and the backing POST /jobs/export:trivy-db) always emits a metadata.json document in the OCI layout root. Operators consuming the bundle or delta updates should inspect the following fields:
| Field | Type | Purpose |
|---|---|---|
mode |
full | delta |
Indicates whether the current run rebuilt the entire database (full) or only the changed files (delta). |
baseExportId |
string? | Export ID of the last full baseline that the delta builds upon. Only present for mode = delta. |
baseManifestDigest |
string? | SHA-256 digest of the manifest belonging to the baseline OCI layout. |
resetBaseline |
boolean | true when the exporter rotated the baseline (e.g., repo change, delta chain reset). Treat as a full refresh. |
treeDigest |
string | Canonical SHA-256 digest of the JSON tree used to build the database. |
treeBytes |
number | Total bytes across exported JSON files. |
advisoryCount |
number | Count of advisories included in the export. |
exporterVersion |
string | Version stamp of StellaOps.Concelier.Exporter.TrivyDb. |
builder |
object? | Raw metadata emitted by trivy-db build (version, update cadence, etc.). |
delta.changedFiles[] |
array | Present when mode = delta. Each entry lists { "path": "<relative json>", "length": <bytes>, "digest": "sha256:..." }. |
delta.removedPaths[] |
array | Paths that existed in the previous manifest but were removed in the new run. |
When the planner opts for a delta run, the exporter copies unmodified blobs from the baseline layout identified by baseManifestDigest. Consumers that cache OCI blobs only need to fetch the changedFiles and the new manifest/metadata unless resetBaseline is true.
When pushing to ORAS, set concelier:exporters:trivyDb:oras:publishFull / publishDelta to control whether full or delta runs are copied to the registry. Offline bundles follow the analogous includeFull / includeDelta switches under offlineBundle.
Example configuration (appsettings.yaml):
concelier:
exporters:
trivyDb:
oras:
enabled: true
publishFull: true
publishDelta: false
offlineBundle:
enabled: true
includeFull: true
includeDelta: false
Authentication
- API key is sent as
Authorization: Bearer <token>automatically when configured. - Anonymous operation is permitted only when Concelier runs with
authority.allowAnonymousFallback: true. This flag is temporary—plan to disable it before 2025-12-31 UTC so bearer tokens become mandatory.
Authority-backed auth workflow:
- Configure Authority settings via config or env vars (see sample below). Minimum fields:
Url,ClientId, and eitherClientSecret(client credentials) orUsername/Password(password grant). - Run
stellaops-cli auth loginto acquire and cache a token. Use--forceif you need to ignore an existing cache entry. - Execute CLI commands as normal—the backend client injects the cached bearer token automatically and retries on transient 401/403 responses with operator guidance.
- Inspect the cache with
stellaops-cli auth status(shows expiry, scope, mode) or clear it viastellaops-cli auth logout. - Run
stellaops-cli auth whoamito dump token subject, audience, issuer, scopes, and remaining lifetime (verbose mode prints additional claims). - Expect Concelier to emit audit logs for each
/jobs*request showingsubject,clientId,scopes,status, and whether network bypass rules were applied.
Tokens live in ~/.stellaops/tokens unless StellaOps:Authority:TokenCacheDirectory overrides it. Cached tokens are reused offline until they expire; the CLI surfaces clear errors if refresh fails.
For offline workflows, configure StellaOps:Offline:KitsDirectory (or STELLAOPS_OFFLINE_KITS_DIR) to control where bundles, manifests, and metadata are stored, and StellaOps:Offline:KitMirror (or STELLAOPS_OFFLINE_MIRROR_URL) to override the download base URL when pulling from a mirror.
Configuration file template
{
"StellaOps": {
"ApiKey": "your-api-token",
"BackendUrl": "https://concelier.example.org",
"ScannerCacheDirectory": "scanners",
"ResultsDirectory": "results",
"DefaultRunner": "docker",
"ScannerSignaturePublicKeyPath": "",
"ScannerDownloadAttempts": 3,
"Offline": {
"KitsDirectory": "offline-kits",
"KitMirror": "https://get.stella-ops.org/ouk/"
},
"Authority": {
"Url": "https://authority.example.org",
"ClientId": "concelier-cli",
"ClientSecret": "REDACTED",
"Username": "",
"Password": "",
"Scope": "concelier.jobs.trigger",
"TokenCacheDirectory": ""
}
}
}
Drop appsettings.local.json or .yaml beside the binary to override per environment.
### 2.5 Misc Endpoints
| Path | Method | Description |
|---|---|---|
/healthz |
GET | Liveness; returns "ok" |
/metrics |
GET | Prometheus exposition (OTel) |
/version |
GET | Git SHA + build date |
### 2.6 Authority Admin APIs
Administrative endpoints live under /internal/* on the Authority host and require the bootstrap API key (x-stellaops-bootstrap-key). Responses are deterministic and audited via AuthEventRecord.
| Path | Method | Description |
|---|---|---|
/internal/revocations/export |
GET | Returns the revocation bundle (JSON + detached JWS + digest). Mirrors the output of stellaops-cli auth revoke export. |
/internal/signing/rotate |
POST | Promotes a new signing key and marks the previous key as retired without restarting the service. |
Rotate request body
{
"keyId": "authority-signing-2025",
"location": "../certificates/authority-signing-2025.pem",
"source": "file",
"provider": "default"
}
The API responds with the active kid, previous key (if any), and the set of retired key identifiers. Always export a fresh revocation bundle after rotation so downstream mirrors receive signatures from the new key.
## 3 First‑Party CLI Tools
### 3.1 stella
Package SBOM + Scan + Exit code – designed for CI.
Usage: stella [OPTIONS] IMAGE_OR_SBOM
| Flag / Option | Default | Description |
|---|---|---|
--server |
http://localhost:8080 |
API root |
--token |
env STELLA_TOKEN |
Bearer token |
--sbom-type |
auto | Force trivy-json-v2/spdx-json/cyclonedx-json |
--delta |
false |
Enable delta layer optimisation |
--policy-file |
none | Override server rules with local YAML/Rego |
--threshold |
critical |
Fail build if ≥ level found |
--output-json |
none | Write raw scan result to file |
--wait-quota |
true |
If 429 received, automatically wait Retry‑After and retry once. |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Scan OK, policy passed |
| 1 | Vulnerabilities ≥ threshold OR policy block |
| 2 | Internal error (network etc.) |
### 3.2 stella‑zastava
Daemon / K8s DaemonSet – watch container runtime, push SBOMs.
Core flags (excerpt):
| Flag | Purpose |
|---|---|
--mode |
listen (default) / enforce |
--filter-image |
Regex; ignore infra/busybox images |
--threads |
Worker pool size |
### 3.3 stellopsctl
Admin utility – policy snapshots, feed status, user CRUD.
Examples:
stellopsctl policy export > policies/backup-2025-07-14.yaml
stellopsctl feed refresh # force OSV merge
stellopsctl user add dev-team --role developer
## 4 Error Model
Uniform problem‑details object (RFC 7807):
{
"type": "https://stella-ops.org/probs/validation",
"title": "Invalid request",
"status": 400,
"detail": "Layer digest malformed",
"traceId": "00-7c39..."
}
## 5 Rate Limits
Default 40 requests / second / token.
429 responses include Retry-After seconds header.
## 6 FAQ & Tips
- Skip SBOM generation in CI – supply a pre‑built SBOM and add
?sbom-only=trueto/scanfor <1 s path. - Air‑gapped? – point
--servertohttp://oukgw:8080inside the Offline Update Kit. - YAML vs Rego – YAML simpler; Rego unlocks time‑based logic (see samples).
- Cosign verify plug‑ins – enable
SCANNER_VERIFY_SIG=trueenv to refuse unsigned plug‑ins.
## 7 Planned Changes (Beyond 6 Months)
These stay in Feature Matrix → To Do until design is frozen.
| Epic / Feature | API Impact Sketch |
|---|---|
| SLSA L1‑L3 attestation | /attest (see §2.4) |
| Rekor transparency log | /rekor/log/{id} (GET) |
| Plug‑in Marketplace metadata | /plugins/market (catalog) |
| Horizontal scaling controls | POST /cluster/node (add/remove) |
| Windows agent support | Update LSAPI to PDE, no API change |
## 8 References
- OpenAPI YAML →
/openapi/v1.yaml(served by backend) - OAuth2 spec: https://datatracker.ietf.org/doc/html/rfc6749
- SLSA spec: https://slsa.dev/spec/v1.0
## 9 Changelog (truncated)
- 2025‑07‑14 – added delta SBOM, policy import/export, CLI
--sbom-type. - 2025‑07‑12 – initial public reference.