# Generated by cmd/openapi-gen from the agent's route table. Do not edit by hand:
# run `task openapi` after changing internal/adapter/http/routes.go or any response type.
openapi: 3.1.0
info:
  description: |-
    The local control plane of a Keystone agent: apply a deployment plan, inspect what
    is actually running, and control individual components.

    This document is generated from the agent's route table, so it describes the
    endpoints the running binary serves rather than a hand-maintained copy of them.

    Authentication: every endpoint except /healthz requires `Authorization: Bearer <token>`
    when the agent was started with a token. A token is mandatory for the agent to
    bind any address other than loopback, so in practice a reachable API is always
    authenticated. Keystone does not terminate TLS — put it behind a reverse proxy or
    reach it over a VPN.
  title: Keystone agent API
  version: 0.1.0-dev
servers:
- description: Default loopback bind
  url: http://127.0.0.1:8080
- description: A device behind a TLS-terminating proxy
  url: https://{device}
  variables:
    device:
      default: device.example.com
paths:
  /healthz:
    get:
      description: Exempt from authentication and free of component detail, so it
        is safe for an unauthenticated probe.
      operationId: getHealthz
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdapterHealthStatus'
          description: OK
      summary: Agent liveness
      tags:
      - agent
  /metrics:
    get:
      description: Component state, health, restarts, and per-process CPU and memory.
      operationId: getMetrics
      responses:
        "200":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: OK
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
      security:
      - bearerAuth: []
      summary: Prometheus metrics
      tags:
      - agent
  /v1/components:
    get:
      description: A component reported running has a live supervision loop and, for
        a process, a live PID. See the component state contract in the documentation.
      operationId: getComponents
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/StoreComponentInfo'
                type:
                - "null"
                - array
          description: OK
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
      security:
      - bearerAuth: []
      summary: List components
      tags:
      - components
  /v1/components/{name}:restart:
    post:
      description: 'Restarts a component and cascades to its dependents according
        to each dependency type: hard and soft cascade, ordering does not. With dry=true
        the response is the planned order instead (stopOrder/startOrder).'
      operationId: postComponentsNameRestart
      parameters:
      - description: pid (default) returns once a new PID exists; health returns once
          it probes healthy
        in: query
        name: wait
        schema:
          description: pid (default) returns once a new PID exists; health returns
            once it probes healthy
          examples:
          - health
          type: string
      - description: How long to wait, as a Go duration. Default 60s
        in: query
        name: timeout
        schema:
          description: How long to wait, as a Go duration. Default 60s
          examples:
          - 120s
          type: string
      - description: true reports what would be restarted and changes nothing
        in: query
        name: dry
        schema:
          description: true reports what would be restarted and changes nothing
          examples:
          - "true"
          type: string
      - description: Component name as given in the plan
        in: path
        name: name
        required: true
        schema:
          description: Component name as given in the plan
          examples:
          - api
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                oneOf:
                - $ref: '#/components/schemas/AdapterRestartResult'
                - $ref: '#/components/schemas/AdapterRestartDryResult'
          description: OK
        "400":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Bad Request
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
        "500":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Internal Server Error
        "504":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Gateway Timeout
      security:
      - bearerAuth: []
      summary: Restart one component
      tags:
      - components
  /v1/components/{name}:stop:
    post:
      description: Stops a single component. Its dependents are left running.
      operationId: postComponentsNameStop
      parameters:
      - description: Component name as given in the plan
        in: path
        name: name
        required: true
        schema:
          description: Component name as given in the plan
          examples:
          - api
          type: string
      responses:
        "204":
          description: No Content
        "400":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Bad Request
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
        "500":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Internal Server Error
      security:
      - bearerAuth: []
      summary: Stop one component
      tags:
      - components
  /v1/datasets:
    get:
      description: 'What each refreshable dataset is currently serving: version, when
        it was published, when it was last refreshed, and how old it is. `stale` means
        it has passed its max_age. `ageUnknown` means the device''s clock cannot be
        trusted, so no age is reported rather than a made-up one.'
      operationId: getDatasets
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/AdapterDatasetInfo'
                type:
                - "null"
                - array
          description: OK
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
      security:
      - bearerAuth: []
      summary: List datasets
      tags:
      - agent
  /v1/datasets:refresh:
    post:
      description: Checks every dataset for a new version immediately, instead of
        waiting for its refresh interval — for the day a vulnerability feed cannot
        wait until tonight. Each dataset still goes through signature verification
        and the anti-replay rule.
      operationId: postDatasetsRefresh
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/AdapterDatasetInfo'
                type:
                - "null"
                - array
          description: OK
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
        "500":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Internal Server Error
      security:
      - bearerAuth: []
      summary: Refresh datasets now
      tags:
      - agent
  /v1/plan/apply:
    post:
      description: 'The body is the plan TOML itself. A JSON body naming a planPath
        is rejected: letting the API name a local path would turn it into a file-read
        primitive. Applying an unchanged plan is a no-op — unchanged components that
        are alive and supervised are left running.'
      operationId: postPlanApply
      parameters:
      - description: true validates the plan and installs or starts nothing; the reconcile
          (stop, start and no-touch sets) goes to the agent's log, and the response
          has no body
        in: query
        name: dry
        schema:
          description: true validates the plan and installs or starts nothing; the
            reconcile (stop, start and no-touch sets) goes to the agent's log, and
            the response has no body
          examples:
          - "true"
          type: string
      requestBody:
        content:
          text/plain:
            schema:
              type: string
        description: TOML document
      responses:
        "202":
          description: Accepted
        "400":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Bad Request
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
        "500":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Internal Server Error
        "503":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Service Unavailable
      security:
      - bearerAuth: []
      summary: Apply a plan
      tags:
      - plan
  /v1/plan/graph:
    get:
      description: Nodes, edges (dependency to dependents) and a valid topological
        start order.
      operationId: getPlanGraph
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdapterGraphInfo'
          description: OK
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
      security:
      - bearerAuth: []
      summary: Dependency graph
      tags:
      - plan
  /v1/plan/reconcile:
    post:
      description: 'Re-applies the plan already applied, restarting components that
        died and exhausted their restart budget. Components that are alive and supervised
        are left running. It changes nothing when no plan has been applied, when an
        apply is already running, or when an operator stopped the plan — those answer
        200 with skipped=true and a reason. Unlike apply, it never rolls back: the
        plan in effect is its own predecessor, so a rollback would stop healthy components
        and re-apply the failure.'
      operationId: postPlanReconcile
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdapterReconcileResult'
          description: OK
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
        "500":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Internal Server Error
      security:
      - bearerAuth: []
      summary: Repair the plan in effect
      tags:
      - plan
  /v1/plan/status:
    get:
      description: The applied plan, its status, the last error if any, and the component
        list — so a poller needs one request rather than two.
      operationId: getPlanStatus
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdapterPlanStatus'
          description: OK
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
      security:
      - bearerAuth: []
      summary: Plan status
      tags:
      - plan
  /v1/plan/stop:
    post:
      description: 'Stops the whole plan in reverse dependency order and records the
        plan as stopped, which the agent remembers across reboots: it will not resume
        a plan you stopped.'
      operationId: postPlanStop
      responses:
        "204":
          description: No Content
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
        "500":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Internal Server Error
      security:
      - bearerAuth: []
      summary: Stop every component
      tags:
      - plan
  /v1/recipes:
    get:
      description: GET lists name:version entries in the agent's recipe store. POST
        stores a recipe TOML document so plans can refer to it as name:version; recipes
        added this way are trusted through API authentication rather than a file signature.
      operationId: getRecipes
      parameters:
      - description: true overwrites an existing recipe with the same name and version
        in: query
        name: force
        schema:
          description: true overwrites an existing recipe with the same name and version
          examples:
          - "true"
          type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  type: string
                type:
                - "null"
                - array
          description: OK
        "400":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Bad Request
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
        "409":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Conflict
        "500":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Internal Server Error
      security:
      - bearerAuth: []
      summary: List or add recipes
      tags:
      - recipes
    post:
      description: GET lists name:version entries in the agent's recipe store. POST
        stores a recipe TOML document so plans can refer to it as name:version; recipes
        added this way are trusted through API authentication rather than a file signature.
      operationId: postRecipes
      parameters:
      - description: true overwrites an existing recipe with the same name and version
        in: query
        name: force
        schema:
          description: true overwrites an existing recipe with the same name and version
          examples:
          - "true"
          type: string
      requestBody:
        content:
          text/plain:
            schema:
              type: string
        description: TOML document
      responses:
        "200":
          content:
            application/json:
              schema:
                items:
                  type: string
                type:
                - "null"
                - array
          description: OK
        "400":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Bad Request
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
        "409":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Conflict
        "500":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Internal Server Error
      security:
      - bearerAuth: []
      summary: List or add recipes
      tags:
      - recipes
  /v1/recipes/{name}/{version}:
    delete:
      description: Removes a recipe from the store. Name and version are validated
        against an allow-list, so neither can traverse a path.
      operationId: deleteRecipesNameVersion
      parameters:
      - description: Recipe metadata.name
        in: path
        name: name
        required: true
        schema:
          description: Recipe metadata.name
          examples:
          - com.acme.api
          type: string
      - description: Recipe metadata.version
        in: path
        name: version
        required: true
        schema:
          description: Recipe metadata.version
          examples:
          - 1.4.0
          type: string
      responses:
        "204":
          description: No Content
        "400":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Bad Request
        "401":
          content:
            text/plain:
              schema:
                type:
                - "null"
                - string
          description: Unauthorized
      security:
      - bearerAuth: []
      summary: Delete a recipe
      tags:
      - recipes
components:
  schemas:
    AdapterDatasetInfo:
      properties:
        ageSeconds:
          format: int64
          type: integer
        ageUnknown:
          type: boolean
        lastRefresh:
          type: string
        lastResult:
          type: string
        manifestUri:
          type: string
        maxAge:
          type: string
        name:
          type: string
        path:
          type: string
        published:
          type: string
        refresh:
          type: string
        stale:
          type: boolean
        version:
          type: string
      type: object
    AdapterGraphInfo:
      properties:
        edges:
          additionalProperties:
            items:
              type: string
            type: array
          type:
          - object
          - "null"
        nodes:
          items:
            type: string
          type:
          - array
          - "null"
        order:
          items:
            type: string
          type:
          - array
          - "null"
      type: object
    AdapterHealthStatus:
      properties:
        agent_commit:
          type: string
        agent_version:
          type: string
        clock_source:
          type: string
        clock_trusted:
          type: boolean
        closed:
          type: boolean
        status:
          type: string
        time_utc:
          type: string
        uptime:
          type: string
      type: object
    AdapterPlanStatus:
      properties:
        components:
          items:
            $ref: '#/components/schemas/StoreComponentInfo'
          type:
          - array
          - "null"
        error:
          type: string
        lastReconcile:
          type: string
        lastReconcileResult:
          type: string
        planPath:
          type: string
        status:
          type: string
        unknownFields:
          items:
            type: string
          type: array
      type: object
    AdapterReconcileResult:
      properties:
        duration:
          type: string
        reason:
          type: string
        repaired:
          items:
            type: string
          type: array
        skipped:
          type: boolean
      type: object
    AdapterRestartDryResult:
      properties:
        startOrder:
          items:
            type: string
          type:
          - array
          - "null"
        stopOrder:
          items:
            type: string
          type:
          - array
          - "null"
      type: object
    AdapterRestartResult:
      properties:
        component:
          type: string
        dependents:
          additionalProperties:
            type: integer
          type:
          - object
          - "null"
        pid:
          type: integer
        timeout:
          type: string
        wait:
          type: string
      type: object
    StoreComponentInfo:
      properties:
        last_health:
          type: string
        name:
          type: string
        pid:
          type: integer
        recipe:
          type: string
        restarts:
          type: integer
        state:
          type: string
        version:
          type: string
      type: object
  securitySchemes:
    bearerAuth:
      description: The value of KEYSTONE_API_TOKEN on the agent.
      scheme: bearer
      type: http
