openapi: 3.0.3
info:
  title: SOAT Policies API
  version: 1.0.0
  description: API for managing global policies (admin-only)
  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: Policies
    description: Manage policies
security:
  - bearerAuth: []
paths:
  /api/v1/policies:
    get:
      tags:
        - Policies
      summary: List all policies
      description: >
        Returns global policies. Requires admin role. Pass user_id to list only
        the policies attached to that user.
      operationId: listPolicies
      parameters:
        - name: user_id
          in: query
          required: false
          description: Return only policies attached to this user (user_...)
          schema:
            type: string
        - $ref: './pagination.yaml#/components/parameters/Limit'
        - $ref: './pagination.yaml#/components/parameters/Offset'
      responses:
        '200':
          description: List of policies
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/PolicyRecord'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '401':
          description: Unauthorized
        '403':
          description: Forbidden (non-admin user)
    post:
      tags:
        - Policies
      summary: Create a policy
      description: Creates a new global policy. Requires admin role.
      operationId: createPolicy
      x-soat-agent-exclude: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - document
              additionalProperties: false
              properties:
                name:
                  type: string
                  example: ReadOnlyAccess
                description:
                  type: string
                  example: Allows read-only access to all resources
                document:
                  $ref: '#/components/schemas/PolicyDocument'
      responses:
        '201':
          description: Policy created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PolicyRecord'
        '400':
          description: Bad request (invalid policy document)
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden (non-admin user)
  /api/v1/policies/{policy_id}:
    get:
      tags:
        - Policies
      summary: Get a policy
      description: Returns details of a specific policy. Requires admin role.
      operationId: getPolicy
      parameters:
        - name: policy_id
          in: path
          required: true
          description: Policy public ID (pol_ prefix)
          schema:
            type: string
            example: pol_V1StGXR8Z5jdHi6B
      responses:
        '200':
          description: Policy details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PolicyRecord'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden (non-admin user)
        '404':
          description: Policy not found
    put:
      tags:
        - Policies
      summary: Update a policy
      description: Updates an existing global policy. Requires admin role.
      operationId: updatePolicy
      x-soat-agent-exclude: true
      parameters:
        - name: policy_id
          in: path
          required: true
          description: Policy public ID (pol_ prefix)
          schema:
            type: string
            example: pol_V1StGXR8Z5jdHi6B
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - document
              additionalProperties: false
              properties:
                name:
                  type: string
                description:
                  type: string
                document:
                  $ref: '#/components/schemas/PolicyDocument'
      responses:
        '200':
          description: Policy updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PolicyRecord'
        '400':
          description: Bad request (invalid policy document)
          content:
            application/json:
              schema:
                $ref: './errors.yaml#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden (non-admin user)
        '404':
          description: Policy not found
    delete:
      tags:
        - Policies
      summary: Delete a policy
      description: Deletes a global policy. Requires admin role.
      operationId: deletePolicy
      x-soat-agent-exclude: true
      parameters:
        - name: policy_id
          in: path
          required: true
          description: Policy public ID (pol_ prefix)
          schema:
            type: string
            example: pol_V1StGXR8Z5jdHi6B
      responses:
        '204':
          description: Policy deleted successfully
        '401':
          description: Unauthorized
        '403':
          description: Forbidden (non-admin user)
        '404':
          description: Policy not found
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: JWT token or sk_ api key
  schemas:
    PolicyStatement:
      type: object
      required:
        - effect
        - action
      properties:
        effect:
          type: string
          enum: [Allow, Deny]
          example: Allow
        action:
          type: array
          items:
            type: string
          example: ['files:ListFiles', 'files:CreateFile']
        resource:
          type: array
          items:
            type: string
          example: ['srn:proj_abc:files:*']
        condition:
          type: object
          additionalProperties: true
          description: >
            Optional condition block. Keys are condition operators (e.g.
            StringEquals) mapping to context-key/value maps; evaluated by the
            policy compiler. A context key must be one the platform supplies —
            `soat:ResourceType` or `soat:ResourceTag/<tag>` — because an
            unknown key is absent from the evaluation context, so a `Deny`
            naming one would never deny. The tag name after the slash is
            opaque and unconstrained. Operator and context keys are matched by
            exact string and are never case-converted, so they round-trip
            exactly as written: `soat:ResourceTag/cost_center` selects the tag
            `cost_center`, not `costCenter`.
          example: { StringEquals: { 'soat:ResourceTag/env': 'prod' } }
    PolicyDocument:
      type: object
      required:
        - statement
      properties:
        statement:
          type: array
          items:
            $ref: '#/components/schemas/PolicyStatement'
    PolicyRecord:
      type: object
      properties:
        id:
          type: string
          description: Public policy ID (pol_ prefix)
          example: pol_V1StGXR8Z5jdHi6B
        name:
          type: string
          nullable: true
          example: ReadOnlyAccess
        description:
          type: string
          nullable: true
        document:
          $ref: '#/components/schemas/PolicyDocument'
        created_at:
          type: string
          format: date-time
          example: '2024-01-01T00:00:00.000Z'
        updated_at:
          type: string
          format: date-time
          example: '2024-01-01T00:00:00.000Z'
