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 }