stellaops findings runtime — CLI Reference

Audience: developers and operators who ingest runtime trace observations and read the resulting runtime score, aggregates, and timeline for a finding from the command line.

The stellaops findings runtime command group binds the Findings Ledger runtime instrumentation endpoints to the CLI. Use it to push runtime trace observations into the ledger and to query the per-finding runtime score, per-function aggregates, and event timeline. For the wire-level request/response contracts, see the runtime instrumentation API reference.

Source: src/Cli/StellaOps.Cli/Commands/FindingsRuntime/.

Overview

stellaops findings runtime traces ingest --finding-id <guid> --trace-file <path>
stellaops findings runtime traces list   --finding-id <guid> [--limit N] [--sort-by hits|recent]
stellaops findings runtime score get     --finding-id <guid>
stellaops findings runtime timeline range --finding-id <guid> [--from] [--to] [--bucket-hours N]

All commands accept these shared flags:

FlagDescription
--tenantTenant context (overrides the saved profile from ~/.stellaops/profile.json).
--outputOutput format. human (default) is a human-readable table; json is the raw response.
--verboseVerbose logging.

Tenant resolution

Tenant context resolves in this order:

  1. --tenant <id> (CLI flag)
  2. Active tenant in ~/.stellaops/profile.json (set via stellaops tenants use ...)

The resolved tenant is sent as X-StellaOps-TenantId on every request.

Authentication & scopes

The client attaches the configured CLI bearer token (ApiKey from StellaOpsCliOptions) as Authorization: Bearer <token> when one is present; if no token is configured the request is sent unauthenticated and the server will reject it. The Findings.Ledger WebService gates these endpoints with two scope policies (see Program.cs and the canonical scope catalog StellaOpsScopes.cs):

Command(s)Required scope
traces list, score get, timeline rangefindings:read
traces ingestfindings:write

A token missing the required scope yields 403 Forbidden (exit code 4). A read-only token is rejected by the write policy on ingest.

Exit codes

CodeMeaning
0Success (200 OK or 202 Accepted)
2A required option resolved to an empty value (ingest only; e.g. --trace-file "")
4Server returned 4xx (auth, validation, not-found)
5Server returned 5xx
8Trace file could not be read, or a generic (non-4xx/5xx) non-2xx response

These codes are intended for CI / automation pipelines.

Notes (source: FindingsRuntimeCommandGroup.cs, FindingsRuntimeRenderer.RenderError, CliExitCodes.cs):

  • --finding-id and --trace-file are declared Required on every subcommand, so a truly absent flag is rejected by the System.CommandLine parser before a handler runs (parser exit code, conventionally 1). Code 2 (MissingRequiredOption) is only emitted by the ingest handler when --trace-file is present but blank.
  • A failure reading the trace file (missing path, I/O error, or stdin error) returns code 8 (GeneralError) — there is no dedicated file-not-found code on this path.
  • The 4/5/8 split is computed from the HTTP status by RenderError: 4xx → 4, 5xx → 5, any other non-2xx → 8.

Commands

findings runtime traces ingest

POST a runtime trace observation to the ledger. The body is read from --trace-file (a JSON document) or, if --trace-file - is passed, from stdin.

stellaops findings runtime traces ingest \
    --finding-id 00000000-0000-0000-0000-000000000001 \
    --trace-file ./trace.json \
    --tenant tenant-a

Trace file shape (matches the ingest endpoint contract):

{
  "capturedAt": "2026-04-28T12:34:56Z",
  "artifactDigest": "sha256:abc...",
  "componentPurl": "pkg:npm/lodash@4.17.21",
  "frames": [
    {"symbol": "main", "isEntryPoint": true},
    {"symbol": "lodash.merge", "isVulnerableFunction": true}
  ]
}

Optional trace fields: containerName and metadata

The trace JSON may include containerName (string) and metadata (string→string map). Both are persisted to the trace record since sprint 026 and surface on subsequent traces list output, sourced from the most recent persisted trace for the (tenant, finding). See v1 → v2 history in the API reference for the full contract and migration notes.

Successful response: 202 Accepted. Human output prints a === Trace ingested === block listing each response field (findingId, observationId, recordedAt, artifactDigest, componentPurl, frameCount, privacyFilterApplied); --output json emits the raw body.

findings runtime traces list

GET the per-function aggregates for a finding.

stellaops findings runtime traces list \
    --finding-id 00000000-0000-0000-0000-000000000001 \
    --limit 25 --sort-by recent

Renders a table with FUNCTION, HITS, DIRECT, FIRST SEEN, LAST SEEN columns plus a summary header. The summary header surfaces Total hits, Unique paths, Posture, Container count, and Last hit (each printed only when present in the response). --output json emits the full response body verbatim.

--sort-by accepts hits (default) or recent; the CLI rejects any other value at parse time (AcceptOnlyFromAmong). --limit defaults to 50 and is clamped to [1, 200] server-side.

A 404 NotFound is returned (and exit code 4) when no aggregates exist — this is the correct “no data” state, not an error condition.

findings runtime score get

GET the current runtime score with breakdown.

stellaops findings runtime score get \
    --finding-id 00000000-0000-0000-0000-000000000001

Human output renders the display score as 0..100 and includes the raw wire value in parentheses, for example 78.00/100 (wire: 0.78). --output json emits the raw response where score.score is 0..1. The command also renders the computedAt timestamp and per-component breakdown (observationScore, recencyFactor, qualityFactor).

findings runtime timeline range

GET chronological events in a [from, to] window.

stellaops findings runtime timeline range \
    --finding-id 00000000-0000-0000-0000-000000000001 \
    --from 2026-04-27T00:00:00Z \
    --to   2026-04-28T00:00:00Z \
    --bucket-hours 4

Defaults: to = now, from = to - 24h, bucket-hours = 1. The server clamps bucket-hours to [1, 168] and rejects a window where from > to with 400 Bad Request (exit code 4). Human output prints the resolved From / To / BucketHours header, then a table of TIMESTAMP, KIND, CORRELATION-ID columns; --output json emits the raw response.

Wire note: the timeline route is GET /api/v1/findings/{id}/runtime-timeline (a dash, not a runtime/timeline path segment), unlike the traces and score routes which nest under runtime/.

Sample script

#!/usr/bin/env bash
set -euo pipefail
FID="00000000-0000-0000-0000-000000000001"

# 1. Ingest a synthetic trace.
stellaops findings runtime traces ingest \
    --finding-id "$FID" --trace-file ./trace.json

# 2. Query the score.
stellaops findings runtime score get --finding-id "$FID"

# 3. List aggregates.
stellaops findings runtime traces list \
    --finding-id "$FID" --sort-by hits --limit 5

Cross-references