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