Files
git.stella-ops.org/docs/modules/findings-ledger/openapi/findings-ledger.v1.yaml
2025-12-26 00:32:58 +02:00

1618 lines
49 KiB
YAML

openapi: 3.0.3
info:
title: StellaOps Findings Ledger API
version: 1.0.0-beta1
description: >-
Canonical, aggregation-only surface for append-only findings events, projections, and
Merkle anchoring metadata. Aligns with schema in docs/modules/findings-ledger/schema.md.
contact:
name: StellaOps API Team
url: https://stellaops.io/docs/api
email: api@stellaops.io
tags:
- name: ledger
description: Ledger event operations
- name: projections
description: Finding projections
- name: export
description: Data export endpoints
- name: attestation
description: Attestation verification
- name: metadata
description: API metadata endpoints
- name: scoring
description: Evidence-Weighted Score (EWS) operations
- name: webhooks
description: Webhook management for score notifications
servers:
- url: https://{env}.ledger.api.stellaops.local
description: Default environment-scoped host
variables:
env:
default: prod
enum: [dev, staging, prod, airgap]
- url: https://ledger.{region}.offline.bundle
description: Offline bundle host for air-gapped deployments
variables:
region:
default: local
enum: [local]
security:
- bearerAuth: []
- mTLS: []
paths:
/v1/ledger/events:
get:
summary: List ledger events
operationId: listLedgerEvents
tags: [ledger]
parameters:
- $ref: '#/components/parameters/TenantId'
- name: chainId
in: query
required: false
schema:
type: string
format: uuid
- name: sinceSequence
in: query
schema:
type: integer
minimum: 0
- name: limit
in: query
schema:
type: integer
default: 200
maximum: 1000
responses:
'200':
description: Paged ledger events in deterministic order (chainId, sequence_No asc)
headers:
Req-Cursor:
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/LedgerEventPage'
post:
summary: Append deterministic ledger event
operationId: appendLedgerEvent
tags: [ledger]
parameters:
- $ref: '#/components/parameters/TenantId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LedgerEventAppendRequest'
responses:
'201':
description: Event persisted
content:
application/json:
schema:
$ref: '#/components/schemas/LedgerEvent'
'409':
description: Hash/sequence conflict (non-deterministic input)
/v1/ledger/projections/findings:
get:
summary: Get latest projection for findings
operationId: listFindingProjections
tags: [projections]
parameters:
- $ref: '#/components/parameters/TenantId'
- name: findingId
in: query
schema:
type: string
- name: policyVersion
in: query
schema:
type: string
- name: status
in: query
schema:
type: string
- name: limit
in: query
schema:
type: integer
default: 200
maximum: 1000
responses:
'200':
description: Projection rows with cycleHash for replay validation
content:
application/json:
schema:
$ref: '#/components/schemas/FindingProjectionPage'
/v1/ledger/export/findings:
get:
summary: Export findings in deterministic order
operationId: exportFindings
tags: [export]
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/Shape'
- $ref: '#/components/parameters/SinceSequence'
- $ref: '#/components/parameters/UntilSequence'
- $ref: '#/components/parameters/SinceObservedAt'
- $ref: '#/components/parameters/UntilObservedAt'
- name: finding_status
in: query
schema: { type: string }
- name: severity
in: query
schema: { type: number }
- name: risk_profile_version
in: query
schema: { type: string }
- $ref: '#/components/parameters/PageSize'
- $ref: '#/components/parameters/PageToken'
responses:
'200':
description: Paged findings export
headers:
X-Stella-Next-Page-Token:
schema: { type: string }
X-Stella-Result-Count:
schema: { type: integer }
content:
application/json:
schema:
$ref: '#/components/schemas/FindingExportPage'
application/x-ndjson:
schema:
type: string
description: NDJSON stream of FindingExportItem
/v1/ledger/export/vex:
get:
summary: Export VEX statements
operationId: exportVex
tags: [export]
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/Shape'
- $ref: '#/components/parameters/SinceSequence'
- $ref: '#/components/parameters/UntilSequence'
- $ref: '#/components/parameters/SinceObservedAt'
- $ref: '#/components/parameters/UntilObservedAt'
- name: product_id
in: query
schema: { type: string }
- name: advisory_id
in: query
schema: { type: string }
- name: status
in: query
schema: { type: string }
- name: statement_type
in: query
schema: { type: string }
- $ref: '#/components/parameters/PageSize'
- $ref: '#/components/parameters/PageToken'
responses:
'200':
description: Paged VEX export
headers:
X-Stella-Next-Page-Token:
schema: { type: string }
X-Stella-Result-Count:
schema: { type: integer }
content:
application/json:
schema:
$ref: '#/components/schemas/VexExportPage'
application/x-ndjson:
schema:
type: string
description: NDJSON stream of VexExportItem
/v1/ledger/export/advisories:
get:
summary: Export advisories
operationId: exportAdvisories
tags: [export]
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/Shape'
- $ref: '#/components/parameters/SinceSequence'
- $ref: '#/components/parameters/UntilSequence'
- $ref: '#/components/parameters/SinceObservedAt'
- $ref: '#/components/parameters/UntilObservedAt'
- name: severity
in: query
schema: { type: string }
- name: source
in: query
schema: { type: string }
- name: cwe_id
in: query
schema: { type: string }
- name: kev
in: query
schema: { type: boolean }
- name: cvss_version
in: query
schema: { type: string }
- name: cvss_score_min
in: query
schema: { type: number }
- name: cvss_score_max
in: query
schema: { type: number }
- $ref: '#/components/parameters/PageSize'
- $ref: '#/components/parameters/PageToken'
responses:
'200':
description: Paged advisory export
headers:
X-Stella-Next-Page-Token:
schema: { type: string }
X-Stella-Result-Count:
schema: { type: integer }
content:
application/json:
schema:
$ref: '#/components/schemas/AdvisoryExportPage'
application/x-ndjson:
schema:
type: string
description: NDJSON stream of AdvisoryExportItem
/v1/ledger/export/sboms:
get:
summary: Export SBOMs
operationId: exportSboms
tags: [export]
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/Shape'
- $ref: '#/components/parameters/SinceSequence'
- $ref: '#/components/parameters/UntilSequence'
- $ref: '#/components/parameters/SinceObservedAt'
- $ref: '#/components/parameters/UntilObservedAt'
- name: subject_digest
in: query
schema: { type: string }
- name: sbom_format
in: query
schema: { type: string }
- name: component_purl
in: query
schema: { type: string }
- name: contains_native
in: query
schema: { type: boolean }
- name: slsa_build_type
in: query
schema: { type: string }
- $ref: '#/components/parameters/PageSize'
- $ref: '#/components/parameters/PageToken'
responses:
'200':
description: Paged SBOM export
headers:
X-Stella-Next-Page-Token:
schema: { type: string }
X-Stella-Result-Count:
schema: { type: integer }
content:
application/json:
schema:
$ref: '#/components/schemas/SbomExportPage'
application/x-ndjson:
schema:
type: string
description: NDJSON stream of SbomExportItem
/v1/ledger/attestations:
get:
summary: List attestation verifications
operationId: listLedgerAttestations
tags: [attestation]
parameters:
- $ref: '#/components/parameters/TenantId'
- name: artifactId
in: query
schema: { type: string }
- name: findingId
in: query
schema: { type: string }
- name: attestationId
in: query
schema: { type: string }
- name: status
in: query
schema:
type: string
enum: [verified, failed, unknown]
- name: sinceRecordedAt
in: query
schema: { type: string, format: date-time }
- name: untilRecordedAt
in: query
schema: { type: string, format: date-time }
- $ref: '#/components/parameters/AttestationLimit'
- $ref: '#/components/parameters/PageToken'
responses:
'200':
description: Paged attestation verifications
headers:
X-Stella-Next-Page-Token:
schema: { type: string }
X-Stella-Result-Count:
schema: { type: integer }
content:
application/json:
schema:
$ref: '#/components/schemas/AttestationExportPage'
application/x-ndjson:
schema:
type: string
description: NDJSON stream of AttestationExportItem
/.well-known/openapi:
get:
summary: Serve Findings Ledger OpenAPI document
operationId: getOpenApi
tags: [metadata]
responses:
'200':
description: OpenAPI YAML document
content:
application/yaml:
schema:
type: string
# Evidence-Weighted Score (EWS) Endpoints - Sprint 8200.0012.0004
/api/v1/findings/{findingId}/score:
post:
summary: Calculate evidence-weighted score for a finding
description: >-
Calculates the Evidence-Weighted Score (EWS) for a finding by aggregating
reachability, runtime signals, exploit likelihood, source trust, and mitigation
effectiveness. Returns a 0-100 score and action bucket (ActNow, ScheduleNext,
Investigate, Watchlist).
operationId: calculateFindingScore
tags: [scoring]
security:
- bearerAuth: []
parameters:
- name: findingId
in: path
required: true
description: Finding identifier in format CVE-ID@pkg:PURL. Requires scope write:scores.
schema:
type: string
pattern: "^[A-Z]+-\\d+-\\d+@pkg:.+$"
example: "CVE-2024-1234@pkg:deb/debian/curl@7.64.0-4"
requestBody:
required: false
content:
application/json:
schema:
$ref: '#/components/schemas/CalculateScoreRequest'
example:
forceRecalculate: false
includeBreakdown: true
policyVersion: null
responses:
'200':
description: Score calculated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/EvidenceWeightedScoreResponse'
example:
findingId: "CVE-2024-1234@pkg:deb/debian/curl@7.64.0-4"
score: 78
bucket: "ScheduleNext"
inputs:
rch: 0.85
rts: 0.40
bkp: 0.00
xpl: 0.70
src: 0.80
mit: 0.10
weights:
rch: 0.30
rts: 0.25
bkp: 0.15
xpl: 0.15
src: 0.10
mit: 0.10
flags: ["live-signal", "proven-path"]
explanations:
- "Static reachability: path to vulnerable sink (confidence: 85%)"
- "Runtime: 3 observations in last 24 hours"
policyDigest: "sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
calculatedAt: "2026-01-15T14:30:00Z"
cachedUntil: "2026-01-15T15:30:00Z"
'400':
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/ScoringErrorResponse'
'404':
description: Finding not found or no evidence available
content:
application/json:
schema:
$ref: '#/components/schemas/ScoringErrorResponse'
'429':
description: Rate limit exceeded (100/min)
get:
summary: Get cached evidence-weighted score for a finding
description: Returns the most recently calculated score from cache. Returns 404 if no score has been calculated. Requires scope read:scores.
operationId: getFindingScore
tags: [scoring]
security:
- bearerAuth: []
parameters:
- name: findingId
in: path
required: true
schema:
type: string
responses:
'200':
description: Cached score retrieved
content:
application/json:
schema:
$ref: '#/components/schemas/EvidenceWeightedScoreResponse'
example:
findingId: "CVE-2024-1234@pkg:deb/debian/curl@7.64.0-4"
score: 78
bucket: "ScheduleNext"
policyDigest: "sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
calculatedAt: "2026-01-15T14:30:00Z"
cachedUntil: "2026-01-15T15:30:00Z"
fromCache: true
'404':
description: No cached score found
/api/v1/findings/scores:
post:
summary: Calculate evidence-weighted scores for multiple findings
description: Batch calculation of scores for up to 100 findings. Returns summary statistics and individual results. Requires scope write:scores.
operationId: calculateFindingScoresBatch
tags: [scoring]
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CalculateScoresBatchRequest'
example:
findingIds:
- "CVE-2024-1234@pkg:deb/debian/curl@7.64.0-4"
- "CVE-2024-5678@pkg:npm/lodash@4.17.20"
forceRecalculate: false
includeBreakdown: true
responses:
'200':
description: Batch scores calculated
content:
application/json:
schema:
$ref: '#/components/schemas/CalculateScoresBatchResponse'
example:
results:
- findingId: "CVE-2024-1234@pkg:npm/lodash@4.17.20"
score: 78
bucket: "ScheduleNext"
policyDigest: "sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
calculatedAt: "2026-01-15T14:30:00Z"
summary:
total: 2
succeeded: 2
failed: 0
byBucket: { actNow: 0, scheduleNext: 1, investigate: 1, watchlist: 0 }
averageScore: 65
calculationTimeMs: 45
errors: []
policyDigest: "sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
calculatedAt: "2026-01-15T14:30:00Z"
'400':
description: Invalid request or batch too large (max 100)
content:
application/json:
schema:
$ref: '#/components/schemas/ScoringErrorResponse'
'429':
description: Rate limit exceeded (10/min)
/api/v1/findings/{findingId}/score-history:
get:
summary: Get score history for a finding
description: Returns historical score calculations with pagination. Tracks score changes, triggers, and which factors changed. Requires scope read:scores.
operationId: getFindingScoreHistory
tags: [scoring]
security:
- bearerAuth: []
parameters:
- name: findingId
in: path
required: true
schema:
type: string
- name: from
in: query
description: Start of date range (inclusive)
schema:
type: string
format: date-time
- name: to
in: query
description: End of date range (inclusive)
schema:
type: string
format: date-time
- name: limit
in: query
description: Maximum entries to return (1-100, default 50)
schema:
type: integer
default: 50
minimum: 1
maximum: 100
- name: cursor
in: query
description: Pagination cursor from previous response
schema:
type: string
responses:
'200':
description: Score history retrieved
content:
application/json:
schema:
$ref: '#/components/schemas/ScoreHistoryResponse'
example:
findingId: "CVE-2024-1234@pkg:deb/debian/curl@7.64.0-4"
history:
- score: 78
bucket: "ScheduleNext"
policyDigest: "sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
calculatedAt: "2026-01-15T14:30:00Z"
trigger: "evidence_update"
changedFactors: ["rts", "xpl"]
- score: 65
bucket: "Investigate"
policyDigest: "sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
calculatedAt: "2026-01-10T09:15:00Z"
trigger: "scheduled"
changedFactors: []
pagination:
hasMore: false
'404':
description: Finding not found
/api/v1/scoring/policy:
get:
summary: Get active scoring policy configuration
description: Returns the currently active evidence weight policy including weights, guardrails, and bucket thresholds. Requires scope read:scores.
operationId: getActiveScoringPolicy
tags: [scoring]
security:
- bearerAuth: []
responses:
'200':
description: Active policy retrieved
content:
application/json:
schema:
$ref: '#/components/schemas/ScoringPolicyResponse'
example:
version: "ews.v1.2"
digest: "sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
activeSince: "2026-01-01T00:00:00Z"
environment: "production"
weights:
rch: 0.30
rts: 0.25
bkp: 0.15
xpl: 0.15
src: 0.10
mit: 0.10
guardrails:
notAffectedCap: { enabled: true, maxScore: 15 }
runtimeFloor: { enabled: true, minScore: 60 }
speculativeCap: { enabled: true, maxScore: 45 }
buckets:
actNowMin: 90
scheduleNextMin: 70
investigateMin: 40
/api/v1/scoring/policy/{version}:
get:
summary: Get specific scoring policy version
description: Returns a specific version of the scoring policy for historical comparison or audit. Requires scope read:scores.
operationId: getScoringPolicyVersion
tags: [scoring]
security:
- bearerAuth: []
parameters:
- name: version
in: path
required: true
schema:
type: string
example: "ews.v1.2"
responses:
'200':
description: Policy version retrieved
content:
application/json:
schema:
$ref: '#/components/schemas/ScoringPolicyResponse'
example:
version: "ews.v1.2"
digest: "sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
activeSince: "2026-01-01T00:00:00Z"
environment: "production"
weights:
rch: 0.30
rts: 0.25
bkp: 0.15
xpl: 0.15
src: 0.10
mit: 0.10
guardrails:
notAffectedCap: { enabled: true, maxScore: 15 }
runtimeFloor: { enabled: true, minScore: 60 }
speculativeCap: { enabled: true, maxScore: 45 }
buckets:
actNowMin: 90
scheduleNextMin: 70
investigateMin: 40
'404':
description: Policy version not found
/api/v1/scoring/webhooks:
post:
summary: Register a webhook for score change notifications
description: >-
Registers a webhook to receive notifications when finding scores change.
Supports filtering by finding patterns, minimum score change threshold,
and bucket changes. Webhook payloads are signed with HMAC-SHA256 if a
secret is provided.
operationId: registerScoringWebhook
tags: [scoring, webhooks]
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegisterWebhookRequest'
example:
url: "https://example.com/webhook/scores"
secret: "webhook-secret-key"
findingPatterns: ["CVE-*"]
minScoreChange: 10
triggerOnBucketChange: true
responses:
'201':
description: Webhook registered
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookResponse'
example:
id: "550e8400-e29b-41d4-a716-446655440000"
url: "https://example.com/webhook/scores"
hasSecret: true
findingPatterns: ["CVE-*"]
minScoreChange: 10
triggerOnBucketChange: true
createdAt: "2026-01-15T14:30:00Z"
'400':
description: Invalid webhook URL or configuration
'429':
description: Rate limit exceeded (10/min)
get:
summary: List all registered webhooks
description: List all registered scoring webhooks. Requires scope admin:scoring.
operationId: listScoringWebhooks
tags: [scoring, webhooks]
security:
- bearerAuth: []
responses:
'200':
description: List of webhooks
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookListResponse'
example:
webhooks:
- id: "550e8400-e29b-41d4-a716-446655440000"
url: "https://example.com/webhook/scores"
hasSecret: true
findingPatterns: ["CVE-*"]
minScoreChange: 10
triggerOnBucketChange: true
createdAt: "2026-01-15T14:30:00Z"
totalCount: 1
/api/v1/scoring/webhooks/{id}:
get:
summary: Get a specific webhook by ID
description: Get details of a specific webhook. Requires scope admin:scoring.
operationId: getScoringWebhook
tags: [scoring, webhooks]
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Webhook details
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookResponse'
example:
id: "550e8400-e29b-41d4-a716-446655440000"
url: "https://example.com/webhook/scores"
hasSecret: true
findingPatterns: ["CVE-*"]
minScoreChange: 10
triggerOnBucketChange: true
createdAt: "2026-01-15T14:30:00Z"
'404':
description: Webhook not found
put:
summary: Update a webhook configuration
description: Update a webhook configuration. Requires scope admin:scoring.
operationId: updateScoringWebhook
tags: [scoring, webhooks]
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegisterWebhookRequest'
responses:
'200':
description: Webhook updated
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookResponse'
example:
id: "550e8400-e29b-41d4-a716-446655440000"
url: "https://example.com/webhook/updated"
hasSecret: true
findingPatterns: ["CVE-*", "GHSA-*"]
minScoreChange: 5
triggerOnBucketChange: true
createdAt: "2026-01-15T14:30:00Z"
'404':
description: Webhook not found
'400':
description: Invalid configuration
delete:
summary: Delete a webhook
description: Delete a webhook registration. Requires scope admin:scoring.
operationId: deleteScoringWebhook
tags: [scoring, webhooks]
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'204':
description: Webhook deleted
'404':
description: Webhook not found
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
mTLS:
type: mutualTLS
parameters:
TenantId:
name: X-Stella-Tenant
in: header
required: true
schema:
type: string
Shape:
name: shape
in: query
required: true
schema:
type: string
enum: [canonical, compact]
SinceSequence:
name: since_sequence
in: query
schema:
type: integer
minimum: 0
UntilSequence:
name: until_sequence
in: query
schema:
type: integer
minimum: 0
SinceObservedAt:
name: since_observed_at
in: query
schema:
type: string
format: date-time
UntilObservedAt:
name: until_observed_at
in: query
schema:
type: string
format: date-time
PageSize:
name: page_size
in: query
schema:
type: integer
default: 500
maximum: 5000
PageToken:
name: page_token
in: query
schema:
type: string
AttestationLimit:
name: limit
in: query
schema:
type: integer
default: 200
maximum: 1000
schemas:
# Evidence-Weighted Score (EWS) Schemas - Sprint 8200.0012.0004
CalculateScoreRequest:
type: object
description: Request to calculate score for a finding
properties:
forceRecalculate:
type: boolean
default: false
description: Force recalculation even if cached score exists
includeBreakdown:
type: boolean
default: true
description: Include detailed breakdown in response
policyVersion:
type: string
nullable: true
description: Specific policy version to use. Null = use latest
CalculateScoresBatchRequest:
type: object
required: [findingIds]
description: Request to calculate scores for multiple findings
properties:
findingIds:
type: array
maxItems: 100
items:
type: string
description: Finding IDs to calculate scores for. Max 100.
forceRecalculate:
type: boolean
default: false
includeBreakdown:
type: boolean
default: true
policyVersion:
type: string
nullable: true
EvidenceWeightedScoreResponse:
type: object
required: [findingId, score, bucket, policyDigest, calculatedAt]
description: Evidence-weighted score calculation result
properties:
findingId:
type: string
description: Finding identifier
score:
type: integer
minimum: 0
maximum: 100
description: Calculated score (0-100). Higher = more urgent.
bucket:
type: string
enum: [ActNow, ScheduleNext, Investigate, Watchlist]
description: Action bucket classification
inputs:
$ref: '#/components/schemas/EvidenceInputs'
weights:
$ref: '#/components/schemas/EvidenceWeights'
flags:
type: array
items:
type: string
enum: [live-signal, proven-path, vendor-na, speculative]
description: Active flags affecting the score
explanations:
type: array
items:
type: string
description: Human-readable explanations for each factor
caps:
$ref: '#/components/schemas/AppliedCaps'
policyDigest:
type: string
pattern: "^sha256:[a-f0-9]{64}$"
description: Policy digest used for calculation
calculatedAt:
type: string
format: date-time
description: When the score was calculated
cachedUntil:
type: string
format: date-time
description: When the cached score expires
fromCache:
type: boolean
description: Whether this result came from cache
EvidenceInputs:
type: object
description: Normalized evidence input values (0-1 scale)
properties:
rch:
type: number
format: double
minimum: 0
maximum: 1
description: Reachability
rts:
type: number
format: double
minimum: 0
maximum: 1
description: Runtime signal
bkp:
type: number
format: double
minimum: 0
maximum: 1
description: Backport availability
xpl:
type: number
format: double
minimum: 0
maximum: 1
description: Exploit likelihood
src:
type: number
format: double
minimum: 0
maximum: 1
description: Source trust
mit:
type: number
format: double
minimum: 0
maximum: 1
description: Mitigation effectiveness
EvidenceWeights:
type: object
description: Evidence weight configuration (weights must sum to 1.0)
properties:
rch:
type: number
format: double
description: Reachability weight
rts:
type: number
format: double
description: Runtime signal weight
bkp:
type: number
format: double
description: Backport weight
xpl:
type: number
format: double
description: Exploit weight
src:
type: number
format: double
description: Source trust weight
mit:
type: number
format: double
description: Mitigation weight
AppliedCaps:
type: object
description: Applied guardrail caps and floors
properties:
speculativeCap:
type: boolean
description: Speculative cap applied (no runtime evidence)
notAffectedCap:
type: boolean
description: Not-affected cap applied (VEX status)
runtimeFloor:
type: boolean
description: Runtime floor applied (observed in production)
CalculateScoresBatchResponse:
type: object
required: [results, summary, policyDigest, calculatedAt]
description: Batch score calculation result
properties:
results:
type: array
items:
$ref: '#/components/schemas/EvidenceWeightedScoreResponse'
description: Individual score results
summary:
$ref: '#/components/schemas/BatchSummary'
errors:
type: array
items:
$ref: '#/components/schemas/ScoringError'
description: Errors for failed calculations
policyDigest:
type: string
description: Policy digest used for all calculations
calculatedAt:
type: string
format: date-time
BatchSummary:
type: object
required: [total, succeeded, failed, byBucket, averageScore, calculationTimeMs]
properties:
total:
type: integer
description: Total findings processed
succeeded:
type: integer
description: Successful calculations
failed:
type: integer
description: Failed calculations
byBucket:
$ref: '#/components/schemas/BucketDistribution'
averageScore:
type: number
format: double
description: Average score across all findings
calculationTimeMs:
type: number
format: double
description: Total calculation time in milliseconds
BucketDistribution:
type: object
description: Score distribution by bucket
properties:
actNow:
type: integer
scheduleNext:
type: integer
investigate:
type: integer
watchlist:
type: integer
ScoreHistoryResponse:
type: object
required: [findingId, history, pagination]
description: Score history response
properties:
findingId:
type: string
description: Finding ID
history:
type: array
items:
$ref: '#/components/schemas/ScoreHistoryEntry'
description: History entries
pagination:
$ref: '#/components/schemas/Pagination'
ScoreHistoryEntry:
type: object
required: [score, bucket, policyDigest, calculatedAt, trigger]
description: Score history entry
properties:
score:
type: integer
minimum: 0
maximum: 100
description: Score value at this point in time
bucket:
type: string
description: Bucket at this point in time
policyDigest:
type: string
description: Policy digest used
calculatedAt:
type: string
format: date-time
description: When calculated
trigger:
type: string
enum: [evidence_update, policy_change, scheduled, manual]
description: What triggered recalculation
changedFactors:
type: array
items:
type: string
description: Which factors changed since previous calculation
Pagination:
type: object
required: [hasMore]
description: Pagination metadata
properties:
hasMore:
type: boolean
description: Whether more results are available
nextCursor:
type: string
description: Cursor for next page. Null if no more pages.
ScoringPolicyResponse:
type: object
required: [version, digest, activeSince, environment, weights, guardrails, buckets]
description: Scoring policy response
properties:
version:
type: string
description: Policy version identifier
digest:
type: string
description: Policy content digest
activeSince:
type: string
format: date-time
description: When this policy became active
environment:
type: string
description: Environment (production, staging, etc.)
weights:
$ref: '#/components/schemas/EvidenceWeights'
guardrails:
$ref: '#/components/schemas/GuardrailsConfig'
buckets:
$ref: '#/components/schemas/BucketThresholds'
GuardrailsConfig:
type: object
description: Guardrail configuration
properties:
notAffectedCap:
$ref: '#/components/schemas/Guardrail'
runtimeFloor:
$ref: '#/components/schemas/Guardrail'
speculativeCap:
$ref: '#/components/schemas/Guardrail'
Guardrail:
type: object
required: [enabled]
description: Individual guardrail settings
properties:
enabled:
type: boolean
maxScore:
type: integer
minScore:
type: integer
BucketThresholds:
type: object
required: [actNowMin, scheduleNextMin, investigateMin]
description: Bucket threshold configuration
properties:
actNowMin:
type: integer
description: Minimum score for ActNow bucket
scheduleNextMin:
type: integer
description: Minimum score for ScheduleNext bucket
investigateMin:
type: integer
description: Minimum score for Investigate bucket
RegisterWebhookRequest:
type: object
required: [url]
description: Request to register a webhook for score changes
properties:
url:
type: string
format: uri
description: Webhook URL to call on score changes
secret:
type: string
description: Optional secret for HMAC-SHA256 signature
findingPatterns:
type: array
items:
type: string
description: Finding ID patterns to watch. Empty = all findings.
minScoreChange:
type: integer
default: 5
description: Minimum score change to trigger webhook
triggerOnBucketChange:
type: boolean
default: true
description: Whether to trigger on bucket changes
WebhookResponse:
type: object
required: [id, url, hasSecret, minScoreChange, triggerOnBucketChange, createdAt]
description: Webhook registration response
properties:
id:
type: string
format: uuid
description: Webhook ID
url:
type: string
description: Webhook URL
hasSecret:
type: boolean
description: Whether secret is configured
findingPatterns:
type: array
items:
type: string
description: Finding patterns being watched
minScoreChange:
type: integer
description: Minimum score change threshold
triggerOnBucketChange:
type: boolean
description: Whether to trigger on bucket changes
createdAt:
type: string
format: date-time
description: When webhook was created
WebhookListResponse:
type: object
required: [webhooks, totalCount]
properties:
webhooks:
type: array
items:
$ref: '#/components/schemas/WebhookResponse'
totalCount:
type: integer
ScoringError:
type: object
required: [findingId, code, message]
description: Scoring error information
properties:
findingId:
type: string
description: Finding ID that failed
code:
type: string
description: Error code
message:
type: string
description: Error message
ScoringErrorResponse:
type: object
required: [code, message]
description: Scoring error response
properties:
code:
type: string
description: Error code
message:
type: string
description: Error message
details:
type: object
additionalProperties: true
description: Additional details
traceId:
type: string
description: Trace ID for debugging
# Existing Ledger Schemas
LedgerEvent:
type: object
required: [event]
properties:
event:
type: object
required: [id, type, tenant, chainId, sequence, policyVersion, occurredAt, recordedAt, payload]
properties:
id:
type: string
format: uuid
type:
type: string
description: ledger_event_type (see schema.md §2.2)
tenant:
type: string
chainId:
type: string
format: uuid
sequence:
type: integer
policyVersion:
type: string
finding:
type: object
properties:
id: { type: string }
artifactId: { type: string }
vulnId: { type: string }
actor:
type: object
properties:
id: { type: string }
type: { type: string }
occurredAt:
type: string
format: date-time
recordedAt:
type: string
format: date-time
payload:
type: object
additionalProperties: true
evidenceBundleRef:
type: string
eventHash:
type: string
previousHash:
type: string
merkleLeafHash:
type: string
LedgerEventPage:
type: object
required: [items]
properties:
items:
type: array
items:
$ref: '#/components/schemas/LedgerEvent'
nextCursor:
type: string
LedgerEventAppendRequest:
type: object
required: [id, type, tenant, chainId, sequence, policyVersion, occurredAt, payload]
properties:
id:
type: string
format: uuid
type:
type: string
tenant:
type: string
chainId:
type: string
format: uuid
sequence:
type: integer
policyVersion:
type: string
finding:
type: object
properties:
id: { type: string }
artifactId: { type: string }
vulnId: { type: string }
actor:
type: object
properties:
id: { type: string }
type: { type: string }
occurredAt:
type: string
format: date-time
payload:
type: object
additionalProperties: true
evidenceBundleRef:
type: string
previousHash:
type: string
description: Optional; validated if supplied
merkleLeafHash:
type: string
description: Optional; server recomputes to validate determinism
FindingProjection:
type: object
required: [tenantId, findingId, policyVersion, status, cycleHash]
properties:
tenantId: { type: string }
findingId: { type: string }
policyVersion: { type: string }
status: { type: string }
severity:
type: number
format: double
riskScore:
type: number
format: double
riskSeverity:
type: string
riskProfileVersion:
type: string
riskExplanationId:
type: string
format: uuid
labels:
type: object
additionalProperties: true
currentEventId:
type: string
format: uuid
cycleHash:
type: string
updatedAt:
type: string
format: date-time
FindingProjectionPage:
type: object
required: [items]
properties:
items:
type: array
items:
$ref: '#/components/schemas/FindingProjection'
nextCursor:
type: string
ExportProvenance:
type: object
properties:
policyVersion: { type: string }
cycleHash: { type: string }
ledgerEventHash: { type: string }
FindingExportItem:
type: object
properties:
eventSequence: { type: integer }
observedAt: { type: string, format: date-time }
findingId: { type: string }
policyVersion: { type: string }
status: { type: string }
severity: { type: number, format: double }
cycleHash: { type: string }
evidenceBundleRef: { type: string }
provenance:
$ref: '#/components/schemas/ExportProvenance'
labels:
type: object
additionalProperties: true
VexExportItem:
type: object
properties:
eventSequence: { type: integer }
observedAt: { type: string, format: date-time }
vexStatementId: { type: string }
productId: { type: string }
status: { type: string }
statementType: { type: string }
knownExploited: { type: boolean }
cycleHash: { type: string }
provenance:
$ref: '#/components/schemas/ExportProvenance'
AdvisoryExportItem:
type: object
properties:
eventSequence: { type: integer }
published: { type: string, format: date-time }
advisoryId: { type: string }
source: { type: string }
title: { type: string }
severity: { type: string }
cvssScore: { type: number, format: double }
cvssVector: { type: string }
kev: { type: boolean }
cycleHash: { type: string }
provenance:
$ref: '#/components/schemas/ExportProvenance'
SbomExportItem:
type: object
properties:
eventSequence: { type: integer }
createdAt: { type: string, format: date-time }
sbomId: { type: string }
subjectDigest: { type: string }
sbomFormat: { type: string }
componentsCount: { type: integer }
hasVulnerabilities: { type: boolean }
cycleHash: { type: string }
provenance:
$ref: '#/components/schemas/ExportProvenance'
FindingExportPage:
type: object
properties:
items:
type: array
items: { $ref: '#/components/schemas/FindingExportItem' }
nextPageToken: { type: string }
VexExportPage:
type: object
properties:
items:
type: array
items: { $ref: '#/components/schemas/VexExportItem' }
nextPageToken: { type: string }
AdvisoryExportPage:
type: object
properties:
items:
type: array
items: { $ref: '#/components/schemas/AdvisoryExportItem' }
nextPageToken: { type: string }
SbomExportPage:
type: object
properties:
items:
type: array
items: { $ref: '#/components/schemas/SbomExportItem' }
nextPageToken: { type: string }
AttestationExportItem:
type: object
properties:
attestationId: { type: string }
artifactId: { type: string }
findingId: { type: string }
verificationStatus: { type: string }
verificationTime: { type: string, format: date-time }
dsseDigest: { type: string }
rekorEntryId: { type: string }
evidenceBundleRef: { type: string }
ledgerEventId: { type: string, format: uuid }
recordedAt: { type: string, format: date-time }
merkleLeafHash: { type: string }
rootHash: { type: string }
AttestationExportPage:
type: object
properties:
items:
type: array
items: { $ref: '#/components/schemas/AttestationExportItem' }
nextPageToken: { type: string }