Findings Ledger — Architecture
The Findings Ledger is the append-only, content-addressed system of record for every vulnerability finding, VEX decision, and risk score the Stella Ops release control plane produces. This page is the architecture-level view of the consolidated Findings module (src/Findings/): the append-only Findings Ledger event spine, the RiskEngine scoring service, the merged VulnExplorer read/write surface, and the runtime instrumentation subsystem that landed across sprints SPRINT_20260429_011..015. It is written for engineers integrating with the ledger and for operators reasoning about its runtime, persistence, and determinism guarantees.
Companion pages. For module overview and runtime-cutover status see
README.md. For low-level algorithm semantics seeruntime-instrumentation.md. For the on-disk schema seeschema.mdandschema-catalog.md. The Console exposure/aggregate contract owned by the dedicated Findings.Security host is documented insecurity-read-model.md. The Findings.VulnCorrelation worker — owner of theanalytics.*schema and of the SBOM/attestation/advisory ingestion lanes — has its own dossier:vuln-correlation.md.
Module composition (Sprint 207 consolidation)
src/Findings/ is the unified home for all findings-related services. Four deployable hosts (three HTTP, one worker) plus their shared libraries live side by side. Each schema-owning host auto-migrates its own schema on startup (AGENTS.md §2.7): findings (Ledger), findings_security (Security), riskengine (RiskEngine), analytics (VulnCorrelation).
| Host / library | Project | Role |
|---|---|---|
| Findings Ledger core | src/Findings/StellaOps.Findings.Ledger/ | Domain types, hashing, deterministic ids, projection reducer, services (write, chain verify, snapshot, attestation pointer, alert, decision, evidence bundle, VEX consensus, air-gap, orchestrator export). |
| Findings Ledger WebService | src/Findings/StellaOps.Findings.Ledger.WebService/ | HTTP host. Also serves the merged VulnExplorer endpoints and a compatibility scoring/webhook surface. Owns the findings schema. |
| Findings Security WebService | src/Findings/StellaOps.Findings.Security.WebService/ | Tenant/scoped Console exposure read model: flat findings, facets, pivots, disposition overlays, image context, aggregate risk, and tenant-scoped freshness status. It subscribes to disposition changes and runs a periodic authoritative repair. Owns the findings_security schema through its persistence library. |
| Findings VulnCorrelation WebService | src/Findings/StellaOps.Findings.VulnCorrelation.WebService/ | Worker host (no gateway route). Runs the SBOM/attestation/advisory ingestion + correlation BackgroundServices and owns the analytics.* schema. Relocated out of platform-web by PAC-5. → vuln-correlation.md. |
| RiskEngine Core | src/Findings/StellaOps.RiskEngine.Core/ | Score providers (CVSS/KEV, EPSS, combined, VEX gate, fix-exposure, fix-chain), exploit-maturity service, in-process score queue + worker. |
| RiskEngine WebService | src/Findings/StellaOps.RiskEngine.WebService/ | HTTP host for risk-score jobs/simulations and exploit-maturity assessment. |
| RiskEngine Worker | src/Findings/StellaOps.RiskEngine.Worker/ | Background snapshot exporter: ExecuteAsync exports on startup and on a PeriodicTimer (Worker:ExportIntervalSeconds, default 3600 s), reading Concelier’s vuln.advisory_cvss / vuln.kev_flags and writing cvss-snapshot.gz + kev-snapshot.gz to Worker:BundleOutputDirectory (Worker.cs:43-106). It does not process score jobs — those run in-line in the WebService. |
| RiskEngine Infrastructure | src/Findings/__Libraries/StellaOps.RiskEngine.Infrastructure/ | riskengine schema stores (PostgresRiskScoreResultStore, in-memory test store) and migration 001_initial_schema.sql. |
| VulnCorrelation persistence | src/Findings/__Libraries/StellaOps.Findings.VulnCorrelation.Persistence/ | analytics schema ownership: AddVulnCorrelationMigrations + the embedded 001_v1_analytics_baseline.sql. |
| Findings disposition contracts | src/Findings/__Libraries/StellaOps.Findings.Disposition.Contracts/ | Domain-agnostic FindingDisposition contract shared by the Security read model and the VulnCorrelation ingestion lane. |
| Runtime processing library | src/Findings/__Libraries/StellaOps.Findings.Ledger.Runtime/ | Pure-function privacy redactor, trace aggregator, score calculator. |
| DORA/ROI taxonomy | src/Findings/__Libraries/StellaOps.Findings.DoraRoi/ | DORA-ROI taxonomy validation, signing contract, template encoder. |
The Findings Security host also owns the bounded query-specific GET /api/v2/security/findings/cve-exposure read. It computes exact tenant/CVE and optional region/environment/artifact counts inside PostgreSQL over the owned materialized projection, then returns at most 50 deterministic reachable finding rows. Its verdict is exposed, not_exposed, or unknown; only an all-not-present exact match set proves not_exposed. The projection does not store digest, service, or owner identity, so the endpoint accepts no digest query and reports service/owner as not_recorded rather than inferring them. See security-read-model.mdfor the full contract and successor boundary.
Findings Ledger core surface
The ledger is the append-only event spine. The WebService exposes (non-exhaustive):
- Ledger writes/exports:
POST /vuln/ledger/events(scopeledger.events.write),GET /ledger/export/{findings,vex,advisories,sboms}andGET /v1/ledger/attestations(scopeledger.export.read). - Chain integrity / snapshots / time-travel:
MapLedgerChainVerifyEndpoints,POST|GET|DELETE /v1/ledger/snapshots[/{id}],GET /v1/ledger/time-travel/{findings,vex,advisories},POST /v1/ledger/replay,POST /v1/ledger/diff,GET /v1/ledger/changelog/{entityType}/{entityId},GET /v1/ledger/staleness,GET /v1/ledger/current-point. - Attestation pointers:
POST|GET /v1/ledger/attestation-pointers[...], per-finding pointer/summary reads,POST /v1/ledger/attestation-pointers/search,PUT /v1/ledger/attestation-pointers/{id}/verification. - Air-gap / orchestrator provenance:
POST /internal/ledger/orchestrator-export,GET /internal/ledger/orchestrator-export/{artifactHash},POST /internal/ledger/airgap-import. - Alerts / decisions / evidence:
GET /v1/alerts[...],POST /v1/alerts/{alertId}/decisions,GET /v1/alerts/{alertId}/{audit,bundle}, plus VEX consensus (POST /v1/vex-consensus/compute[-batch], projection + issuer reads). - Finding summary, evidence graph, reachability map, backport, security-vulnerability, scoring, webhook endpoint groups (mapped at the tail of
Program.cs). - Compatibility read-model (dead-routed):
findings-ledgerstill mapsGET /api/v2/security/vulnerabilities/{identifier}over the ledger projection set, but the gateway routes^/api/v2/security(.*)tofindings-security, so the live vulnerability-detail route the Web console reaches is served byStellaOps.Findings.Security.WebService(advisory metadata fromvuln.advisory_canonical, CVSS fromvuln.advisory_cvss, joined to the tenant-scopedfindings_security.security_finding_projection; see security-read-model.md → “Per-CVE vulnerability detail”). This ledger handler is retained for its in-process tests but is not gateway-reachable; a CVE id is resolved by an exact tenant-scoped projection-identity query, newest projection wins, no match returns a disclosure-safe404.
The compatibility EvidenceSubgraphAdapter maps a Ledger EvidenceGraphResponse into the VulnExplorer tree contract without discarding evidence. It uses graph.VulnerabilityId, builds children from inbound edges starting at RootNodeId, orders IDs ordinally, handles cycles, attaches any disconnected nodes deterministically, maps Ledger node types explicitly, and emits only edges whose endpoints resolve in the returned tree. This repairs the backend adapter only. The current Web request route/identifier still does not match the Findings endpoint contract and is not claimed as an end-to-end UI flow.
Background hosted services: LedgerMerkleAnchorWorker, LedgerProjectionWorker, AssetRegistryLedgerProjectionWorker, RuntimeTraceRetentionWorker. The findings schema auto-migrates on startup via AddStartupMigrations("findings", "FindingsLedger", …).
Runtime cutover (truthful fail-closed posture). In non-
Testinghosts the WebService deliberately registersUnsupported*services for retired or unimplemented surfaces (evidence content, reachability map, backport evidence, risk-explanation store, score-history store, webhook store/delivery, VulnExplorer VEX-override attestor). Those return501 problem+jsoninstead of fabricating success. Rekor verification is disabled unlessfindings:ledger:rekor:enabled=true+ a configuredbaseUrl. SeeREADME.md“Runtime cutover status” for the full list.
RiskEngine
RiskEngine.WebService (service name riskengine, riskengine Postgres schema) computes risk scores from registered providers and assesses exploit maturity.
| Method | Path | Scope | Notes |
|---|---|---|---|
| GET | /risk-scores/providers | risk-engine:read | Lists registered provider names. |
| POST | /risk-scores/jobs | risk-engine:operate | Enqueues + runs a job in-line; returns 202 Accepted. |
| GET | /risk-scores/jobs/{jobId} | risk-engine:read | Reads a stored result (404 if absent). |
| POST | /risk-scores/simulations | risk-engine:operate | Evaluates a batch (no persistence). |
| POST | /risk-scores/simulations/summary | risk-engine:operate | Batch + aggregate (avg/min/max + top-3). |
| GET | /exploit-maturity/{cveId}[/level|/history] | risk-engine:read | Exploit-maturity assessment. |
| POST | /exploit-maturity/batch | risk-engine:operate | Batch maturity assessment. |
Providers registered in Program.cs: DefaultTransformsProvider, CvssKevProvider, EpssProvider, CvssKevEpssProvider, VexGateProvider, FixExposureProvider (plus FixChainRiskProvider in Core/Providers/FixChain/, which is not registered in the WebService provider registry). The result store is selected by the Storage:Driver config key (default postgres): postgres → PostgresRiskScoreResultStore (requires a connection string and runs the riskengine startup migration), inmemory → InMemoryRiskScoreResultStore which throws unless the host is Testing. CVSS/KEV/EPSS sources fail closed (Unsupported* → 501 risk_sources_unavailable) unless a real EPSS bundle/snapshot/directory is configured under RiskEngine:Sources:Epss:* or the caller supplies explicit inline scoring signals. risk-engine:read and risk-engine:operate are the JWT scopes (StellaOpsScopes.RiskEngineRead / RiskEngineOperate); RiskEnginePolicies.Read/Operate are the policy names.
VulnExplorer (merged)
The standalone StellaOps.VulnExplorer.Api container has been decommissioned (SPRINT_20260408_002). Its endpoints are served by the Ledger WebService (Endpoints/VulnExplorerEndpoints.cs) under vuln:* scopes:
| Method | Path | Scope (policy) |
|---|---|---|
| GET | /v1/vulns, /v1/vulns/{id} | vuln:view (VulnExplorer.View) |
| POST / PATCH | /v1/vex-decisions[...] | vuln:operate (VulnExplorer.Operate) |
| GET | /v1/vex-decisions[...] | vuln:view |
| GET | /v1/evidence-subgraph/{vulnId} | vuln:view |
| POST / PATCH | /v1/fix-verifications[...] | vuln:operate |
| POST | /v1/audit-bundles | vuln:audit (VulnExplorer.Audit) |
vuln:investigate (VulnExplorer.Investigate) is also registered as a policy. VEX decisions persist in findings.vex_decisions (migration 010); the store is Postgres-backed with an honest empty 200 when a tenant has no decisions.
Findings.VulnCorrelation (analytics ingestion + correlation)
StellaOps.Findings.VulnCorrelation.WebService is a worker — no gateway route, no read-model endpoints, only worker health probes — that owns and auto-migrates the whole analytics.* schema (11 tables, 4 materialized views, the sp_* / compute_daily_rollups / refresh_all_views functions) and runs three hosted consumers:
| Background service | Stream | Writes |
|---|---|---|
AnalyticsIngestionService | orchestrator:events (scanner.event.report.ready, scanner.scan.completed) | analytics.artifacts, raw_sboms, components, artifact_components — SBOM read from CAS |
AttestationIngestionService | attestor:events (rekor.entry.logged, rekor.inclusion.verified) | analytics.attestations, raw_attestations, vex_overrides, provenance/SLSA fields on artifacts |
VulnerabilityCorrelationService | concelier:advisory.observation.updated:v1, concelier:advisory.linkset.updated:v1 | analytics.component_vulns (+ artifact severity counts), joining Concelier’s vuln.* tables |
It was relocated out of platform-web by SPRINT_20260703_001 PAC-5, keeping the Platform:AnalyticsIngestion config section names and the CAS stream checkpoint so ingestion resumed without a gap. Platform retains the read surface (GET /api/analytics/*, scope analytics.read) and the daily-rollup / materialized-view-refresh maintenance lane — the ownership split, the schema, the config keys, and the tenancy constraint (analytics.* has no tenant column) are all documented in vuln-correlation.md.
Runtime instrumentation
What this is for
The runtime instrumentation subsystem turns ad-hoc eBPF / syscall / APM probe observations into a per-finding evidence stream. Operators use the resulting runtime score (0–100) to triage which of N otherwise-equivalent vulnerabilities are actually exercised in production. The feature is gated by findings:ledger:runtime:enabled; operators opt in per the enable guide.
Default-value note (verify before relying on it). The toggle is read in two places with different defaults:
RuntimePersistenceExtensions.AddFindingsLedgerRuntimePersistencereads it withdefaultValue: true(Postgres repositories wired unless explicitly set tofalse), whileRuntimeCapabilitiesEndpointsreads it withdefaultValue: false. The operational intent (ADR-013, enable guide) is opt-in, but the persistence layer wires the Postgres repositories by default. Set the key explicitly in any environment where the posture matters.
Pipeline
flowchart LR
A[Agent / probe] -->|HTTP POST| B[Ingest endpoint]
B --> C[Privacy filter
RuntimeSymbolRedactor]
C --> D[Aggregator
RuntimeTraceAggregator]
D --> E[(Postgres
findings.runtime_traces
findings.runtime_trace_aggregates
findings.runtime_scores
findings.runtime_timeline_events)]
E --> F[Score calculator
RuntimeScoreCalculator]
F --> E
E --> G[Query API
/runtime/traces
/runtime/score
/runtime-timeline]
G --> H[CLI / Web console / SDK]
ASCII fallback (for environments where Mermaid does not render):
agent → ingest → privacy filter → aggregator → persistence ─┐
├→ score calculator → persistence
│
query / timeline ←─────────┘
↓
CLI / UI / SDK
Components
| Component | Lives at | Sprint |
|---|---|---|
| Domain types | src/Findings/StellaOps.Findings.Ledger/Domain/Runtime/RuntimeModels.cs | 011 |
| Persistence interfaces + Null repos | src/Findings/StellaOps.Findings.Ledger/Infrastructure/Runtime/ (Postgres impls under Infrastructure/Postgres/Runtime/) | 011 |
| SQL migrations (4 runtime tables) | src/Findings/StellaOps.Findings.Ledger/migrations/011_runtime_traces.sql, 012_runtime_scores_timeline.sql, 013_runtime_aggregates_entry_point.sql, 014_runtime_traces_container_metadata.sql, 018_runtime_traces_release_id.sql | 011+ |
| Privacy filter, aggregator, score | src/Findings/__Libraries/StellaOps.Findings.Ledger.Runtime/ (Privacy/, Aggregation/, Scoring/) | 012 |
| HTTP endpoints | src/Findings/StellaOps.Findings.Ledger.WebService/Endpoints/RuntimeTracesEndpoints.cs, RuntimeTimelineEndpoints.cs, RuntimeCapabilitiesEndpoints.cs, RuntimeObservationsEndpoint.cs | 013, 020, 025 |
| Orchestration service | src/Findings/StellaOps.Findings.Ledger.WebService/Services/ (RuntimeInstrumentationService) | 013 |
| Observability + retention | src/Findings/StellaOps.Findings.Ledger.WebService/ (RuntimeInstrumentationMetrics, RuntimeTraceRetentionWorker) | 014 |
| Documentation, ADRs, CLI, SDK | docs/modules/findings-ledger/, docs/architecture/decisions/, src/Cli/StellaOps.Cli/ | 015 |
Data model
Four Postgres tables under the findings schema:
findings.runtime_traces— raw frame stream (framesJSONB), one row per ingested trace observation, keyed by a deterministicid(UUID). Trail evidence; retained per the retention policy.release_idwas added in migration 018 for the release-bound unified observation ingest.findings.runtime_trace_aggregates— denormalized per-function aggregates, primary key(tenant_id, finding_id, artifact_digest, vulnerable_function). Carrieshit_count,first_seen,last_seen,container_count; the entry-point flag was added in migration 013.findings.runtime_scores— the most recent computed score per(tenant_id, finding_id)asNUMERIC(5,2)(0–100, CHECK-constrained), thederivation_window_s, and the per-component values stored as acomponentsJSONB object (hits,entryPoint,coverage,recency).findings.runtime_timeline_events— append-only timeline events (event_id,kind,occurred_at, optionalcorrelation_id,payloadJSONB) backing theruntime-timelinequery.
The doc previously described “three tables” and named the aggregate table
runtime_aggregates; the shipped schema has four runtime tables and the aggregate table isruntime_trace_aggregates.
See schema.mdand the SQL migrations under src/Findings/StellaOps.Findings.Ledger/migrations/ (files 011–014, 018) for column-level detail.
HTTP surface (summary)
| Method | Path | Description | Policy / JWT scope |
|---|---|---|---|
| POST | /api/v1/findings/{findingId}/runtime/traces | Ingest one runtime trace observation | findings.runtime.write → scope findings:write |
| GET | /api/v1/findings/{findingId}/runtime/traces | List per-function aggregates | findings.runtime.read → scope findings:read |
| GET | /api/v1/findings/{findingId}/runtime/score | Read the current runtime score | findings.runtime.read → scope findings:read |
| GET | /api/v1/findings/{findingId}/runtime-timeline | Chronological event timeline (note: dash, not slash) | findings.runtime.read → scope findings:read |
| GET | /api/v1/capabilities/runtime | Platform-enabled flag + tenant-scoped ingest stats | findings.runtime.read → scope findings:read |
| POST | /api/v1/runtime/observations | Unified release-bound, finding-agnostic ingest | AllowAnonymous (mTLS-authenticated upstream; no Authority scope) |
Scope clarification.
findings.runtime.read/findings.runtime.writeare the authorization-policy names, not the scope strings. InProgram.csthe read policy requires the JWT scopefindings:read(StellaOpsScopes.FindingsRead) and the write policy requiresfindings:write. A read-only token is rejected with403by the write policy.
Ingest always returns 202 Accepted. When the feature is disabled the Null repositories no-op writes (still 202) and return empty reads, which surface as 404. Empty-result reads return 404 NotFound (not 200 OK + empty array) — this is contract-tested. Clients MUST distinguish the two states.
Full details: runtime-instrumentation-api.md. OpenAPI: docs/modules/findings-ledger/openapi/.
Operational view
- Disabled by default. Operators flip
findings:ledger:runtime:enabledtotrueafter reviewing disk and ingest-rate expectations. Seeenabling-runtime-instrumentation.md. - Auto-migration. All four runtime tables are created via
AddStartupMigrations("findings", "FindingsLedger", …)per CODE_OF_CONDUCT §2.7 — no manualpsqlstep is required on upgrade. - Observability + retention. Metrics, dashboards, and the retention worker are documented in the runbook (sprint 014):
runtime-instrumentation-operations.md(sibling page). - Disabled-mode contract. When the toggle is off, ingest endpoints return
202 Accepted(no-op acknowledgement) and read endpoints return404 NotFound. SeeADR-013 Findings Runtime Disabled Mode.
Privacy posture
Per-process address tokens (@0x[hex]+ suffixes) are stripped from symbol names and file paths before any persistence path executes. The aggregator’s bucket key is the redacted symbol, so two traces of SSL_write@0xAA… and SSL_write@0xBB… collapse into a single aggregate. The 1-byte loss of fidelity is an explicit design trade-off: the addresses are per-process-image artifacts with no cross-process correlation value. Hit counts and container counts are counts, not identifiers — they cannot de-anonymize which specific pod / process / request produced a trace.
Full rationale, alternatives considered, and per-field redaction rules: ADR-015 Findings Runtime Privacy Filterand runtime-instrumentation.md§1.
Architecture decision records
| ADR | Topic |
|---|---|
ADR-014 Findings Runtime Persistence | Four-table layout (traces / trace-aggregates / scores / timeline), JSONB frames, index design |
ADR-015 Findings Runtime Privacy Filter | Redaction contract, what is / is not stripped |
ADR-016 Findings Runtime Score Formula | Four-component weighted score (0.40 hits / 0.30 entry-point / 0.20 coverage / 0.10 recency), weight rationale |
ADR-013 Findings Runtime Disabled Mode | Null-pattern repository, 202/404 disabled contract |
ADR-017 Findings Runtime Score Recompute Async-Migration | In-line recompute today; migration plan to async when the recompute-P95 alert fires |
Determinism contract
Sprint
SPRINT_20260501_043(Group J determinism sweep, builds on Sprints 040 and 041).
The Findings ledger is content-addressed and append-only. Every row identity that participates in the chain hash, the Merkle anchor, or any persisted audit state is derived from canonical content via SHA-256 — there are no Guid.NewGuid() calls on the production path inside src/Findings/StellaOps.Findings.Ledger/. Two replays of the same input produce byte-identical rows.
The single source of truth for the key shapes is LedgerDeterministicIds. Each helper is unit-tested with a stability assertion, a distinctness assertion, and (for one site) a frozen-anchor literal in LedgerDeterministicIdsTests.
| Row / event | Helper | Key tuple |
|---|---|---|
| Airgap timeline impact event id | AirgapTimelineEventId | (tenantId, chainId, sequence) |
| Evidence-snapshot-linked event id | EvidenceSnapshotEventId | (tenantId, chainId, sequence, dsseDigest) |
| Decision (status-changed) event id | DecisionEventId | (tenantId, chainId, decisionId) |
| Decision id (when caller omits) | DecisionIdFromContent | (tenantId, alertId, actorId, occurredAt, decisionStatus, replayToken) |
| Attestation pointer id | AttestationPointerId | (tenantId, findingId, attestationDigest, attestationType) |
| Attestation pointer event id | AttestationPointerEventId | (tenantId, chainId, sequence, pointerId) |
| Merkle anchor id | MerkleAnchorId | (tenantId, rootHash, sequenceStart, sequenceEnd) |
| Snapshot id | SnapshotId | (tenantId, label, sequenceNumber, timestamp) |
| Runtime trace id | RuntimeTraceId | (tenantId, findingId, capturedAt, artifactDigest, componentPurl) |
Bumping the v1 suffix in any of these keys invalidates existing rows. Pre-prod tolerates a wipe; do not change a key shape on a production ledger without a coordinated re-write.
Time provider injection
Every chain-write site that previously read the wall clock now flows through the constructor-injected TimeProvider. Tests use Microsoft.Extensions.Time.Testing.FakeTimeProvider (MIT, vendored as a package dependency) to fix the recorded time. recorded_at participates in some payload bodies, so freezing the clock is a prerequisite for the byte-identical replay invariant.
A small number of DateTimeOffset.UtcNow reads remain in the ledger project (12 STELLA0112 sites at the close of sprint 043). All of them are either purely observability (for example SnapshotService.ReplayEventsAsync’s ObservedAt metric tag) or operational policy timestamps that do not participate in the chain hash or in any persisted audit state (for example ExpireOldSnapshotsAsync’s sweep cutoff and PostgresSnapshotRepository’s updated_at columns). These will be addressed in a follow-up sprint that injects TimeProvider into the unTimeProvider’d services; they are tracked there.
Decision row contract change (Option A)
DecisionEvent.Id is now string? (nullable) and carries no default. Callers may either supply a content-derived id explicitly or leave it null and let DecisionService.RecordAsync derive one via LedgerDeterministicIds.DecisionIdFromContent(...). The WebService API path takes the latter route; the response and the audit-timeline read path use the populated id from the persisted record.
Signed VEX override anchoring
In non-Testing hosts the WebService wires RealVexOverrideAttestorAdapter, which signs VEX override decisions with a DSSE vex-override@v1 envelope (ES256; key source is PrivateKeyPem, else a stable seed-derived P-256 key from KeySeed) and stores the compact envelope in findings.vex_override_envelopes. When no key source is configured (Enabled=false, or both PEM and seed empty) the adapter degrades honestly to the UNSIGNED interim (PendingVexOverrideAttestorAdapter) rather than fabricating a signature. In Testing hosts a TestVexOverrideAttestorAdapter is used instead.
When a caller requests anchorToRekor=true, Findings calls Attestor’s transparency submission API (POST /api/v1/rekor/entries via the configured findings:ledger:VexOverrideAttestation:AttestorBaseUrl, which defaults to the internal http://attestor.stella-ops.local) instead of talking directly to public Rekor. Attestor’s primary transparency backend is local/offline and air-gap safe. The returned rekorEntryId (RekorEntryId) and rekorLogIndex (RekorLogIndex) DTO fields are compatibility names for the local transparency UUID and monotonic local log index returned by Attestor.
If the transparency submission path is unavailable, Findings fails the create request closed: the /v1/vex-decisions endpoint returns 503 problem+json with title: "local_transparency_unavailable" and a code extension carrying the underlying adapter error (local_transparency_submitter_unavailable when no submitter is configured, local_transparency_submit_failed on a non-2xx Attestor response, or local_transparency_receipt_missing when the receipt has no UUID/log index). A signed-but-unanchored override is not persisted for a request that explicitly required anchoring. The DSSE vex-override@v1 predicate type is https://stellaops.dev/predicates/vex-override@v1 (VexOverrideAttestationOptions.PredicateType); the compact envelope is stored in findings.vex_override_envelopes (migration 021), and the decision row carries the envelope digest and storage reference through signed_override, attestation_ref_id, attestation_ref_digest, and attestation_ref_storage (migration 010).
Cross-links
- API reference:
runtime-instrumentation-api.md - Algorithms (filter / aggregate / score):
runtime-instrumentation.md - Enable guide:
enabling-runtime-instrumentation.md - UI integration requirements:
runtime-instrumentation-ui-requirements.md - CLI reference:
../../cli/findings-runtime.md - Sample SDK consumer:
docs/samples/findings-runtime-sdk/
