openapi: 3.0.3
info:
  title: SOAT Audit Log API
  version: 1.0.0
  description: >-
    Read-only access to the append-only audit log — one entry per mutating
    administrative or resource action, attributed to the principal that made the
    request. The action string is the permission-action that authorized the
    request; the resource SRN is the target it was authorized against.
  contact:
    name: SOAT Team
    url: https://github.com/ttoss/soat
servers:
  - url: '{baseUrl}'
    description: Base URL of your SOAT deployment (e.g. https://your-soat.com or http://localhost:5047)
    variables:
      baseUrl:
        description: The base URL of your SOAT deployment
        default: http://localhost:5047
tags:
  - name: Audit Log
    description: Query the append-only audit log
security:
  - bearerAuth: []
paths:
  /api/v1/audit-log:
    get:
      tags:
        - Audit Log
      summary: List audit entries
      description: >-
        Returns audit-log entries visible to the caller, newest first. All
        filters are optional and combine with AND. `resource_srn` is a prefix
        match (e.g. `srn:{project}:secret:` matches every secret action); every
        other filter is exact.
      operationId: listAuditEntries
      parameters:
        - name: project_id
          in: query
          description: Project ID (scopes results; required if not using project key auth for a specific project)
          schema:
            type: string
            example: proj_V1StGXR8Z5jdHi6B
        - name: action
          in: query
          description: Exact permission-action string, e.g. `secrets:DeleteSecret`
          schema:
            type: string
            example: secrets:DeleteSecret
        - name: principal_id
          in: query
          description: Public id of the principal (`user_…` or `key_…`)
          schema:
            type: string
        - name: resource_public_id
          in: query
          description: Exact target resource public id, e.g. `sec_…`
          schema:
            type: string
        - name: resource_srn
          in: query
          description: >-
            SRN prefix match, e.g. `srn:{project}:secret:`. The log is
            append-only, so a stored SRN is never rewritten; the filter matches
            it as stored.
          schema:
            type: string
        - name: from
          in: query
          description: Only entries created at or after this timestamp (ISO 8601)
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          description: Only entries created at or before this timestamp (ISO 8601)
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          description: Number of results per page (1–200, default 25)
          schema:
            type: integer
            default: 25
        - name: offset
          in: query
          description: Number of results to skip
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: A page of audit entries
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AuditEntry'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '400':
          description: '`from` or `to` is present but not a valid ISO 8601 date'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '500':
          description: Internal server error
  /api/v1/audit-log/export:
    get:
      tags:
        - Audit Log
      summary: Export audit entries as NDJSON
      description: >-
        Streams a project's audit-log entries as newline-delimited JSON — one
        entry object per line, oldest first — for archival before the retention
        window expires, or for shipping into an external system. `project_id` is
        required: the export is per-project by design. Filters behave exactly as
        they do on the list endpoint.
      operationId: exportAuditEntries
      # Not a tool, for two independent reasons. The response is a stream, which
      # has no JSON projection; and an export is deliberately unbounded — it has
      # no `limit` — so the callable form of "read the audit log" is
      # `listAuditEntries`, which is paged. Bulk export stays a REST/CLI/SDK
      # operation, where the caller controls where the bytes go.
      x-soat-mcp-exclude: true
      parameters:
        - name: project_id
          in: query
          required: true
          description: Project whose entries are exported
          schema:
            type: string
            example: proj_V1StGXR8Z5jdHi6B
        - name: action
          in: query
          description: Exact permission-action string, e.g. `secrets:DeleteSecret`
          schema:
            type: string
        - name: principal_id
          in: query
          description: Public id of the principal (`user_…` or `key_…`)
          schema:
            type: string
        - name: resource_public_id
          in: query
          description: Exact target resource public id, e.g. `sec_…`
          schema:
            type: string
        - name: resource_srn
          in: query
          description: >-
            SRN prefix match, e.g. `srn:{project}:secret:`. The log is
            append-only, so a stored SRN is never rewritten; the filter matches
            it as stored.
          schema:
            type: string
        - name: from
          in: query
          description: Only entries created at or after this timestamp (ISO 8601)
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          description: Only entries created at or before this timestamp (ISO 8601)
          schema:
            type: string
            format: date-time
      responses:
        '200':
          description: >-
            A newline-delimited stream of audit entries. Each line is a JSON
            object with the same fields as `AuditEntry`.
          content:
            application/x-ndjson:
              schema:
                type: string
        '400':
          description: >-
            `project_id` is required, or `from`/`to` is present but not a valid
            ISO 8601 date
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
  /api/v1/audit-log/{entry_id}:
    get:
      tags:
        - Audit Log
      summary: Get an audit entry
      description: Returns a single audit-log entry, including its `detail` payload
      operationId: getAuditEntry
      parameters:
        - name: entry_id
          in: path
          required: true
          description: Audit entry ID
          schema:
            type: string
            example: audit_V1StGXR8Z5jdHi6B
      responses:
        '200':
          description: Audit entry details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditEntry'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: Audit entry not found
components:
  schemas:
    AuditEntry:
      type: object
      properties:
        id:
          type: string
          example: audit_V1StGXR8Z5jdHi6B
        project_id:
          x-soat-ref: projects
          type: string
          nullable: true
          description: Project the action targeted; null for global actions
        principal_type:
          type: string
          nullable: true
          enum:
            - user
            - api_key
            - null
          description: >-
            Principal kind; null for platform-originated entries (those are
            identified by their `action`, e.g. `quotas:MonitorBreach`)
        principal_id:
          type: string
          nullable: true
          description: Public id of the principal (`user_…` or `key_…`); null for platform-originated entries
        action:
          type: string
          description: The permission-action string that authorized the request
          example: secrets:DeleteSecret
        resource_srn:
          type: string
          nullable: true
          description: SRN the action targeted (type-level `srn:{project}:{type}:*` on creates)
          example: srn:proj_V1StGXR8Z5jdHi6B:secret:sec_V1StGXR8Z5jdHi6B
        resource_public_id:
          type: string
          nullable: true
          description: Target resource public id (from the SRN, or the response body on creates)
        status:
          type: integer
          description: HTTP status of the response
          example: 200
        request_id:
          type: string
          nullable: true
          description: Per-request correlation id (also returned in the X-Request-Id header)
        ip:
          type: string
          nullable: true
        user_agent:
          type: string
          nullable: true
        detail:
          type: object
          nullable: true
          description: >-
            Kind-specific payload. Multi-check routes record the remaining checks
            under `additional_checks`. Platform-originated entries set a
            `detail.kind` discriminator, e.g. `quota_monitor_breach` or
            `guardrail_evaluation`.
          additionalProperties: true
        created_at:
          type: string
          format: date-time
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: JWT token or sk_ api key
