component_architecture_cli.md — Stella Ops CLI (2025Q4)
Consolidates requirements captured in the Policy Engine, Policy Studio, Vulnerability Explorer, Export Center, and Notifications implementation plans and module guides.
Scope. Implementation‑ready architecture for Stella Ops CLI (
stella/stellaops): command surface, process model, auth (Authority OAuth2 bearer tokens), integration with Scanner/Excititor/Concelier/Signer/Attestor, offline kit behavior, packaging, observability, security posture, and CI ergonomics.
Source-of-truth note (2026-05-31 reconciliation). Several subsections below describe the intended 2025Q4 design (notably DPoP sender-constrained tokens and the Buildx generator integration) that is not present in the current
src/Cli/implementation; such items are flagged inline as (roadmap — not implemented). The authoritative root command tree is registered insrc/Cli/StellaOps.Cli/Commands/CommandFactory.cs(CommandFactory.Create, the root wired fromProgram.cs); the legacyCliApplication.csparallel root (never wired intoProgram.cs) was deleted on 2026-07-03 (DTC-11) together with its stub handlers. The implementation today exposes ~100 top-level command groups in the core tree (verified: ~100root.Add(Build*/…CommandGroup.Build*)registrations inCommandFactory.Create), plus the non-conflictingexcititor,runtime, andoffline kitadditions contributed by restart-time CLI plug-ins — many more than this dossier’s §2 enumerates; see §2.15 for the major omitted groups.
CLI example gate (2026-07-29). Living documentation is checked against the released
CommandFactorytree, including hiddencli-routes.jsonaliases and all eight manifest-backed restart-time plug-ins packaged bydevops/docker/Dockerfile.cli. Examples the parser rejects are explicitly classified as unshipped or stale inunshipped-command-examples.json; see the operator-readable policy. Do not treat a baselined example as runnable merely because it appears elsewhere in this historical design dossier.
0) Mission & boundaries
Mission. Provide a fast, deterministic, CI‑friendly command‑line interface to drive Stella Ops workflows:
- Build‑time SBOM generation via Buildx generator orchestration.
- Post‑build scan/compose/diff/export against Scanner.WebService.
- Policy operations and VEX/Vuln data pulls (operator tasks).
- Verification (attestation chains, signatures, evidence bundles) for audits.
- Air‑gapped/offline kit administration.
Boundaries.
- Most workflow signing remains server-side through Signer/Attestor, but the explicit operator commands
stella crypto signandstella crypto verifyperform local/provider-backed cryptographic operations when the active CLI profile exposes signing keys. - CLI does not store long‑lived credentials beyond OS keychain; tokens are short (Authority OpToks).
- Heavy work (scanning, merging, policy) is executed server‑side (Scanner/Excititor/Concelier).
1) Solution layout & runtime form
src/Cli/
|- StellaOps.Cli/ # net10.0 main host (the `stella`/`stellaops` binary)
| |- Commands/CommandFactory.cs # authoritative root command tree (CommandFactory.Create)
| |- Commands/CommandHandlers.cs # remaining cross-command orchestration handlers
| |- Commands/CommandHandlers.Auth.cs # auth cache/grant/credential value helpers
| |- cli-routes.json # embedded deprecated->canonical route map
|- __Libraries/ # restart-time plug-in command bundles:
| |- StellaOps.Cli.Plugins.NonCore/ # Excititor / runtime / offline-kit groups
| |- StellaOps.Cli.Plugins.{Aoc,GroundTruth,Policy,Symbols,Timestamp,Verdict,Vex}/
|- StellaOps.Cli.Tests/ , __Tests/ # unit + golden-output tests
|- plugins/ # plug-in drop directory (plugins/cli/**)
Correction (2026-05-29): there is no
StellaOps.Cli.Coreproject; verb plumbing, config, HTTP, and auth wiring live insideStellaOps.Cliitself (Commands/,Services/,Configuration/). The singlepackaging/layout above is illustrative — see §15 for the real distribution targets.
CommandHandlers is a partial static command-orchestration type. Authentication cache metadata, cached-grant resolution, password-grant credential normalization, and sensitive-token log fingerprints live in CommandHandlers.Auth.cs; command construction and runtime orchestration remain in CommandHandlers.cs. New command-family extractions should follow this boundary without changing the public command tree or moving stateful service calls into value-helper files.
Language/runtime: .NET 10. (The 2025Q4 target of Native AOT / musl-static single-file publish is roadmap — not yet enabled in StellaOps.Cli.csproj, which currently has no PublishAot/PublishSingleFile/PublishTrimmed.)
Plug-in verbs. Non-core verbs ship as restart-time plug-ins under plugins/cli/** with manifest descriptors. The launcher loads plug-ins on startup; hot reloading is intentionally unsupported. The source-free release image includes all current CLI plug-in payloads under /app/plugins/cli: StellaOps.Cli.Plugins.Aoc, GroundTruth, NonCore, Policy, Symbols, Timestamp, Verdict, and Vex. The loader snapshots the core root command tokens before each module registration and removes newly-added duplicate roots, so plug-in assemblies whose root commands are already core-integrated do not crash startup or hide core help. Current non-conflicting plug-in additions visible from the release image are excititor, runtime, and the offline kit extension. Each plug-in publishes a *.manifest.json file beside its assembly under plugins/cli/StellaOps.Cli.Plugins.<Name>/.
Source-free operator image. Released deployments publish a dedicated stellaops/cli:<version> image from devops/docker/Dockerfile.cli. The image contains the compiled CLI, the restart-time CLI plug-in tree included in the publish output, and stella / stellaops entrypoints. Compose deployments run it through devops/compose/docker-compose.cli.yml and devops/compose/scripts/stella-cli.*, which forward STELLAOPS_BACKEND_URL, STELLAOPS_AUTHORITY_*, API_KEY, and a /workspace setup-config bind mount. This is the supported released-site setup/status/admin control path when the operator does not have repository sources.
OS targets: linux‑x64/arm64, windows‑x64/arm64, macOS‑x64/arm64.
1.1) Command Routing Infrastructure (v2.x→v3.0 Migration)
Sprint: SPRINT_20260118_010_CLI_consolidation_foundation
The CLI includes a command routing infrastructure to support backward-compatible command migration. This enables consolidating 81+ top-level commands into ~18 organized command groups while maintaining backward compatibility.
Routing Components
src/Cli/StellaOps.Cli/Infrastructure/
├── ICommandRouter.cs # Router interface
├── CommandRouter.cs # Route registration and lookup
├── CommandRoute.cs # Route model (old→new path mapping)
├── CommandGroupBuilder.cs # Fluent builder for command groups
├── DeprecationWarningService.cs # Warning display on stderr
├── RouteMappingConfiguration.cs # JSON config model + loader
src/Cli/StellaOps.Cli/
└── cli-routes.json # Embedded route mappings (~105 old→new entries)
How Routing Works
- At startup,
CommandFactory.RegisterDeprecatedAliases()loadscli-routes.json(embedded resource) - For each deprecated route, creates a hidden alias command that:
- Delegates to the canonical command
- Shows a deprecation warning on stderr (once per session)
- Warnings include the old path, new path, removal version, and suppression instructions
Route Configuration Schema
{
"version": "1.0",
"mappings": [
{
"old": "scangraph",
"new": "scan graph",
"type": "deprecated",
"removeIn": "3.0",
"reason": "Consolidated under scan command"
}
]
}
Deprecation Warning Format
WARNING: 'stella scangraph' is deprecated and will be removed in v3.0.
Use 'stella scan graph' instead.
Set STELLA_SUPPRESS_DEPRECATION_WARNINGS=1 to hide this message.
Timeline
- v2.x: Both old and new command paths work; old paths show deprecation warnings
- v3.0: Old command paths removed
Migration Guide
See migration-v3.md for user-facing migration instructions and command mappings.
2) Command surface (verbs)
All verbs default to JSON output when
--jsonis set (CI mode). Human output is concise, deterministic.
2.1 Auth & profile
auth login- Modes: interactive password (current default for seeded
stellaops-cliin fresh local/dev shells), client-credentials (service principal), and future device-code/PKCE once enabled by the Authority profile. - Startup Authority/crypto diagnostics are opt-in via
--verboseand stay suppressed for structured output flags such as--json,--raw, and--format json. - Flags:
--force(ignore cached tokens),--no-save(memory-only; implied bySTELLAOPS_LOGIN_NO_SAVE=1in non-interactive shells). - Produces an Authority OAuth2 access token (OpTok) and caches it in the OS-protected secrets blob (see §3.2). (No DPoP keypair is generated — that is roadmap.)
- Modes: interactive password (current default for seeded
auth status— display cached token status.auth whoami— display cached token claims (subject, scopes, expiry).auth logout— remove cached tokens for the current credentials.auth revoke export|verify— export the Authority revocation bundle + detached JWS signature, or verify a bundle offline against a PEM key.auth token mint ...— service-account token minting/delegation (CLI-TEN-49-001).
2.2 Build‑time SBOM (Buildx) — roadmap, not implemented
There is no
buildxcommand group inCommandFactory.cs. The Buildx-generator wrapper described here (and the orchestration in §6) is a 2025Q4 design item that is not present in the current CLI. SBOM generation today goes throughscan run,scan image,sbomandscannercommand groups. The intended surface was:
buildx install— install/update the StellaOps.Scanner.Sbomer.BuildXPlugin on the host.buildx verify— ensure generator is usable.buildx build— thin wrapper arounddocker buildx build --attest=type=sbom,generator=stellaops/sbom-indexer.
2.3 Scanning & artifacts
Reconciliation (2026-05-29). The real surface differs substantially from the 2025Q4 sketch below. Authoritative forms:
scan run --runner <dotnet|self|docker> --entry <path|image> --target <dir> [analysis flags] [-- args...]— execute a scanner bundle with the configured runner (the primary scan verb).scan image inspect --ref <ref> [-o table|json]andscan image layers --ref <ref>— image metadata/layer inspection (currently lightweight;scan imageis not the rich<ref|digest> --view/--format/--attestverb sketched below).scan diff— binary diff of two artifacts (BinaryDiffCommandGroup), notdiff image --old/--new.scan upload,scan sarif,scan replay,scan delta,scan verify-patches,scan layers,scan layer-sbom,scan recipe,scan graph,scan secrets,scan workers,scan download,scan entry-trace,scan gate-policy,scan gate-results, andscan submit-listare the other realscansubcommands (all added inBuildScanCommand/CommandFactory.cs).- SBOM artifact handling lives under the top-level
sbomgroup:sbom list|show|upload|compare|export|parity-matrix(e.g.sbom upload --file <path>for BYOS;sbom exportto download).- There is no
report finalcommand. PASS/FAIL release reporting is delivered through thegate,verdict,verify release, anddeterminism reportgroups.
Original 2025Q4 sketch (retained for intent; flag names not all implemented):
scan image <ref|digest>— options--force,--wait,--view=inventory|usage|both,--format=cdx-json|cdx-pb|spdx-json,--attest.diff image --old <digest> --new <digest> [--view ...]— layer‑attributed changes.export sbom <digest> [--view ... --format ... --out file]— download artifact.report final <digest> [--policy-revision ... --attest]— backend PASS/FAIL report + optional attestation.
AI Code Guard
stella guard run <path> is registered in the production root as a bounded source collector and typed client for the Integrations-owned POST /api/v1/integrations/ai-code-guard/run contract. It does not duplicate analyzers locally. The typed HTTP client uses the CLI egress guard, requests integration:operate, and propagates the effective tenant through standard API authentication when Authority is configured. JSON is the canonical response; text, SARIF, and GitLab are projections. Pass exits 0, while warnings, failures, pending/error responses, invalid input, and unavailable service paths are nonzero.
Collection prunes generated/dependency directories before descent, skips reparse points, validates paths remain under the requested root, and fails closed on inaccessible input or traversal/file/policy/aggregate-payload bounds. Policy bytes count toward the aggregate request bound. guard status reports contract ownership but deliberately does not claim a live health check. --sealed fails closed because no verified offline analyzer bundle is registered.
2.3.1 Compare Commands & Baseline Selection (SPRINT_20260208_029)
The compare command group supports diffing scan snapshots with automatic baseline resolution.
Commands
compare diff --base <digest> --target <digest>— Full comparison showing detailed diff.compare summary --base <digest> --target <digest>— Quick summary of changes.compare can-ship --base <digest> --target <digest>— Check if target passes policy (exit code: 0=pass, 1=fail).compare vulns --base <digest> --target <digest>— List vulnerability changes only.
Baseline Selection Strategies
All compare commands support --baseline-strategy for automatic baseline resolution:
| Strategy | Description | Requirements |
|---|---|---|
explicit (default) | Uses the digest provided via --base | --base required |
last-green | Selects most recent passing snapshot | --artifact required |
previous-release | Selects previous release tag from registry metadata | --artifact required |
Options:
--baseline-strategy <explicit|last-green|previous-release>— Strategy for baseline selection--artifact <purl|oci-ref>— Artifact identifier for auto-resolution strategies--current-version <tag>— Current version (helpsprevious-releasefind older releases)--verification-report <path>- Attachbundle verify --output jsonchecks to compare output (hash/signature overlay)--reverify-bundle <directory>- Recompute artifact hash and DSSE-sidecar status from local evidence bundle for live re-verification--determinism-manifest <path>- Attach determinism manifest score/threshold summary to compare output
Examples:
# Explicit baseline (traditional)
stella compare can-ship --base sha256:abc123 --target sha256:def456
# Auto-select last green baseline
stella compare can-ship --target sha256:def456 \
--baseline-strategy last-green \
--artifact pkg:oci/myapp
# Use previous release as baseline
stella compare can-ship --target sha256:def456 \
--baseline-strategy previous-release \
--artifact pkg:oci/myapp \
--current-version v2.0.0
# Compare diff with inline verification overlay and determinism context
stella compare diff --base sha256:abc123 --target sha256:def456 \
--verification-report ./verify-report.json \
--reverify-bundle ./evidence-bundle \
--determinism-manifest ./determinism.json
Resolution Behavior:
last-green: Queries forensic snapshot store for latest artifact snapshot withverdict:passtag.previous-release: Queries for release-tagged snapshots, excludes--current-version, returns most recent.- Both strategies show suggestions when resolution fails.
- Verification overlay:
compare diffcan now include per-artifacthash/signaturestatus plus determinism score context in table and JSON outputs.
Service Interface:
public interface IBaselineResolver
{
Task<BaselineResolutionResult> ResolveAsync(BaselineResolutionRequest request, CancellationToken ct);
Task<IReadOnlyList<BaselineSuggestion>> GetSuggestionsAsync(string artifactId, CancellationToken ct);
}
2.4 Policy & data
policy get/set/apply— fetch active policy, apply staged policy, compute digest.excititor export— trigger Excititor export generation. This verb ships only with theStellaOps.Cli.Plugins.NonCorerestart-time plug-in, not the core tree (NonCoreCliCommandModule.BuildExcititorCommand). The full pluginexcititorgroup isinit,pull,resume,list-providers,show-provider,enable-provider,disable-provider,run-provider,update-provider,export,backfill-statements,verify,reconcile.- There is no
conceliercommand in the current CLI (core or plugin). The “trigger/export canonical JSON or Trivy DB” 2025Q4 sketch is not implemented. Concelier advisory-source administration is reached via the coresourcesgroup (§2.4.0) and the federationfeedsergroup; Concelier job triggering is gated by theconcelier.jobs.trigger/concelier.mergescopes but is not exposed as aconcelier exportverb.
2.4.0 Advisory sources & VEX providers (Sprint 20260503-013)
config sources list/check/enable/disable/status [--format table|json] [--json]— read and mutate Concelier’s persisted advisory-source operator state through/api/v1/advisory-sources.listmerges the live catalog with persisted enablement/readiness,checkuses bounded per-source API requests and returns non-zero for unhealthy results, enable/disable use the persisted batch endpoints, andstatusrenders live sync/freshness/error counters. API failures and malformed responses fail closed.sources listremains a hidden deprecated alias with a warning; the separatesources ingestworkflow remains registered. The other top-level management verbs remain compatibility entry points over the same HTTP-backed handlers.vex providers list [--include-disabled]— list Excititor VEX providers viaGET /excititor/providerswith readiness, trust, and persisted-override metadata.vex providers show <providerId>— fetch one provider viaGET /excititor/providers/{id}. Renders trust-tier, base URIs, PGP fingerprints, cosign issuer/identity pattern.vex providers configure <providerId> [--set k=v ...] [--clear k ...] [--upload-artifact k=@path]— inspect or update persisted provider configuration (/excititor/providers/{id}/configuration+/artifacts). Sensitive values submitted blank are retained server-side.vex providers enable|disable <providerId>— operator-override enablement intent (POST /excititor/providers/{id}/enable|disable).vex providers sync <providerId> [--since <iso8601>] [--window 24h] [--force]— trigger an ingest run when readiness allows (POST /excititor/providers/{id}/run). Returns exit code 3 when the connector is not ready, 4 when the provider is unknown.vex providers check [<providerId>]— read-only health summary derived from list/show responses; never triggers a run.
Supply-chain safety gate. configure rejects AllowUnsigned=true or TrustWeight>1.0 unless --i-understand-this-weakens-supply-chain is also passed. This mirrors the Console confirmation modal so a copy-pasted automation script cannot accidentally weaken Stella Ops’s default fail-closed posture. Server-side validation independently rejects TrustWeight>1.0 and audit-logs AllowUnsigned flips regardless of the CLI gate.
2.4.1 Assurance packs
assurance packs list [--json]- list embedded optional Assurance pack descriptors without network access.assurance pack inspect <packId> [--json]- inspect one embeddedassurance-pack-v1descriptor.assurance pack status <packId> --tenant <tenant> [--json]- compose live Authority, ExportCenter, and Notify readiness for one tenant and fail closed unless every required line is configured.
Assurance commands use assurance as the product noun rather than europe or eu. packs list and pack inspect are local descriptor commands and must produce deterministic output from embedded descriptors only. pack status is a live readiness command: missing tenant profile, unavailable APIs, missing signing key, missing storage/trust roots, missing reporting profile, and publication preflight blockers return stable reason codes and non-zero exit status. It must not print secret values, local secret paths, or failed service response bodies.
Initial descriptors cover nis2, dora, cra.product-security, and cra.technical-documentation. NIS2 and DORA descriptors include evidence scope metadata so CLI output distinguishes Stella-observed software-estate coverage from operator-supplied legal, governance, contract, and filing data. Existing pack-specific export commands remain authoritative for artifact creation and verification; Assurance status links to those commands instead of replacing them.
See stella assurance and Assurance Runtime.
2.4.2 QA fixture seed artifacts
qa seed advanced-assurance-golden --manifest <fixture.json> --output <runRoot>/golden-fixture/seed/seed-manifest.json [--json]- materialize deterministic local seed artifacts for the advanced-assurance rerun contract.
The QA seed command is an offline artifact materializer. It reads the checked-in advanced-assurance-golden fixture manifest, writes seed-manifest.json, seed-integrity.json, and seed-command-output.txt, and records stable IDs, fixture digests, generated artifact names, scenario counts, and the fixture’s deterministic seed timestamp. It does not claim that live service databases were populated; live API/UI reruns still need the service-side fixture readback routes before scenarios can be marked PASS.
2.4.3 Asset inventory
inventory list [--input <asset-registry.json>] [--tenant <id>] [--type <asset-type>] [--include-tombstones] [--format table|json]- list Graph asset inventory entries.inventory show <asset-id> [--input <asset-registry.json>] [--tenant <id>] [--format table|json]- show one asset without expanding sensitive actor, credential, token, or person fields.inventory export [--input <asset-registry.json>] [--tenant <id>] [--type <asset-type>] [--include-tombstones] --format csv|json|cyclonedx-bom [--output <path>]- export a deterministic inventory view.
When --input is supplied, inventory commands are offline and read only the pinned asset-registry.v1 JSON file. When --input is omitted, the CLI uses the configured backend client and Graph asset runtime routes (POST /graph/assets/query, GET /graph/assets/{assetId}, POST /graph/assets/export). Missing backend configuration, runtime errors, schema mismatches, export digest mismatches, and repeated cursors fail closed and do not emit placeholder inventory data. Live export requests Graph JSON, then renders through the same deterministic CLI CSV/JSON/CycloneDX pipeline as offline input mode.
2.5 Verification
verify attestation --uuid <rekor-uuid> | --artifact <sha256> | --bundle <path>— call Attestor /verify and print proof summary.verify image <ref|digest>— verify the attestation chain for a container image (BuildVerifyImageCommand). (There is noverify referrersorverify image-signaturesubcommand — those 2025Q4 names are not implemented.)- Additional real
verifysubcommands:verify offline,verify bundle,verify vex,verify patch,verify sbom,verify sar, plus the compliance verifiersverify cra-tech-file,verify dora-roi,verify dora-incident-report,verify dora-info-sharing, andverify tlpt-pack. verify release <bundle> [--sbom <path>] [--vex <path>] [--trust-root <path>] [--checkpoint <path>]— run promotion bundle verification and fail if source/build/rekor/signature links cannot be validated.verify release-manifest <file> --trust-root <path> [--json]- verify a Signer DSSE product update manifest with local PEM trust roots and no network access.verify product-csaf-advisory <csaf.json> --envelope <dsse.json> --trust-root <pem-or-dir> [--key-id <id>] [--json]- verify a signed Stella product CSAF advisory offline against local manufacturer PEM trust roots, including the canonical CSAF JSON hash bound in the DSSE envelope.verify nis2-effectiveness --bundle <file> [--signing-key-file <file>] [--json]- verify the local monthly NIS2 effectiveness bundle schema, hashes, DSSE payload bytes, and local HMAC signature without network access.
2.5.1 Attestor transparency controls
attestor transparency targets list|show|upsert|enable|disablemanages external Rekor-compatible mirror targets. Target creation accepts only secret references in--auth-ref; raw bearer tokens and credential material are rejected before any API call.attestor transparency sync run --target <name-or-id> [--max-entries N] [--max-runtime-seconds N] [--dry-run] [--live --confirm-egress-window]runs a bounded connected-window external mirror sync. Dry-run is the default. Live sync is refused in sealed/offline mode and always requires--confirm-egress-window.attestor transparency sync reportreads connected sync batches and renders mirrored, pending, failed, skipped duplicate, and last-local-index counts.attestor transparency receipts list|showreads Attestor/api/v1/attestations*and rendersLocal transparency receiptseparately from additiveExternal mirror receipt,External mirror pending, andExternal mirror failedentries. JSON output preserveslocalTransparencyReceiptandexternalMirrorReceipts[]without collapsing them into the legacymirrorfield.airgap exchange transparency schedule create|relay-import create|import validate|import apply|statusis the controlled-media manual exchange path. The same implementation is also discoverable underattestor transparency exchange ....
Manual exchange packages are file-preserving: the CLI writes the package file bytes returned by Attestor and a descriptor (stellaops-transparency-package.json) so operators can move either a directory or descriptor through controlled media. The signing certificate and signature metadata come from the installation authority path selected by the backend for the required regional crypto profile; the CLI does not generate certificates or private keys.
2.5.2 Truthfulness requirements for local evidence commands
Evidence-oriented commands must fail closed when the verifier, trust store, transparency log, or service interface required to prove the claim is not available. Commands must not print success, create placeholder bundles, or emit unsigned/fake DSSE envelopes that look like release evidence.
Current local behavior:
deltasig signanddeltasig verifyuse DSSE PAE signing and verification with supplied ECDSA or RSA PEM key material. Unsupported algorithms and invalid signatures return non-zero verification failures.attest patchrequires--no-rekorand a local--keyfor local DSSE output.--publish, Rekor publication, and keyless signing return explicit failures until the Authority/Attestor integration is wired.- Proof key-rotation commands require a configured key-rotation service and return explicit failure instead of simulating trust-anchor changes or successful validity checks.
- Reachability slice verification/export and related graph/witness verification paths return explicit unavailable/not-implemented failures until ReachGraph/Attestor replay and export contracts are wired. They must not produce fabricated replay summaries or archive paths.
reachability explain,reachability witness,reachability guards,reachability graph list|show|slice,reachability slice create|show, andreachability witness-ops list|show|exportare discoverable roadmap commands without a real query service. They return exit code9(NotImplemented), emit no verdict/path/guard/count, and write no output file. The local-filereachability show|exportpaths and the Scanner-backedreachability tracepath remain implemented.config signals inspect|list|summarylikewise return exit code9and emit no signal rows until a persisted Signals query client is wired.config signals verify-chainremains implemented for signed evidence files on disk.- Binary micro-witness generation requires the patch verification orchestrator. If it is not registered or it fails,
witness generateexits non-zero and writes no witness. Local placeholder HMAC signatures and synthetic Rekor metadata are disabled. - PoE verification treats DSSE as cryptographic evidence only when trusted keys are supplied and the DSSE signature verifies. SHA-256 digests are labeled as
sha256; unsupported BLAKE3 digest inputs fail closed instead of being recomputed with SHA-256. aoc verifymust surface stored-row hash-chain errors such as missing document hashes and mismatchedprevious_hashlinks asERR_AOC_00xviolations.groundtruth validate run,groundtruth validate metrics, andgroundtruth validate exportfail closed until a real validation harness, security-pair resolver, and persisted run store are configured. They must not synthesize validation progress, match rates, false-negative rates, reports, DSSE attestations, or Rekor claims.- Identity watchlist commands require a durable watchlist service/API. Until that service is wired, add/list/get/update/remove/test/alerts return non-zero and do not use sample entries or synthetic alerts.
mirror createis discoverable but is not yet wired to an authoritative mirror exporter. It returns exit code9withmirror_create_not_implemented, writes no files, and points operators to the implementedfeedser bundle exportandmirror seed exportpaths. Signing and attestation flags do not weaken this fail-closed boundary.- Setup migration execution is not a CLI simulation surface. Services own PostgreSQL startup migrations; the setup step fails closed when no real migration runner is configured.
sbom generate, golden-setverify-fix, andtrust snapshot exportmust not write placeholder SBOMs, verification verdicts, SARIF, attestations, or trust snapshots. They return non-zero until Scanner, BinaryIndex, and trust snapshot writer contracts are wired.- Symbols plugin
ingest --dry-runmust not report success without the real symbol extractor; it exits non-zero and writes no manifest until extraction is implemented. - Scanner runner execution treats a missing scanner output artifact as a failed run even when the child process exits 0. It records run metadata only, does not create
{"status":"placeholder"}scan artifacts, and does not upload a placeholder result. scan verify-patches --scan-idrequires a real Scanner scan-result resolver before deriving image digest, artifact PURL, CVE set, or patch verdicts. Without that resolver it exits non-zero and writes no output file.poe exportrequires a ReachGraph/Attestor PoE export service and configured trusted-key bundle provider. Until those are wired, it exits non-zero and writes no PoE bundle, trusted keys, manifest, or archive.attest fixchainresolves the BinaryIndex/analyzer source digest from trusted local analyzer metadata when--analyzer-metadata <file>and--analyzer-metadata-digest sha256:<64 hex>are supplied. The metadata digest is the SHA-256 of the exact metadata file bytes and must match before the embeddedsourceDigest, analyzer name, and analyzer version are accepted. Without trusted metadata, the command requires--analyzer-source-digest sha256:<64 hex>,--analyzer-name, and--analyzer-versionas a manual fallback. Missing, malformed, tampered, or disagreeing metadata/source digests exit non-zero and write no envelope; the command must never emitsha256:unknownor invent analyzer identity.export nis2-effectivenessrequires a localnis2-effectiveness-dashboard-v1report and local HMAC key material until live Platform export resolution and production Signer/KMS/CAdES support are wired. Missing target/control/policy snapshot references are recorded as blockers in the bundle, not synthesized.
2.6 Runtime (Zastava helper)
runtime policy test --image/-i <digest> [--file <path> --ns <name> --label key=value --json]— ask backend/policy/runtimelike the webhook would (accepts multiple--image, comma/space lists, or stdin pipelines).- The
runtimegroup is aStellaOps.Cli.Plugins.NonCoreplug-in command (NonCoreCliCommandModule.BuildRuntimeCommand), not part of the coreCommandFactory.Createtree. It is present only when the NonCore plug-in is loaded fromplugins/cli/**.
- The
2.7 Offline kit
Correction (2026-05-31): the core
OfflineCommandGroupis always present and registered inCommandFactory.Createwith flatoffline importandoffline status. When the NonCore plug-in is loaded, it extends that existingofflinecommand with akitsub-level (kit pull,kit import, andkit status) instead of registering a second rootofflinecommand.
Historical correction (2026-05-30, superseded by 2026-05-31 implementation note above): earlier builds had two
offlineregistration paths depending on plug-in load state:
- The core
OfflineCommandGroup(always present, registered inCommandFactory.Create) exposes a flatoffline importandoffline status— nokitsub-level at this layer.- The NonCore plug-in (
NonCoreCliCommandModule.BuildOfflineCommand) adds anoffline kitsub-level withkit pull,kit import, andkit status(offline-kit bundle workflows). Sooffline kit pulland the--bundle-id/--destination/--overwrite/--no-resumedownload path do exist when the NonCore plug-in is loaded. Mirror produce/fetch is also handled by themirrortop-level group.
offline import <tar>— (core) import an offline kit with verification into on‑prem services.offline status— (core) display current offline kit status / seed versions.offline kit pull|import|status— (NonCore plug-in) download/upload/inspect offline-kit bundles.mirror ...— manage air-gap mirror bundles for offline distribution (the fetch/produce side).
2.8 Utilities
idp list|show|add|update|remove|test|enable|disable|apply— administer the process-global Authority identity-provider store through/console/admin/identity-providers; the CLI does not call or write the retired Platform facade.config idpandconfig identity-providersremain compatibility routes to the same command group. Provider names, rather than legacy Platform row IDs, address read/write/apply operations. Operator-configurable types areldap,oidc, andsaml; legacyadspellings select the LDAP preset instead of inventing an Authority provider type. Candidate and stored configuration is carried as JSON without flattening value types, and sensitive fields accept only Authority-supportedenv:,file:,vault:, orsecret:references.config show/config list— display resolved configuration values and config paths. (Real subcommands areshow/listplus the consolidated settings groupsconfig notify|integrations|feeds|registry|sources|signals—BuildConfigCommandinCommandFactory.cs; there is noconfig set/config get.config list --categoryrecognisesnotify|feeds|integrations|registry|sources|signals|policy|scanner.)auth whoami— short auth display. (There is no top-levelwhoami; it lives underauth.)self update— check, verify, and apply CLI updates. (There is no standalone top-levelversioncommand; version/update checks are under theselfgroup.)tools lint|benchmark|migrate ...— local policy/config linting, benchmarks, and migration utilities. (Thetools policy-dsl-validate/policy-schema-export/policy-simulation-smokeverbs listed in 2025Q4 are not in the currentToolsCommandGroup; they survive only in legacy tests.)
2.8.1 User locale preference commands
tenants locale list [--tenant <id>] [--json]- Fetches tenant-visible locale catalog from Platform
GET /api/v1/platform/localization/locales. - Provides the canonical locale set used by both CLI and UI selection controls.
- Supports deterministic text output or JSON payload (
locales,count) for automation.
- Fetches tenant-visible locale catalog from Platform
tenants locale get [--tenant <id>] [--json]- Fetches the authenticated actor’s persisted locale preference from Platform
GET /api/v1/platform/preferences/language. - Resolves tenant context from
--tenant, thenSTELLAOPS_TENANT, then active tenant profile. - Prints deterministic text output by default (
tenant,locale,updated) and optional JSON payload for automation.
- Fetches the authenticated actor’s persisted locale preference from Platform
tenants locale set <locale> [--tenant <id>] [--json]- Writes the authenticated actor’s persisted locale preference through Platform
PUT /api/v1/platform/preferences/language. - Supported locale set is service-validated (
en-US,de-DE,bg-BG,ru-RU,es-ES,fr-FR,uk-UA,zh-TW,zh-CN); CLI pre-validates against the platform locale catalog when available. - This command shares the same preference record consumed by the Web shell locale selector so locale choice follows the user across Web and CLI sessions.
- Writes the authenticated actor’s persisted locale preference through Platform
2.9 Aggregation-only guard helpers
sources ingest --dry-run --source <id> --input <path|uri> [--tenant ... --format table|json --output file]- Normalises documents (handles gzip/base64), posts them to the backend
aoc/ingest/dry-runroute, and exits non-zero when guard violations are detected. - Defaults to table output with ANSI colour;
--json/--outputproduce deterministic JSON for CI pipelines.
- Normalises documents (handles gzip/base64), posts them to the backend
aoc verify [--since <ISO8601|duration>] [--limit <count>] [--sources list] [--codes list] [--format table|json] [--export file] [--tenant id] [--no-color]- Replays guard checks against stored raw documents. Maps backend
ERR_AOC_00xcodes onto deterministic exit codes so CI can block regressions. - Supports pagination hints (
--limit,--since), tenant scoping via--tenantorSTELLA_TENANT, and JSON exports for evidence lockers.
- Replays guard checks against stored raw documents. Maps backend
2.10 Key management (file KMS support)
kms export --key-id <logicalId> --output <file> [--version <id>] [--force]- Decrypts the file-backed KMS store (passphrase supplied via
--passphrase,STELLAOPS_KMS_PASSPHRASE, or interactive prompt) and writes a portable JSON bundle (KmsKeyMaterial) with key metadata and coordinates for offline escrow or replication.
- Decrypts the file-backed KMS store (passphrase supplied via
kms import --key-id <logicalId> --input <file> [--version <override>]- Imports a previously exported bundle into the local KMS root (
kms/by default), promotes the imported version toActive, and preserves existing versions by marking themPendingRotation. Prompts for the passphrase when not provided to keep automation password-safe.
- Imports a previously exported bundle into the local KMS root (
2.11 CI Template Generation (Sprint 015)
ci init --platform <github|gitlab|gitea|all> [--template <gate|scan|verify|full>] [--mode <scan-only|scan-attest|scan-vex>] [--output <dir>] [--force] [--offline] [--scanner-image <ref>]- Generates ready-to-run CI workflow templates for the specified platform(s).
- Template types:
gate- PR gating workflow that blocks merges on policy violations.scan- Scheduled/push scan workflow for container images.verify- Verification workflow for attestations and signatures.full- All templates combined.
- Modes control attestation behavior:
scan-only- Scan without attestation.scan-attest- Scan and create attestations (default).scan-vex- Scan with VEX document generation.
--offlinegenerates templates with pinned digests for air-gapped environments.
ci list- Lists available template types and supported platforms.
ci validate <workflow-file>- Validates a generated workflow file for correctness.
- Checks integration IDs, registry endpoints, and AuthRef references.
Generated files:
- GitHub:
.github/workflows/stellaops-{gate,scan,verify}.yml - GitLab:
.gitlab-ci.ymlor.gitlab/stellaops-{scan,verify}.yml - Gitea:
.gitea/workflows/stellaops-{gate,scan,verify}.yml
Implementation: CiCommandGroup.cs, CiTemplates.cs in src/Cli/StellaOps.Cli/Commands/.
Both subcommands honour offline-first expectations (no network access) and normalise relative roots via --root when operators mirror the credential store.
2.11 Advisory AI (RAG summaries)
advise run <summary|conflict|remediation> --advisory-key <id> [--artifact-id id] [--artifact-purl purl] [--policy-version v] [--profile profile] [--section name] [--force-refresh] [--timeout seconds]- Calls the Advisory AI service (
/v1/advisory-ai/pipeline/{task}+/outputs/{cacheKey}) to materialise a deterministic plan, queue execution, and poll for the generated brief. - Renders plan metadata (cache key, prompt template, token budgets), guardrail results, provenance hashes/signatures, and citation list. Exit code is non-zero if guardrails block or the command times out.
- Uses
STELLAOPS_ADVISORYAI_URLwhen configured; otherwise it reuses the backend base address and addsX-StellaOps-Scopes(advisory:run+ task scope) per request. --timeout 0performs a single cache lookup (for CI flows that only want cached artefacts).
- Calls the Advisory AI service (
advise ask "<question>" [--image|-i <ref>] [--digest|-d <digest>] [--environment|-e <name>] [--evidence] [--no-action] [--conversation-id <id>]- Calls
POST /api/v1/chat/query, returns the current hosted response contract with evidence refs, and supports unscoped, image-, digest-, and environment-scoped questions. --no-actiondisables action proposals;--evidenceforces evidence chips in output.--file <queries.jsonl>processes newline-delimited JSON batch requests ({"query":"..."}or JSON string lines) and emits deterministic per-line results injson|table|markdownformat.
- Calls
advise chat-doctor [--format table|json]andadvise chat-settings [get|update|clear]- Use the hosted
/api/v1/chat/doctorand/api/v1/chat/settingscontracts. Running barechat-settingsdisplays the effective settings. - Inference-provider and model readiness are server-owned. The CLI does not require local OpenAI/Ollama keys before calling these endpoints.
- Chat uses
STELLAOPS_ADVISORYAI_URLwhen present and otherwise falls back to the backend URL. With Authority configured, the typed client requests the canonicaladvisory-ai:operatescope and remains behind the CLI egress guard. - HTTP 503/transport failures and malformed responses fail non-zero.
--verboseidentifies both the outgoing request and the returned response/intent without fabricating an answer.
- Use the hosted
advise export [--conversation-id <id>] [--tenant <tenant>] [--user <user>] [--limit <n>] [--format <json|table|markdown>] [--output <file>]- Exports advisory conversation history through the existing AdvisoryAI conversation endpoints (
/v1/advisory-ai/conversations). - When no
--conversation-idis provided, the CLI lists conversations for the scope and fetches each conversation deterministically byconversationIdbefore rendering.
- Exports advisory conversation history through the existing AdvisoryAI conversation endpoints (
2.12 Decision evidence (new)
Reconciliation (2026-05-29). The real
decisiongroup manages VEX decision documents, not the bench-harness evidence workflow sketched below. Authoritative subcommands (CommandFactory.csline ~6592):
decision export --tenant <id> [--scan-id <id>] [--vuln-id <id>...] [--purl <purl>...]— export VEX decisions as OpenVEX documents with optional DSSE signing.decision verify <file> [--digest sha256:...]— verify the DSSE signature and optional Rekor inclusion proof of a VEX decision document.decision compare <a> <b>— compare two VEX decision documents and show differences.There is no
--cve/--product/--output-dir/--syncbenchmark workflow, no--from bench/--remote, and thedecision comparecommand does not run Trivy/Syft/Grype/Snyk/Xray benchmarks. The exit codes do not derive from averify.sh. The 2025Q4 sketch below is retained for historical intent only and does not reflect the implementation.
Original 2025Q4 sketch (does not match implementation):
decision export- Parameters:
--cve,--product <purl or digest>,--scan-id <optional>,--output-dir. - Pulls
decision.openvex.json,decision.dsse.json,rekor.txt, and evidence metadata from Policy Engine and writes them into thebench/findings/<CVE>/layout defined in docs/benchmarks/vex-evidence-playbook.md. - When
--syncis set, uploads the bundle to Git (bench repo) with deterministic commit messages.
- Parameters:
decision verify- Offline verifier that wraps
tools/verify.sh/verify.pyfrom the bench repo. Checks DSSE signature, optional Rekor inclusion, and recomputes digests for reachability/SBOM artifacts. - Supports
--from bench(local path) and--remote(fetch via API). Exit codes align withverify.sh(0 success, 3 signature failure, 18 truncated evidence).
- Offline verifier that wraps
decision compare- Executes the benchmark harness against baseline scanners (Trivy/Syft/Grype/Snyk/Xray), capturing false-positive reduction, mean-time-to-decision, and reproducibility metrics into
results/summary.csv. - Flags regressions when Stella Ops produces more false positives or slower MTTD than the configured target.
- Executes the benchmark harness against baseline scanners (Trivy/Syft/Grype/Snyk/Xray), capturing false-positive reduction, mean-time-to-decision, and reproducibility metrics into
These verbs use scopes drawn from the canonical set in StellaOps.Auth.Abstractions/StellaOpsScopes.cs — e.g. findings:read (not policy.findings:read), signer:read/signer:sign (there is no signer.verify), and attest:read for Rekor/attestation lookups (there is no attestor.read). They honour sealed-mode rules by falling back to offline verification only when Rekor/Signer endpoints are unreachable.
2.13 Air-gap guard
- CLI outbound HTTP flows (Authority auth, backend APIs, advisory downloads) route through
StellaOps.AirGap.Policy. When sealed mode is active the CLI refuses commands that would require external egress and surfaces the sharedAIRGAP_EGRESS_BLOCKEDremediation guidance instead of attempting the request.
2.14 Unknowns export artifacts
unknowns export [--band <hot|warm|cold|all>] [--format <json|csv|ndjson>] [--schema-version <value>] [--output <path>]jsonnow emits a deterministic export envelope withschemaVersion,exportedAt,itemCount, and sorteditems.csvprepends a schema metadata comment (schema_version,exported_at,item_count) before the column header.ndjsonemits a metadata header line followed by schema-scoped item lines.- The formal contract artifact for the JSON envelope is at
src/Cli/StellaOps.Cli/Commands/Schemas/unknowns-export.schema.json.
2.15 Real top-level command groups not enumerated above (completeness)
Added 2026-05-29 (counts trued up 2026-07-12). Sections 2.1–2.14 document a curated subset. The live root tree in
CommandFactory.Createregisters ~108 top-level command groups. Beyond those above, the following real groups exist (names taken fromCommandFactory.csand the*CommandGroupclasses). This list is for orientation; see each group’sBuild*Commandfor verbs/flags.
Scanning / SBOM / artifacts:
scanner,scan,image,sbom,sbomer,artifact,layer-sbom,binary,symbols,ruby,php,python,bun(per-ecosystem analyzer outputs).Advisory / VEX / findings:
sources,aoc,vuln,vex,obs(VEX observations),advisory,decision,findings,feedser(federation), advisory-AI/knowledge search,advise.Policy / gating / risk:
policy,gate,verdict,score,risk,budget(risk budget),exceptions,explain,unknowns,reachability,graph.Attestation / evidence / proofs:
attest,attestor(transparency),bundle,chain,proof,prove,replay,timeline,delta,deltasig,witness,evidence,forensic,timestamp,change-trace,function-map,golden/verify-fix,groundtruth,fixchain,drift,seal,promotion,detscore,slice.Timestamping status (verified 2026-07-18): the standalone
timestamp rfc3161command is a deterministic synthetic JSON helper, not a TSA network client.attest sign --timestampcurrently fails before producing a token, whileattest verify --require-timestampchecks timestamp metadata presence but does not cryptographically decode and verify an RFC-3161 token. Do not use these commands as production timestamp proof until the real client/trust path is composed.Crypto / compliance / keys:
crypto,compliance,kms,key,issuer,trust-profile, plus the DORA/NIS2/CRA/TLPT compliance groups (incident,regulatory,tlpt, and the compliance export/verify subcommands). The unregistered pluralkeys,issuer-keys, andtrust-anchorssample groups were deleted on 2026-07-29.Platform / tenancy / orchestration:
auth,tenants,admin,config,setup,system,router,orch(orchestrator),notify,qa,doctor,self,db. (topologywas removed 2026-07-03/DTC-2 — it emitted fabricated output; the unregisteredjobenginesample group was removed 2026-07-29.)Advisory database command status (verified 2026-07-18): the registered
dbgroup owns Concelier job verbsfetch|merge|export; it does not exposestatusorconnectors. The separateDbCommandGroupis unregistered, its proposed/api/v1/health/databasebackend route does not exist, and its former sample database/connector successes were removed. Use the registry-backedsources list|status|checkcommands for current source operations; do not claim the historical database-health/freshness aggregate until an owned backend contract and production registration exist.Release orchestration (added 2026-07-03, DTC-11; RC1 scope confirmed 2026-07-29):
releaseexposes operator decision verbsapprove/reject/promote/deploy/rollbackplusshow;deploymentexposes observation verbslist/show/logs/events/watch;agentexposeslist/show/token create/token list. Release creation/listing and environment lifecycle are deliberately Console/API-only for RC1: use/api/v1/release-orchestrator/releases,/api/v1/release-orchestrator/environments, or the authenticated Console setup/release pages. There is nostella release create|listorstella envgroup in RC1.Integrations / SCM / registry / CI:
scm,registry,integrations,github,ci,api,sdk,dev-portal,license,analytics,cvss,pack(Task Packs),assurance,inventory,mirror,airgap,watchlist,model,secrets.Offline vuln DB:
vuln-db— the compact offline vulnerability-database exporter. Release-critical; documented in full in §2.16 below (it is not the same thing as thevulngroup).
2.15.1 Regional DSSE verification
stella attest verify accepts an explicit --algorithm (-A) when a DSSE envelope was signed by a regional provider:
stella attest verify \
--envelope ./scan-attestation.dsse.json \
--root ./sm2-public.pem \
--algorithm SM2 \
--format json
Supported identifiers are ES256, PS256, GOST12-256, GOST12-512, and SM2. Omitting the option preserves the legacy EC/RSA key-derived verification behavior. Regional public keys must be PEM-encoded SubjectPublicKeyInfo (PUBLIC KEY) blocks. GOST verification accepts the provider’s raw s||r and DER encodings; SM2 uses the platform default user id 1234567812345678. This public-key command is verification-only: it does not import regional private keys or enable software signing.
Regional distribution builds compose additional external-holder routes at startup. The StellaOpsEnableGOST=true build registers managed GOST verification plus OpenSSL, PKCS#11, and Windows CryptoPro providers. The StellaOpsEnableSM=true build registers SM verification/software capabilities and a typed cn.sm.remote.http transport bound from StellaOps:Crypto:SmRemote (legacy alias StellaOps:Crypto:Profiles:sm-remote). Production SM configuration must use explicit key mappings and a vendor-contract-validated, non-skipped probe; software or loopback routes remain wiring evidence, not production-holder evidence.
2.16 vuln-db — compact offline vulnerability database (release-critical)
Added 2026-07-12 (audit gap: the group shipped undocumented). Registered at
src/Cli/StellaOps.Cli/Commands/CommandFactory.cs:160; implemented insrc/Cli/StellaOps.Cli/Commands/VulnDb/VulnDbCommandGroup.cs.
stella vuln-db produces the compact, offline-matchable vulnerability DB that stella sbom check consumes in air-gapped estates, and that the /release-vuln-db publishing path ships to the mirror. It is the CLI face of the offline-first posture: no network calls, deterministic artifacts, digest + DSSE over the manifest.
One exporter, not two. vuln-db export is a thin delegate of the single export implementation — CompactVulnDbExporter in StellaOps.Concelier.Exporter.VulnDbCompact (src/Concelier/__Libraries/StellaOps.Concelier.Exporter.VulnDbCompact/CompactVulnDbExporter.cs, unified under SPRINT_20260703_006 / EXPORT-E1). The CLI maps its options onto a CompactVulnDbExportRequest and keeps only packaging concerns: DSSE signing of the library-produced manifest, the latest pointer, and console output. Projection, license segregation, corpus digests, and the capability manifest are the library’s — the CLI carries no export logic of its own. The historical “CLI exporter vs plugin exporter” divergence (two implementations that could ship different DBs) is closed; ICompactVulnDbExportService has exactly one production implementation.
Verbs. export is the only verb today (the group’s own help text reserves update/status as intended-but-unbuilt — treat them as roadmap, not shipped).
stella vuln-db export options (verified against VulnDbCommandGroup.BuildExportCommand):
| Option | Default | Meaning |
|---|---|---|
--connection / -c | local compose Postgres (stellaops_platform, schema vuln) | PostgreSQL connection to the Concelier advisory store. |
--output / -o | devops/offline/vuln-db, overridable by env STELLA_VULN_DB_EXPORT_DIR | Output root; a <db-version> subdirectory is created inside it. |
--db-version | UTC timestamp | Version label = the export id = the subdirectory name. |
--sources | all ecosystems | Comma-separated canonical ecosystem allow-list (apk,deb,rpm,npm,nuget, …). |
--max-memory-mb | auto (machine/cgroup) | Caps the build’s memory envelope (page cache + batch sizing). |
--format | sqlite | sqlite (raw DB + manifest) or bundle (also emits a deterministic tar.zst). |
--include-sharealike-companion | off (BUSL core only) | Also emit the segregated CC-BY-SA share-alike companion DB beside the BUSL-core artifact. |
--with-descriptions | disabled — hard refusal | Retained only to fail loudly: the ungated prose detail DB carried verbatim advisory text from every source with no license gate. See docs/legal/decisions/compact-vulndb-license-segregation-counsel-thread-reply.md. |
--sign | off | Write a DSSE envelope over the canonical export manifest. Requires --key. |
--advisory-source | legacy | Advisory projection authority: legacy or gate-decision. The latter streams the gate-only Link-Not-Merge decision projection and is intended for parity/proof until the cutover is accepted. |
--key / -k | — | PEM-encoded ECDSA P-256 private key used with --sign. |
Artifacts and layout. The export writes <output>/<db-version>/ containing vuln-db.sqlite, the export manifest (the per-artifact sidecar manifest is authoritative when the library produces one; the aggregate manifest.json is the legacy fallback), the optional share-alike companion DB, and — with --format bundle — the deterministic tar.zst. It then repoints <output>/latest (a symlink) at <db-version> and fails the command if latest does not resolve to vuln-db.sqlite, so a half-written export can never be published as current. The console report prints the db-version, profile, advisory/affected/alias/sink counts, size, the artifact sha256, the corpus content digest, and the manifest + latest paths.
The gate-decision source reads only active decision rows with materialized PURL package/ecosystem keys. It reconstructs affected ranges through the shared EcosystemVersionFilter object/string/array parser, retains decision aliases and hashes, and attributes each record to the most restrictive license disposition among the field-winning sources. This is deliberately conservative: a mixed-source decision cannot route prose through a more permissive source than the observation that supplied it. legacy remains the default for one release while CANON-D6 parity evidence is collected.
Signing. With --sign --key <pem>, the CLI writes vuln-db.sqlite.dsse.json next to the DB: a DSSE envelope over the manifest with payload type application/vnd.stellaops.vuln-db.manifest.v1+json. Signing covers the manifest (which carries the artifact digests), not the SQLite bytes directly — verify the digest chain, not just the signature.
Exit codes. 0 success; 1 for every failure mode (invalid --format, --sign without --key, the --with-descriptions refusal, export failure, or a latest-pointer update failure). This group predates the richer CliExitCodes scheme in §4.3 and does not use it.
3) AuthN: Authority OAuth2 bearer tokens
3.1 Token acquisition
- Pre-issued bearer / PAT:
API_KEY(orStellaOps:ApiKey) is attached directly as a Bearer token to the Release Orchestrator and Policy Gateway named clients together with the resolved tenant header. This mode does not require an Authority URL; it is the supported fallback when OIDC discovery is unavailable. - Human interactive login (current local/dev behavior): when
STELLAOPS_AUTHORITY_CLIENT_IDis unset, the CLI defaults to the seededstellaops-clipublic client and prompts for username/password if they are not preconfigured. That login uses the current password-grant bootstrap path. - Client-credentials: service principals use a confidential client such as the seeded local/dev
stellaops-cli-automation, or a deployment-specific confidential client with private_key_jwt,client_secret, or mTLS. - Device-code/PKCE: documented as the intended long-term human posture, but (roadmap — not implemented) in the current CLI token client.
StellaOps.Auth.Client.StellaOpsTokenClientonly implements the OAuth2passwordandclient_credentialsgrants. - Token endpoint: resolved from the Authority OIDC discovery document (
token_endpoint), not hard-coded. Against the standard local/dev gateway this resolves to/connect/tokenat the gateway root. Discovery + JWKS are cached (StellaOpsDiscoveryCache) with an offline-tolerant fallback.
3.1.1 Bootstrap client inventory (current local/dev standard profile)
- Authority seeds first-party CLI clients from
etc/authority/plugins/standard.yaml. stellaops-cliis the default human client ID when no explicit Authority client ID is configured.stellaops-cli-automationis the seeded confidential automation client for non-interactive local/dev flows.- Production deployments should override local secrets and may disable the password grant entirely once device-code or browser-mediated PKCE is enabled.
- Startup Authority and crypto diagnostics are emitted only for verbose human-readable invocations; structured output commands stay clean even when optional crypto providers are unavailable.
3.2 Credential & token storage
DPoP is roadmap — not implemented. The current CLI does not generate a DPoP JWK or attach DPoP proofs. Backend calls carry a plain OAuth2 Bearer access token. The 2025Q4 DPoP design below is retained as forward-looking intent only.
- After login the CLI caches the access token (and, when configured, refresh material) and protects the on-disk secrets blob with the host OS secret store via
SecretProtectorFactory: Windows → DPAPI (WindowsDpapiSecretProtector), macOS → Keychain (MacOsKeychainSecretProtector), Linux → libsecret/GNOME-Keyring when available else a machine-id-derived fallback (MachineIdFallbackSecretProtector; setSTELLAOPS_REQUIRE_OS_SECRET_STORE=trueto refuse the fallback). KWallet is not a supported backend. stella auth login --no-save(orSTELLAOPS_LOGIN_NO_SAVE=1) keeps tokens in process memory only (InMemorySecretProtector) for CI/kiosk runs.- The CLI refreshes/re-requests tokens as needed; no DPoP proof or nonce dance is involved.
(Roadmap) DPoP sender-constrained tokens with an Ed25519 keypair held in the OS keychain.
3.3 Multi‑audience & scopes
CLI requests audiences as needed per verb:
scannerAPI — scopesscanner:read,scanner:scan,scanner:export(seeStellaOps.Auth.Abstractions/StellaOpsScopes.cs).signer(indirect; usually backend calls Signer) — scopessigner:read,signer:sign. There is nosigner.verifyscope.attestorfor attestation reads/creates — scopesattest:read,attest:create. The 2025Q4 namesattestor.verify/attestor.readdo not exist.concelier/excititorfor advisory/VEX admin verbs — e.g.concelier.jobs.trigger,vex:read,vex:ingest,vex.admin.
CLI rejects verbs if required scopes are missing.
4) Process model & reliability
4.1 HTTP client
- Single http2 client with connection pooling, DNS pinning, retry/backoff (idempotent GET/POST marked safe).
- Bearer-token auth via the shared
StellaOps.Auth.Clientmessage handler; tokens are refreshed/re-requested on expiry. (DPoP nonce handling is roadmap — not implemented.) - Current command-path clients are created through
CliHttpClients/IHttpClientFactory; when a full host factory is absent,CliHttpClientsfalls back to a sharedSocketsHttpHandlerinstead of per-invocation rawHttpClientconstruction.CliHttpClientsTests.Command_sources_do_not_construct_raw_http_clientsaudits command sources for regressions.
4.2 Streaming
- Long-running
scanflows surface progress events;--jsonemits machine-readable lines. --jsonprints machine events; human mode shows compact spinners and crucial updates only.
4.3 Exit codes (CI‑safe)
| Code | Meaning |
|---|---|
| 0 | Success |
| 2 | Policy fail (final report verdict=fail) |
| 3 | Verification failed (attestation/signature) |
| 4 | Auth error (invalid/missing token) |
| 5 | Resource not found (image/SBOM) |
| 6 | Rate limited / quota exceeded |
| 7 | Backend unavailable (retryable) |
| 9 | Invalid arguments |
| 11–17 | Aggregation-only guard violation (ERR_AOC_00x) |
| 18 | Verification truncated (increase --limit) |
| 70 | Transport/authentication failure |
| 71 | CLI usage error (missing tenant, invalid cursor) |
Reconciliation (2026-05-30). The codes
11–17,18,70, and71above are the aggregation-only / guard family and are real (Environment.ExitCode = 70/71,>= 11 and <= 17 => "violations",18 => "truncated"inCommandHandlers.cs). However, this is not a single repo-wide scheme. The sign / verify / doctor command families use a separate set of constants inCommands/CliExitCodes.cs:0Success,1InputFileNotFound,2MissingRequiredOption,3ServiceNotConfigured,4SigningFailed,5VerificationFailed,6PolicyViolation,7FileNotFound,8GeneralError,9NotImplemented,99UnexpectedError, and100/101for Doctor failed/warning. The two tables disagree on the meaning of several low codes (e.g.2,3,4,5); always read the specific command’s handler when scripting exact exit-code assertions. The crypto-profile-select family adds yet another local scheme (10–14, see §23).
Parser-boundary update (2026-07-12). System.CommandLine-rejected invocations now always exit
2: missing required arguments/options and malformed or unknown arguments. The parser still renders its diagnostic and usage text. Help/version remain0, and valid commands retain their family-specific handler exit codes; the aggregation-only handler code71above is not the process-level parser code.
5) Configuration model
Precedence: CLI flags → env vars → config file → defaults.
Config file: ${XDG_CONFIG_HOME}/stellaops/config.yaml (Windows: %APPDATA%\StellaOps\config.yaml)
cli:
authority: "https://authority.internal"
backend:
scanner: "https://scanner-web.internal"
attestor: "https://attestor.internal"
concelier: "https://concelier-web.internal"
excititor: "https://excititor-web.internal"
auth:
audienceDefault: "scanner"
deviceCode: true
output:
json: false
color: auto
tls:
caBundle: "/etc/ssl/certs/ca-bundle.crt"
offline:
kitMirror: "s3://mirror/stellaops-kit"
Environment variables: STELLAOPS_AUTHORITY, STELLAOPS_SCANNER_URL, etc.
6) Buildx generator orchestration — roadmap, not implemented
No
buildxcommand exists in the current CLI (see §2.2). This section describes the 2025Q4 intended behavior only.
buildx installlocates the Docker root directory, writes the generator plugin manifest, and pullsstellaops/sbom-indexerimage (pinned digest).buildx buildwrapper injects:--attest=type=sbom,generator=stellaops/sbom-indexer--label org.stellaops.request=sbom
Post‑build: CLI optionally calls Scanner.WebService to verify referrers, compose image SBOMs, and attest via Signer/Attestor.
Detection: If Buildx or generator unavailable, CLI falls back to post‑build scan with a warning.
7) Artifact handling
- Downloads (
sbom export, evidence/bundle exports): stream to file; compute sha256 on the fly; write a sidecar.sha256and optional verification bundle. (report finalis not a real command — see §2.3.) - Uploads (
offline import,sbom upload,scan upload): chunked upload; retry on transient errors; progress shown unless--json.
8) Security posture
- Token/secret storage: the OAuth2 access token (and client secrets) are protected at rest by the host OS secret store (DPAPI / Keychain / libsecret) via
SecretProtectorFactory;--no-savekeeps them memory-only. (No DPoP private key is stored — DPoP is roadmap.) - No plaintext tokens on disk; short‑lived OpToks held in the OS-protected blob / memory.
- TLS: verify backend certificates; allow custom CA bundle for on‑prem.
- Redaction: CLI logs redact
Authorizationheaders and secret material. - Supply chain: CLI distribution binaries are intended to be cosign‑signed; self-verification is exposed under the
self updategroup rather than aversion --verifyflag.
9) Observability
--verboseadds request IDs, timings, and retry traces.- Metrics (optional, disabled by default): Prometheus text file exporter for local monitoring in long‑running agents.
- Structured logs (
--json): per‑event JSON lines withts,verb,status,latencyMs.
10) Performance targets
Aspirational. These targets assume the Native-AOT single-file build of §1, which is not yet enabled. On the current JIT
net10.0build, startup is dominated by JIT/host init and will not meet the 20 ms figure.
- Startup ≤ 20 ms (AOT).
scan imagerequest/response overhead ≤ 5 ms (excluding server work).- Buildx wrapper overhead negligible (<1 ms).
- Large artifact download (100 MB) sustained ≥ 80 MB/s on local networks.
11) Tests & golden outputs
- Unit tests: argument parsing, config precedence, URL resolution, token cache/secret-protector behavior. (No DPoP proof-creation tests exist — DPoP is unimplemented.)
- Integration tests (Testcontainers): mock Authority/Scanner/Attestor; CI pipeline with fake registry.
- Golden outputs: verb snapshots for
--jsonacross OSes; kept intests/golden/…. - Contract tests: ensure API shapes match service OpenAPI; fail build if incompatible.
12) Error envelopes (human + JSON)
Human:
✖ Policy FAIL: 3 high, 1 critical (VEX suppressed 12)
- pkg:rpm/openssl (CVE-2025-12345) — affected (vendor) — fixed in 3.0.14
- pkg:npm/lodash (GHSA-xxxx) — affected — no fix
See: https://ui.internal/scans/sha256:...
Exit code: 2
JSON (--json):
{ "event":"report", "status":"fail", "critical":1, "high":3, "url":"https://ui..." }
13) Admin & advanced flags
Reconciliation. The only flags registered as global options on the root command are
--verbose/-vand--tenant/-t(CommandFactory.Create). The URL/TLS/trace flags below are 2025Q4 design intent and are not wired as global options today; URLs are configured via env vars / config file (see §5) and per-command options. Treat this list as aspirational.
--authority,--scanner,--attestor,--concelier,--excititoroverride config URLs.--no-color,--quiet,--json.--timeout,--retries,--retry-backoff-ms.--ca-bundle,--insecure(dev only; prints warning).--trace(dump HTTP traces to file; scrubbed).
14) Interop with other tools
- Emits CycloneDX Protobuf directly to stdout when
export sbom --format cdx-pb --out -. - Pipes to
jq/yqcleanly in JSON mode. - Can mint service-account tokens for scripts via
stella auth token mint --service-account <id> --scope <scope> [--raw](the--rawflag prints only the token value). (There is noauth token --audform.)
15) Packaging & distribution
- Installers: deb/rpm (postinst registers completions), Homebrew, Scoop, Winget, MSI/MSIX.
- Shell completions: bash/zsh/fish/pwsh.
- Update channel: the
self updatecommand group (stella self update ...) checks, verifies, and applies updates; corporate environments can disable. (Spelledself update, notself-update.)
16) Security hard lines
- Refuse to print token values; redact Authorization headers in verbose output.
- Enforce short token TTL; refresh/re-request as expiry approaches.
- Protect the on-disk secrets blob with the host OS secret store;
STELLAOPS_REQUIRE_OS_SECRET_STORE=truerefuses the machine-id fallback. (Device‑code machine/user binding and the--insecuredouble-opt-in gate are roadmap — not wired in the active command tree.)
17) Wire sequences
A) Scan & wait with attestation
sequenceDiagram
autonumber
participant CLI
participant Auth as Authority
participant SW as Scanner.WebService
participant SG as Signer
participant AT as Attestor
CLI->>Auth: password grant bootstrap on seeded stellaops-cli client
Auth-->>CLI: OpTok (aud=scanner)
CLI->>SW: POST /scans { imageRef, attest:true }
SW-->>CLI: { scanId }
CLI->>SW: GET /scans/{id} (poll)
SW-->>CLI: { status: completed, artifacts, rekor? } # if attested
alt attestation pending
SW->>SG: POST /sign/dsse (server-side)
SG-->>SW: DSSE
SW->>AT: POST /rekor/entries
AT-->>SW: { uuid, proof }
end
CLI->>SW: GET /sboms/?format=cdx-pb&view=usage
SW-->>CLI: bytes
B) Verify attestation by artifact
sequenceDiagram
autonumber
participant CLI
participant AT as Attestor
CLI->>AT: POST /rekor/verify { artifactSha256 }
AT-->>CLI: { ok:true, uuid, index, logURL }
18) Roadmap (CLI)
scan fs <path>(local filesystem tree) → upload to backend for analysis.policy test --sbom <file>(simulate policy results offline using local policy bundle).runtime capture(developer mode) — capture small/proc/<pid>/mapsfor troubleshooting.- Pluggable output renderers for SARIF/HTML (admin‑controlled).
19) Example CI snippets
Note: the snippets below are illustrative of the 2025Q4 intent. Some forms (
scan image <digest> --wait,export sbom --out, thebuildxverbs) are roadmap — see §2.3, §2.2. The real artifact path issbom export/sbom upload;auth loginandverify attestationare real.
GitHub Actions (post-build)
- name: Login (service principal)
env:
STELLAOPS_AUTHORITY_CLIENT_ID: stellaops-cli-automation
STELLAOPS_AUTHORITY_CLIENT_SECRET: ${{ secrets.STELLAOPS_AUTHORITY_CLIENT_SECRET }}
run: stellaops auth login --json --authority ${{ secrets.AUTHORITY_URL }}
- name: Scan
run: stellaops scan image ${{ steps.build.outputs.digest }} --wait --json
- name: Export (usage view, protobuf)
run: stellaops export sbom ${{ steps.build.outputs.digest }} --view usage --format cdx-pb --out sbom.pb
- name: Verify attestation
run: stellaops verify attestation --artifact $(sha256sum sbom.pb | cut -d' ' -f1) --json
GitLab (buildx generator) — roadmap; the buildx verbs do not exist (see §2.2/§6). For real CI templates use stella ci init --platform gitlab (§2.11).
script:
- stellaops buildx install # roadmap — not implemented
- docker buildx build --attest=type=sbom,generator=stellaops/sbom-indexer -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- stellaops scan image $CI_REGISTRY_IMAGE@$IMAGE_DIGEST --wait --json # roadmap flags
20) Test matrix (OS/arch)
- Linux: ubuntu‑20.04/22.04/24.04 (x64, arm64), alpine (musl).
- macOS: 13–15 (x64, arm64).
- Windows: 10/11, Server 2019/2022 (x64, arm64).
- Docker engines: Docker Desktop, containerd‑based runners.
21) OCI Referrers for Evidence Storage
21.1 Overview
Two new evidence sub-commands enable native OCI Referrers API integration:
| Command | Purpose |
|---|---|
stella evidence push-referrer | Push an evidence artifact as an OCI referrer attached to a subject digest |
stella evidence list-referrers | List all OCI referrers for a given artifact, with optional artifact-type filter |
21.2 Push Referrer
Options: --image (required), --artifact-type (required), --file (required), --annotation (repeatable), --offline.
Builds an OCI image manifest v2 with subject field pointing to the target digest. The evidence file becomes a single layer. Config is the OCI empty descriptor. Annotations are passed through to the manifest.
--offline mode simulates the push locally without network, producing the manifest JSON on stdout for auditing.
21.3 List Referrers
Options: --image (required), --digest (optional), --artifact-type (filter), --format (table|json), --offline.
Uses IOciRegistryClient.GetReferrersAsync() (already implemented) to query the registry’s Referrers API. --offline returns simulated data for testing.
21.4 Implementation
EvidenceReferrerCommands.cs— static command builder class following existing pattern- Wired into
EvidenceCommandGroup.BuildEvidenceCommand()alongside existing sub-commands - Reuses
IOciRegistryClientand OCI models fromStellaOps.Cli.Services - 25 unit tests in
EvidenceReferrerCommandTests.cs
22) Advisory Commitments (2026-02-26 Batch)
SPRINT_20260226_222_Cli_proof_chain_verification_and_replay_paritydelivers cryptographic verification-first command behavior forchain,bundle,sbom,timeline, andreplayflows.SPRINT_20260226_223_Platform_score_explain_contract_and_replay_alignmentoriginated the deterministic explain/history contract; the live CLI read now reaches its Signals owner through the Backend Router.SPRINT_20260226_229_DOCS_advisory_hygiene_dedup_and_archival_translationtracks advisory translation and archival state for this batch.
23) Crypto profile selection (Sprint 20260503-006)
stella crypto profiles exposes the regional compliance profiles known to the CLI (international, fips, eidas, gost, sm).
| Command | Purpose |
|---|---|
stella crypto profiles list | List available crypto compliance profiles. |
stella crypto profiles show | Show the active profile and provider capabilities. |
stella crypto profiles select <name> | Persist the active profile selection. |
stella crypto profiles select writes the selection to a real, durable store instead of merely printing a success message (the prior behaviour was a stub). Persistence follows a hybrid model that converges with the UI:
- Platform mode — when
StellaOps:BackendUrlis configured, the CLI callsPUT /api/v1/admin/crypto-providers/preferences(the same endpoint the console’s crypto-provider panel uses). The platform service emits theplatform.update_crypto_preferenceaudit event automatically. - Local mode — when no backend URL is set (offline / air-gap bootstrap), the CLI writes
~/.stellaops/appsettings.crypto.yamlwithStellaOps:Crypto:Registry:ActiveProfile. The file is consumed byStellaOps.Configuration.StellaOpsCryptoOptionson the next CLI invocation.
In either mode the CLI also appends a structured audit-journal entry to ~/.stellaops/audit/crypto-profile-select.jsonl so the operator change is observable locally even when the platform sink is unavailable.
Distinct exit codes for failure modes (Sprint 20260503-006, B-CLICRYPTO-002):
| Exit code | Condition | Operator hint |
|---|---|---|
0 | Persistence succeeded. | — |
10 | Local YAML file is read-only or permission denied. | Remove the read-only attribute or run with sufficient permissions. |
11 | Platform admin endpoint unreachable / timed out. | Verify the platform service, or unset BackendUrl to fall back to local YAML. |
12 | Platform returned 401/403 (unauthenticated/unauthorized). | Run stella login and retry. |
13 | Unknown profile name supplied. | Use stella crypto profiles list to see available options. |
14 | Generic persistence error. | See the inline error message. |
Implementation: Commands/CryptoCommandGroup.cs::HandleProfilesSelectAsync, Services/CryptoProfilePreferenceStore.cs, Audit/CryptoProfileAuditEvent.cs. Tests: __Tests/StellaOps.Cli.Tests/Commands/CryptoCommandPersistenceTests.cs.
24) Crypto and compliance setup defaults (Sprint 20260506-001)
The setup-wizard Cryptography step is mirrored by thin CLI commands that write the same Platform env-settings keys as the UI. These commands require a configured Platform backend; unlike stella crypto profiles select, they do not write a local YAML fallback because the settings are installation-scoped service configuration.
| Command | Key written or read |
|---|---|
| `stella crypto provider set <international | eidas |
stella crypto provider show | Crypto:Region |
stella crypto algorithm set --provider <provider> --signing <algorithm> | Crypto:<provider>:Algorithm |
stella crypto fips enable | Crypto:FipsMode=true after confirming Crypto:Region=international |
stella crypto fips disable | Crypto:FipsMode=false |
stella crypto status | Crypto:* plus NIS2/DORA/CRA enabled flags |
| `stella compliance nis2 enable | disable` |
| `stella compliance dora enable | disable` |
| `stella compliance cra enable | disable` |
stella compliance status | NIS2/DORA/CRA enabled flags |
HTTP implementation:
GET /platform/envsettings/db/reads the current installation-scoped DB settings.PUT /platform/envsettings/db/{key}writes one canonical key at a time withUpdatedBy=stella-cli.401and403return exit code12with astella loginhint; other platform write/read failures return exit code14.
Implementation: Commands/CryptoCommandGroup.cs, Commands/ComplianceCommandGroup.cs, and Services/PlatformEnvironmentSettingsClient.cs. Tests: __Tests/StellaOps.Cli.Tests/Commands/CryptoComplianceEnvironmentSettingsCommandTests.cs.
25) Tenant crypto and compliance overrides (Sprint 20260507-001)
Tenant-scoped commands mirror the /setup/system/cryptography Configuration page. They are thin clients over Platform’s tenant compliance profile and compliance override endpoints, not local YAML writes and not installation defaults.
| Command | Platform state |
|---|---|
| `stella crypto profile set --tenant | fips |
stella crypto profile show --tenant <id> | active tenant profile row |
| `stella compliance nis2 enable | disable --tenant |
| `stella compliance dora enable | disable --tenant |
| `stella compliance cra enable | disable --tenant |
stella compliance status --tenant <id> | merged effective tenant pack state and source |
stella compliance ... without --tenant keeps the Sprint 20260506 behavior: it reads or writes installation defaults through /platform/envsettings/db. Adding --tenant switches the command path to /api/v1/admin/crypto-providers/compliance-overrides. stella crypto profile uses /api/v1/admin/crypto-providers/compliance-profile and sends the tenant through the normal X-Tenant-Id header resolution path.
Authorization failures return exit code 12 with the existing stella login hint; validation failures such as unknown profile names return non-zero before issuing a Platform request. Implementation: Commands/CryptoCommandGroup.cs, Commands/ComplianceCommandGroup.cs, Services/TenantCryptoComplianceClient.cs, and Services/Models/TenantCryptoComplianceModels.cs. Tests: __Tests/StellaOps.Cli.Tests/Commands/CryptoComplianceEnvironmentSettingsCommandTests.cs.
