Files
git.stella-ops.org/docs/09_API_CLI_REFERENCE.md
master 5ce40d2eeb feat: Initialize Zastava Webhook service with TLS and Authority authentication
- Added Program.cs to set up the web application with Serilog for logging, health check endpoints, and a placeholder admission endpoint.
- Configured Kestrel server to use TLS 1.3 and handle client certificates appropriately.
- Created StellaOps.Zastava.Webhook.csproj with necessary dependencies including Serilog and Polly.
- Documented tasks in TASKS.md for the Zastava Webhook project, outlining current work and exit criteria for each task.
2025-10-19 18:36:22 +03:00

31 KiB
Executable File
Raw Blame History

# API & CLI Reference

Purpose give operators and integrators a single, authoritative spec for REST/GRPC calls and firstparty CLI tools (stella-cli, zastava, stella).
Everything here is sourceoftruth 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; sub5s target
Delta check POST /layers/missing <20ms reply; powers delta SBOM feature
Ratelimit / quota Headers XStellaQuotaRemaining, XStellaReset 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) Clientcredentials preferred
Health GET /healthz Simple liveness probe
Attestation * POST /attest (TODO Q12026) 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

StellaOps uses OAuth 2.0 / OIDC (token endpoint mounted via OpenIddict).

POST /connect/token
ContentType: application/x-www-form-urlencoded

grant_type=client_credentials&
client_id=cibot&
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.0Obtain / Refresh OfflineToken

POST /token/offline
Authorization: Bearer <admintoken>
Body field Required Example Notes
expiresDays no 30 Max 90 days
{
  "jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6...",
  "expires": "20250817T00:00:00Z"
}

Token is signed with the backends private key and already contains "maxScansPerDay": {{ quota_token }}.

### 2.1 Scan  Upload SBOM or Image

POST /scan
Param / Header In Required Description
XStellaSbomType header no trivy-json-v2, spdx-json, cyclonedx-json; omitted ➞ autodetect
?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
XStellaQuotaRemaining 129
XStellaReset 20250718T23:59:59Z
XStellaTokenExpires 20250817T00: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
ContentType: application/json
Authorization: Bearer <token>
{
  "layers": [
    "sha256:d38b...",
    "sha256:af45..."
  ]
}

Response 200 — <20ms target:

{
  "missing": [
    "sha256:af45..."
  ]
}

Client then generates SBOM only for the missing layers and reposts /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 policy to 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 quietedBy and quiet flags; 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
}
  • scanId is 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"}}
  • sequence is monotonic starting at 1.
  • correlationId is deterministic ({scanId}:{sequence:0000}) unless a custom identifier is supplied by the publisher.
  • timestamp is ISO8601 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-3def5f362aa475ef14b6",
    "imageDigest": "sha256:deadbeef",
    "verdict": "blocked",
    "policy": { "revisionId": "rev-1", "digest": "27d2ec2b34feedc304fc564d252ecee1c8fa14ea581a5ff5c1ea8963313d5c8d" },
    "summary": { "total": 1, "blocked": 1, "warned": 0, "ignored": 0, "quieted": 0 },
    "verdicts": [
      {
        "findingId": "finding-1",
        "status": "Blocked",
        "ruleName": "Block Critical",
        "ruleAction": "Block",
        "score": 40.5,
        "configVersion": "1.0",
        "inputs": {
          "reachabilityWeight": 0.45,
          "baseScore": 40.5,
          "severityWeight": 90,
          "trustWeight": 1,
          "trustWeight.NVD": 1,
          "reachability.runtime": 0.45
        },
        "quiet": false,
        "sourceTrust": "NVD",
        "reachability": "runtime"
      }
    ],
    "issues": []
  },
  "dsse": {
    "payloadType": "application/vnd.stellaops.report+json",
    "payload": "<base64 canonical report>",
    "signatures": [
      {
        "keyId": "scanner-report-signing",
        "algorithm": "hs256",
        "signature": "<base64 signature>"
      }
    ]
  }
}
  • The report object omits null fields and is deterministic (ISO timestamps, sorted keys).
  • dsse follows the DSSE (Dead Simple Signing Envelope) shape; payload is the canonical UTF-8 JSON and signatures[0].signature is the base64 HMAC/Ed25519 value depending on configuration.
  • A runnable sample envelope is available at samples/api/reports/report-sample.dsse.json 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 Location from the submission call until status transitions away from Pending/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  Q12026)

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.


## 3 StellaOps CLI (stellaops-cli)

The new CLI is built on System.CommandLine2.0.0beta5 and mirrors the Concelier backend REST API.
Configuration follows the same precedence chain everywhere:

  1. Environment variables (e.g. API_KEY, STELLAOPS_BACKEND_URL, StellaOps:ApiKey)
  2. appsettings.json → appsettings.local.json
  3. appsettings.yaml → appsettings.local.yaml
  4. 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 status
auth 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 config show Display resolved configuration Masks secret values; helpful for airgapped 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 + per-image verdict/signed/SBOM status. Accepts newline/whitespace-delimited stdin when piped; --json emits the raw response without additional logging.

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.

Startup diagnostics

  • stellaops-cli now loads Authority plug-in manifests during startup (respecting Authority: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 entrys config path and raise passwordPolicy.minimumLength to at least 12 while keeping requireUppercase, requireLowercase, requireDigit, and requireSymbol set to true to clear the warning; weakened overrides are treated as actionable security deviations.

Logging & exit codes

  • Structured logging via Microsoft.Extensions.Logging with single-line console output (timestamps in UTC).
  • --verbose / -v raises log level to Debug.
  • Command exit codes bubble up: backend conflict → 1, cancelled via CTRL+C130, scanner exit codes propagate as-is.

Artifact validation

  • Downloads are verified against the X-StellaOps-Digest header (SHA-256). When StellaOps:ScannerSignaturePublicKeyPath points to a PEM-encoded RSA key, the optional X-StellaOps-Signature header 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 run executions create timestamped JSON artefacts inside ResultsDirectory plus a scan-run-*.json metadata 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:

  1. Configure Authority settings via config or env vars (see sample below). Minimum fields: Url, ClientId, and either ClientSecret (client credentials) or Username/Password (password grant).
  2. Run stellaops-cli auth login to acquire and cache a token. Use --force if you need to ignore an existing cache entry.
  3. Execute CLI commands as normal—the backend client injects the cached bearer token automatically and retries on transient 401/403 responses with operator guidance.
  4. Inspect the cache with stellaops-cli auth status (shows expiry, scope, mode) or clear it via stellaops-cli auth logout.
  5. Run stellaops-cli auth whoami to dump token subject, audience, issuer, scopes, and remaining lifetime (verbose mode prints additional claims).
  6. Expect Concelier to emit audit logs for each /jobs* request showing subject, 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.

Configuration file template

{
  "StellaOps": {
    "ApiKey": "your-api-token",
    "BackendUrl": "https://concelier.example.org",
    "ScannerCacheDirectory": "scanners",
    "ResultsDirectory": "results",
    "DefaultRunner": "docker",
    "ScannerSignaturePublicKeyPath": "",
    "ScannerDownloadAttempts": 3,
    "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 FirstParty 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 RetryAfter 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 stellazastava

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 problemdetails 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 prebuilt SBOM and add ?sbom-only=true to /scan for <1s path.
  • Airgapped? point --server to http://oukgw:8080 inside the Offline Update Kit.
  • YAML vs Rego YAML simpler; Rego unlocks timebased logic (see samples).
  • Cosign verify plugins enable SCANNER_VERIFY_SIG=true env to refuse unsigned plugins.

## 7 Planned Changes (Beyond 6 Months)

These stay in Feature Matrix → To Do until design is frozen.

Epic / Feature API Impact Sketch
SLSA L1L3 attestation /attest (see §2.4)
Rekor transparency log /rekor/log/{id} (GET)
Plugin 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


## 9 Changelog (truncated)

  • 20250714 added delta SBOM, policy import/export, CLI --sbom-type.
  • 20250712 initial public reference.