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 in src/Cli/StellaOps.Cli/Commands/CommandFactory.cs (CommandFactory.Create, the root wired from Program.cs); the legacy CliApplication.cs parallel root (never wired into Program.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: ~100 root.Add(Build*/…CommandGroup.Build*) registrations in CommandFactory.Create), plus the non-conflicting excititor, runtime, and offline kit additions 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 CommandFactory tree, including hidden cli-routes.json aliases and all eight manifest-backed restart-time plug-ins packaged by devops/docker/Dockerfile.cli. Examples the parser rejects are explicitly classified as unshipped or stale in unshipped-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:

Boundaries.


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.Core project; verb plumbing, config, HTTP, and auth wiring live inside StellaOps.Cli itself (Commands/, Services/, Configuration/). The single packaging/ 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

  1. At startup, CommandFactory.RegisterDeprecatedAliases() loads cli-routes.json (embedded resource)
  2. For each deprecated route, creates a hidden alias command that:
    • Delegates to the canonical command
    • Shows a deprecation warning on stderr (once per session)
  3. 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

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 --json is set (CI mode). Human output is concise, deterministic.

2.1 Auth & profile

2.2 Build‑time SBOM (Buildx) — roadmap, not implemented

There is no buildx command group in CommandFactory.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 through scan run, scan image, sbom and scanner command groups. The intended surface was:

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] and scan image layers --ref <ref> — image metadata/layer inspection (currently lightweight; scan image is not the rich <ref|digest> --view/--format/--attest verb sketched below).
  • scan diffbinary diff of two artifacts (BinaryDiffCommandGroup), not diff 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, and scan submit-list are the other real scan subcommands (all added in BuildScanCommand/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 export to download).
  • There is no report finalcommand. PASS/FAIL release reporting is delivered through the gate, verdict, verify release, and determinism report groups.

Original 2025Q4 sketch (retained for intent; flag names not all implemented):

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

Baseline Selection Strategies

All compare commands support --baseline-strategy for automatic baseline resolution:

StrategyDescriptionRequirements
explicit (default)Uses the digest provided via --base--base required
last-greenSelects most recent passing snapshot--artifact required
previous-releaseSelects previous release tag from registry metadata--artifact required

Options:

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:

Service Interface:

public interface IBaselineResolver
{
    Task<BaselineResolutionResult> ResolveAsync(BaselineResolutionRequest request, CancellationToken ct);
    Task<IReadOnlyList<BaselineSuggestion>> GetSuggestionsAsync(string artifactId, CancellationToken ct);
}

2.4 Policy & data

2.4.0 Advisory sources & VEX providers (Sprint 20260503-013)

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 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

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

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

2.5.1 Attestor transparency controls

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:

2.6 Runtime (Zastava helper)

2.7 Offline kit

Correction (2026-05-31): the core OfflineCommandGroup is always present and registered in CommandFactory.Create with flat offline import and offline status. When the NonCore plug-in is loaded, it extends that existing offline command with a kit sub-level (kit pull, kit import, and kit status) instead of registering a second root offline command.

Historical correction (2026-05-30, superseded by 2026-05-31 implementation note above): earlier builds had two offline registration paths depending on plug-in load state:

  • The core OfflineCommandGroup (always present, registered in CommandFactory.Create) exposes a flat offline import and offline statusno kit sub-level at this layer.
  • The NonCore plug-in (NonCoreCliCommandModule.BuildOfflineCommand) adds an offline kit sub-level with kit pull, kit import, and kit status (offline-kit bundle workflows). So offline kit pull and the --bundle-id/--destination/--overwrite/--no-resume download path do exist when the NonCore plug-in is loaded. Mirror produce/fetch is also handled by the mirror top-level group.

2.8 Utilities

2.8.1 User locale preference commands

2.9 Aggregation-only guard helpers

2.10 Key management (file KMS support)

2.11 CI Template Generation (Sprint 015)

Generated files:

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)

2.12 Decision evidence (new)

Reconciliation (2026-05-29). The real decision group manages VEX decision documents, not the bench-harness evidence workflow sketched below. Authoritative subcommands (CommandFactory.cs line ~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/--sync benchmark workflow, no --from bench/--remote, and the decision compare command does not run Trivy/Syft/Grype/Snyk/Xray benchmarks. The exit codes do not derive from a verify.sh. The 2025Q4 sketch below is retained for historical intent only and does not reflect the implementation.

Original 2025Q4 sketch (does not match implementation):

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

2.14 Unknowns export artifacts

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.Create registers ~108 top-level command groups. Beyond those above, the following real groups exist (names taken from CommandFactory.cs and the *CommandGroup classes). This list is for orientation; see each group’s Build*Command for verbs/flags.

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 in src/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):

OptionDefaultMeaning
--connection / -clocal compose Postgres (stellaops_platform, schema vuln)PostgreSQL connection to the Concelier advisory store.
--output / -odevops/offline/vuln-db, overridable by env STELLA_VULN_DB_EXPORT_DIROutput root; a <db-version> subdirectory is created inside it.
--db-versionUTC timestampVersion label = the export id = the subdirectory name.
--sourcesall ecosystemsComma-separated canonical ecosystem allow-list (apk,deb,rpm,npm,nuget, …).
--max-memory-mbauto (machine/cgroup)Caps the build’s memory envelope (page cache + batch sizing).
--formatsqlitesqlite (raw DB + manifest) or bundle (also emits a deterministic tar.zst).
--include-sharealike-companionoff (BUSL core only)Also emit the segregated CC-BY-SA share-alike companion DB beside the BUSL-core artifact.
--with-descriptionsdisabled — hard refusalRetained 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.
--signoffWrite a DSSE envelope over the canonical export manifest. Requires --key.
--advisory-sourcelegacyAdvisory 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 / -kPEM-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

3.1.1 Bootstrap client inventory (current local/dev standard profile)

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.

(Roadmap) DPoP sender-constrained tokens with an Ed25519 keypair held in the OS keychain.

3.3 Multi‑audience & scopes

CLI rejects verbs if required scopes are missing.


4) Process model & reliability

4.1 HTTP client

4.2 Streaming

4.3 Exit codes (CI‑safe)

CodeMeaning
0Success
2Policy fail (final report verdict=fail)
3Verification failed (attestation/signature)
4Auth error (invalid/missing token)
5Resource not found (image/SBOM)
6Rate limited / quota exceeded
7Backend unavailable (retryable)
9Invalid arguments
11–17Aggregation-only guard violation (ERR_AOC_00x)
18Verification truncated (increase --limit)
70Transport/authentication failure
71CLI usage error (missing tenant, invalid cursor)

Reconciliation (2026-05-30). The codes 11–17, 18, 70, and 71 above are the aggregation-only / guard family and are real (Environment.ExitCode = 70/71, >= 11 and <= 17 => "violations", 18 => "truncated" in CommandHandlers.cs). However, this is not a single repo-wide scheme. The sign / verify / doctor command families use a separate set of constants in Commands/CliExitCodes.cs: 0 Success, 1 InputFileNotFound, 2 MissingRequiredOption, 3 ServiceNotConfigured, 4 SigningFailed, 5 VerificationFailed, 6 PolicyViolation, 7 FileNotFound, 8 GeneralError, 9 NotImplemented, 99 UnexpectedError, and 100/101 for 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 (1014, 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 remain 0, and valid commands retain their family-specific handler exit codes; the aggregation-only handler code 71 above 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 buildx command exists in the current CLI (see §2.2). This section describes the 2025Q4 intended behavior only.

Detection: If Buildx or generator unavailable, CLI falls back to post‑build scan with a warning.


7) Artifact handling


8) Security posture


9) Observability


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.0 build, startup is dominated by JIT/host init and will not meet the 20 ms figure.


11) Tests & golden outputs


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/-v and --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.


14) Interop with other tools


15) Packaging & distribution


16) Security hard lines


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)


19) Example CI snippets

Note: the snippets below are illustrative of the 2025Q4 intent. Some forms (scan image <digest> --wait, export sbom --out, the buildx verbs) are roadmap — see §2.3, §2.2. The real artifact path is sbom export / sbom upload; auth login and verify attestation are 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)

21) OCI Referrers for Evidence Storage

21.1 Overview

Two new evidence sub-commands enable native OCI Referrers API integration:

CommandPurpose
stella evidence push-referrerPush an evidence artifact as an OCI referrer attached to a subject digest
stella evidence list-referrersList 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

22) Advisory Commitments (2026-02-26 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).

CommandPurpose
stella crypto profiles listList available crypto compliance profiles.
stella crypto profiles showShow 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:

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 codeConditionOperator hint
0Persistence succeeded.
10Local YAML file is read-only or permission denied.Remove the read-only attribute or run with sufficient permissions.
11Platform admin endpoint unreachable / timed out.Verify the platform service, or unset BackendUrl to fall back to local YAML.
12Platform returned 401/403 (unauthenticated/unauthorized).Run stella login and retry.
13Unknown profile name supplied.Use stella crypto profiles list to see available options.
14Generic 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.

CommandKey written or read
`stella crypto provider set <internationaleidas
stella crypto provider showCrypto:Region
stella crypto algorithm set --provider <provider> --signing <algorithm>Crypto:<provider>:Algorithm
stella crypto fips enableCrypto:FipsMode=true after confirming Crypto:Region=international
stella crypto fips disableCrypto:FipsMode=false
stella crypto statusCrypto:* plus NIS2/DORA/CRA enabled flags
`stella compliance nis2 enabledisable`
`stella compliance dora enabledisable`
`stella compliance cra enabledisable`
stella compliance statusNIS2/DORA/CRA enabled flags

HTTP implementation:

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.

CommandPlatform state
`stella crypto profile set --tenant<worldfips
stella crypto profile show --tenant <id>active tenant profile row
`stella compliance nis2 enabledisable --tenant`
`stella compliance dora enabledisable --tenant`
`stella compliance cra enabledisable --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.