openapi: 3.1.0
info:
  title: MVG Org Signing Service (Strict Template)
  version: "23.5"
  description: >
    Big-tech intake template for an internal org signing service used to anchor mvg/MVG conformance
    reports to org-controlled keys (KMS/HSM). This spec is designed for idempotency, replay protection,
    auditability, and policy-driven key alias selection. DSSEv1 signatures MUST use PAE.

servers:
  - url: http://localhost:8080

paths:
  /healthz:
    get:
      summary: Health check
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { type: object }
  /openapi.yaml:
    get:
      summary: Fetch OpenAPI spec
      responses:
        "200":
          description: YAML
          content:
            text/yaml:
              schema: { type: string }

  /v1/policy:
    get:
      summary: Service policy (allowlisted key aliases, max TTL)
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  max_ttl_seconds: { type: integer }
                  allowed_kms_providers:
                    type: array
                    items: { type: string }
                  allowed_key_aliases:
                    type: array
                    items: { type: string }

  /v1/bootstrap-public:
    get:
      summary: Public bootstrap key (used to verify Trust Anchor Snapshot DSSE)
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/BootstrapPublic" }

  /v1/trust-anchor-snapshot:
    get:
      summary: Trust Anchor Registry Snapshot (DSSE)
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/DSSEEnvelope" }

  /v1/sign-report:
    post:
      summary: Legacy DSSE report signing (compat)
      description: >
        Compatibility endpoint. For production use, prefer /v1/sign-report-strict.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/LegacySigningRequest" }
      responses:
        "200":
          description: DSSE envelope
          content:
            application/json:
              schema: { $ref: "#/components/schemas/DSSEEnvelope" }

  /v1/sign-report-strict:
    post:
      summary: Strict DSSE report signing (idempotent + replay-protected)
      parameters:
        - in: header
          name: Idempotency-Key
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 128
          description: >
            Required idempotency key. Replays with the same key MUST return the same response
            if and only if the request hash is identical; otherwise 409.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/SigningRequest" }
      responses:
        "200":
          description: Signing response wrapper
          content:
            application/json:
              schema: { $ref: "#/components/schemas/SigningResponse" }
        "400":
          description: Validation error
        "403":
          description: Key alias not allowed by policy
        "409":
          description: Idempotency/replay conflict

  /v1/demo/bundle:
    post:
      summary: Convenience bundle (bootstrap + snapshot + signed report DSSE)
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/LegacySigningRequest" }
      responses:
        "200":
          description: Bundle
          content:
            application/json:
              schema:
                type: object
                properties:
                  bootstrap_public: { $ref: "#/components/schemas/BootstrapPublic" }
                  trust_anchor_snapshot_dsse: { $ref: "#/components/schemas/DSSEEnvelope" }
                  signed_report_dsse: { $ref: "#/components/schemas/DSSEEnvelope" }
components:
  schemas:
    BootstrapPublic:
      type: object
      required: [kid, jwk]
      properties:
        kid: { type: string }
        jwk: { type: object }

    DSSEEnvelope:
      type: object
      required: [payloadType, payload, signatures]
      properties:
        payloadType: { type: string }
        payload:
          type: string
          description: base64url payload bytes
        signatures:
          type: array
          items:
            type: object
            required: [keyid, sig]
            properties:
              keyid: { type: string }
              sig: { type: string, description: base64url Ed25519 signature over DSSE PAE }
        ext:
          type: object
          description: Optional extension metadata (non-normative)

    LegacySigningRequest:
      type: object
      required: [payload_b64u]
      properties:
        payloadType: { type: string, default: "application/octet-stream" }
        payload_b64u: { type: string }
        request_id: { type: string }

    SigningRequest:
      type: object
      required:
        - schema_id
        - request_id
        - issued_utc
        - expires_utc
        - client_nonce_b64u
        - payloadType
        - payload_b64u
        - payload_sha256
        - kms
        - audit
      properties:
        schema_id:
          type: string
          const: "mvg.org_signer.signing_request@1"
        request_id:
          type: string
          description: UUID
        issued_utc:
          type: string
          format: date-time
        expires_utc:
          type: string
          format: date-time
        client_nonce_b64u:
          type: string
          description: base64url(16–64 random bytes)
        payloadType:
          type: string
        payload_b64u:
          type: string
        payload_sha256:
          type: string
          description: hex SHA-256(payload bytes)
        kms:
          type: object
          required: [provider, key_alias]
          properties:
            provider:
              type: string
              enum: [local, aws-kms, azure-keyvault]
            key_alias:
              type: string
              description: allowlisted key alias (policy)
            key_version:
              type: string
              nullable: true
        audit:
          type: object
          required: [requester, ticket]
          properties:
            org: { type: string }
            environment: { type: string, enum: [prod, staging, dev], nullable: true }
            requester: { type: string }
            ticket: { type: string }
            purpose: { type: string }
            source: { type: string, nullable: true }

    SigningResponse:
      type: object
      required: [schema_id, request_id, idempotency_key, status, audit_receipt, signed_report_dsse]
      properties:
        schema_id:
          type: string
          const: "mvg.org_signer.signing_response@1"
        request_id: { type: string }
        idempotency_key: { type: string }
        status: { type: string, enum: [SIGNED] }
        audit_receipt:
          type: object
          required: [server_utc, request_hash_sha256, signing_kid, kms]
          properties:
            server_utc: { type: string, format: date-time }
            request_hash_sha256: { type: string }
            signing_kid: { type: string }
            kms:
              type: object
              required: [provider, key_alias]
              properties:
                provider: { type: string }
                key_alias: { type: string }
                key_version: { type: string, nullable: true }
        signed_report_dsse:
          $ref: "#/components/schemas/DSSEEnvelope"
