# Proposed contract, version 1: endpoints, errors, paging, idempotency, security and the capability of every
# operation. Resource schemas were initialized from the data model (docs/06-data) and are kept aligned with it; input
# schemas (*Input, *Request) and views are specific to this contract. Status: proposal for review, not confirmed design.
# Conventions: docs/05-interfaces/choosing-an-interface.md. Vendor extensions: x-moonitoring-audience (who calls),
# x-moonitoring-capability (the capability checked), x-moonitoring-entitlement (the entitlement feature checked),
# x-moonitoring-requirements (the requirements of 1.requisiti-raffinati.txt the operation serves).
openapi: 3.1.0
info:
  title: Releases Signing API
  version: 1.0.0
  description: 'The trust chain clients verify: the public keys of the TUF roles, whose private halves live in Cloud
    KMS, and the signed metadata documents with their signatures. Staff register, rotate and revoke keys but never
    sign: the system signs when a release becomes active. Clients fetch metadata by role.'
servers:
- url: https://releases.moonitoring.com/signing/v1
  description: Prod
- url: https://stage-releases.moonitoring.com/signing/v1
  description: Stage, through IAP
security:
- bearer: []
tags:
- name: Keys
  description: Signing and encryption keys
- name: Metadata
  description: Signed documents
- name: Client
  description: What a client fetches
paths:
  /signing-keys:
    get:
      summary: List signing keys
      operationId: listSigningKeys
      tags:
      - Keys
      parameters:
      - name: role
        in: query
        required: false
        schema:
          type: string
          enum:
          - root
          - targets
          - snapshot
          - timestamp
        description: Only this role.
      - name: status
        in: query
        required: false
        schema:
          type: string
          enum:
          - active
          - rotating
          - revoked
        description: Only this status.
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Cursor
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Limit
      responses:
        '200':
          description: One page of results.
          content:
            application/json:
              schema:
                type: object
                description: One page of items.
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/ReleaseSigningKey'
                  next_cursor:
                    type: string
                    description: Where the next page starts; absent on the last page.
                required:
                - items
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.releases.read
      x-moonitoring-requirements:
      - '114'
      - '1006.5'
    post:
      summary: Register a signing key
      operationId: createSigningKey
      tags:
      - Keys
      parameters:
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/IdempotencyKey
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReleaseSigningKeyInput'
      responses:
        '201':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReleaseSigningKey'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
        '422':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/UnprocessableEntity
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.release_keys.manage
      x-moonitoring-requirements:
      - '114'
      - '1006.15'
  /signing-keys/{key_id}/rotate:
    post:
      summary: Rotate a signing key
      operationId: rotateSigningKey
      description: Root rotation produces a new root document signed by the old and the new keys, each set meeting
        its threshold.
      tags:
      - Keys
      parameters:
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/IdempotencyKey
      - name: key_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReleaseSigningKeyInput'
      responses:
        '201':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReleaseSigningKey'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
        '422':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/UnprocessableEntity
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.release_keys.manage
      x-moonitoring-requirements:
      - '114.6'
      - '115'
      - '1006.15'
  /signing-keys/{key_id}/revoke:
    post:
      summary: Revoke a signing key
      operationId: revokeSigningKey
      tags:
      - Keys
      parameters:
      - name: key_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReleaseSigningKey'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.release_keys.manage
      x-moonitoring-requirements:
      - '114.6'
      - '1006.15'
  /encryption-keys:
    get:
      summary: List encryption keys
      operationId: listEncryptionKeys
      tags:
      - Keys
      parameters:
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Cursor
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Limit
      responses:
        '200':
          description: One page of results.
          content:
            application/json:
              schema:
                type: object
                description: One page of items.
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/ReleaseEncryptionKey'
                  next_cursor:
                    type: string
                    description: Where the next page starts; absent on the last page.
                required:
                - items
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.releases.read
      x-moonitoring-requirements:
      - '117'
    post:
      summary: Register an encryption key
      operationId: createEncryptionKey
      tags:
      - Keys
      parameters:
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/IdempotencyKey
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReleaseEncryptionKeyInput'
      responses:
        '201':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReleaseEncryptionKey'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
        '422':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/UnprocessableEntity
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.release_keys.manage
      x-moonitoring-requirements:
      - '117'
      - '1006.15'
  /encryption-keys/{key_id}/rotate:
    post:
      summary: Rotate an encryption key
      operationId: rotateEncryptionKey
      tags:
      - Keys
      parameters:
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/IdempotencyKey
      - name: key_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReleaseEncryptionKeyInput'
      responses:
        '201':
          description: The result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReleaseEncryptionKey'
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
        '409':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Conflict
        '422':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/UnprocessableEntity
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.release_keys.manage
      x-moonitoring-requirements:
      - '117.3'
      - '1006.15'
  /signed-metadata:
    get:
      summary: List signed metadata
      operationId: listSignedMetadata
      tags:
      - Metadata
      parameters:
      - name: scope_ref
        in: query
        required: false
        schema:
          type: string
        description: Only this scope, e.g. app/<id>/channel/<id>.
      - name: role
        in: query
        required: false
        schema:
          type: string
          enum:
          - root
          - targets
          - snapshot
          - timestamp
        description: Only this role.
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Cursor
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Limit
      responses:
        '200':
          description: One page of results.
          content:
            application/json:
              schema:
                type: object
                description: One page of items.
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/ReleaseSignedMetadata'
                  next_cursor:
                    type: string
                    description: Where the next page starts; absent on the last page.
                required:
                - items
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.releases.read
      x-moonitoring-requirements:
      - '115'
      - '1006.5'
  /signed-metadata/{metadata_id}/signatures:
    get:
      summary: List a document's signatures
      operationId: listMetadataSignatures
      tags:
      - Metadata
      parameters:
      - name: metadata_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Cursor
      - $ref: ../../../../common/v1/schemas.yaml#/components/parameters/Limit
      responses:
        '200':
          description: One page of results.
          content:
            application/json:
              schema:
                type: object
                description: One page of items.
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/ReleaseMetadataSignature'
                  next_cursor:
                    type: string
                    description: Where the next page starts; absent on the last page.
                required:
                - items
        '400':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/BadRequest
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
      x-moonitoring-audience: staff
      x-moonitoring-capability: releases.releases.read
      x-moonitoring-requirements:
      - '116'
  /applications/{application_id}/channels/{channel_id}/metadata/{role}:
    get:
      summary: Fetch the current metadata of a role
      operationId: getChannelMetadata
      description: The TUF document a client trusts, fetched timestamp, then snapshot, then targets. A client refuses
        a version lower than the last one it saw and an expired document. Private channels answer 404 to a caller
        who may not receive them.
      tags:
      - Client
      parameters:
      - name: application_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: channel_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: role
        in: path
        required: true
        schema:
          type: string
          enum:
          - root
          - targets
          - snapshot
          - timestamp
      responses:
        '200':
          description: The result.
          content:
            application/json:
              schema:
                type: object
                description: 'The signed metadata document in TUF JSON format: `signed` and `signatures`.'
                additionalProperties: true
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
      security:
      - {}
      - bearer: []
      - installationToken: []
      x-moonitoring-audience: client
      x-moonitoring-capability: null
      x-moonitoring-requirements:
      - '115'
      - '115.4'
      - '115.5'
      - '115.8'
      - '116'
  /applications/{application_id}/channels/{channel_id}/metadata/root/{version}:
    get:
      summary: Fetch a root version
      operationId: getChannelRootVersion
      description: For a client that steps through root rotations one at a time.
      tags:
      - Client
      parameters:
      - name: application_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: channel_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: version
        in: path
        required: true
        schema:
          type: integer
      responses:
        '200':
          description: The result.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Unauthorized
        '403':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/Forbidden
        '404':
          $ref: ../../../../common/v1/schemas.yaml#/components/responses/NotFound
      security:
      - {}
      - bearer: []
      - installationToken: []
      x-moonitoring-audience: client
      x-moonitoring-capability: null
      x-moonitoring-requirements:
      - '115'
      - '115.4'
components:
  schemas:
    ReleaseSigningKey:
      description: The public half of a key that signs release information, so clients can check that it really
        comes from us. Keys follow The Update Framework (TUF), and each one belongs to a TUF role; the private half
        stays in a key management service (KMS) or a hardware security module (HSM).
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Key.
        role:
          type: string
          enum:
          - root
          - targets
          - snapshot
          - timestamp
        algorithm:
          type: string
          enum:
          - ed25519
          - ecdsa_p256
        public_key:
          type: string
        kms_ref:
          type: string
        status:
          type: string
          enum:
          - active
          - rotating
          - revoked
        validity:
          $ref: ../../../../common/v1/schemas.yaml#/components/schemas/Period
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
      - id
      - role
      - algorithm
      - public_key
      - kms_ref
      - status
      - validity
      - created_at
      - updated_at
    ReleaseSignedMetadata:
      description: A signed document that clients trust, stating for example which build is current on a channel.
        Its version and expiry protect clients from being sent back to, or frozen on, old metadata. Immutable.
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Key.
        role:
          type: string
          enum:
          - root
          - targets
          - snapshot
          - timestamp
        scope_ref:
          type: string
          description: E.g. `app//channel/`.
        version:
          type: integer
          description: 'Always increasing: a client refuses a version lower than the last one it saw (no rollback
            to old metadata). `(role, scope_ref, version)` is unique.'
        expires_at:
          type: string
          format: date-time
          description: A client refuses expired metadata, so it cannot be frozen on an old version.
        content_hash:
          type: string
        object_key:
          type: string
        required_signatures:
          type: integer
          description: 'Threshold of the role when the document was signed: root 2 of at least 3 keys, targets 2,
            snapshot 1, timestamp 1. A client accepts the document only with that many valid signatures from distinct
            active keys of the role.'
        created_at:
          type: string
          format: date-time
      required:
      - id
      - role
      - scope_ref
      - version
      - expires_at
      - content_hash
      - object_key
      - required_signatures
      - created_at
    ReleaseMetadataSignature:
      description: One signature of a signed metadata document by one signing key.
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Key.
        metadata:
          type: string
          format: uuid
          description: ID of the ReleaseSignedMetadata.
        key:
          type: string
          format: uuid
          description: ID of the ReleaseSigningKey. Same role as the metadata; active when it signs. `(metadata,
            key)` is unique.
        signature:
          type: string
        created_at:
          type: string
          format: date-time
      required:
      - id
      - metadata
      - key
      - signature
      - created_at
    ReleaseEncryptionKey:
      description: A reference to a key, held in the key management service (KMS), that encrypts confidential content.
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Key.
        kms_ref:
          type: string
        rotated_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
      required:
      - id
      - kms_ref
      - created_at
    ReleaseSigningKeyInput:
      type: object
      description: Registers a key generated in Cloud KMS, by its reference and public half.
      properties:
        role:
          type: string
          enum:
          - root
          - targets
          - snapshot
          - timestamp
        algorithm:
          type: string
          enum:
          - ed25519
          - ecdsa_p256
        public_key:
          type: string
        kms_ref:
          type: string
        validity:
          $ref: ../../../../common/v1/schemas.yaml#/components/schemas/Period
      required:
      - role
      - algorithm
      - public_key
      - kms_ref
      - validity
      additionalProperties: false
    ReleaseEncryptionKeyInput:
      type: object
      description: Registers an encryption key of the KMS.
      properties:
        kms_ref:
          type: string
      required:
      - kms_ref
  securitySchemes:
    installationToken:
      type: http
      scheme: bearer
      description: 'The token of one installation, `mri_<random>`, issued at enrolment and rotated by the client; told
        apart from a token of `access` by its prefix (decided 2026-11-05). Only its SHA-256 is stored.'
    bearer:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'The short-lived token signed by `access`: an Organization user token, a staff token or a client
        token, as the operation''s audience says.'
