openapi: 3.1.0

info:
  title: GOVP node HTTP surface
  version: "GOVP-1"
  summary: The public endpoints a GOVP issuing domain serves.
  description: |
    Two layers are described here.

    **Layer A — the protocol contract.** Any conforming issuer SHOULD serve the
    three `.well-known` paths. This is what conformance and interoperability
    depend on, and what a verifier fetches to perform the canonical-binding
    check (spec section 10.3).

    **Layer B — the reference implementation.** The WordPress reference plugin
    additionally exposes a read API under `/wp-json/govp/v1`. Other
    implementations MAY mirror it; it is not required for conformance.

    All read endpoints are public, unauthenticated and CORS-open
    (`Access-Control-Allow-Origin: *`) by design: evidence is public and must
    verify from any origin. No secrets are served. Issuing and administration
    are authenticated inside the platform and are never exposed here.

    Normative specification: https://govp.io/govp/spec/govp-1.html
  license:
    name: MIT
  contact:
    name: Gemacode Research
    email: contact@gemacode.org
    url: https://govp.io

servers:
  - url: https://{domain}
    description: Any GOVP issuing domain
    variables:
      domain:
        default: node.example

tags:
  - name: well-known
    description: Layer A — the protocol contract every issuer serves.
  - name: reference-api
    description: Layer B — the reference implementation's read API.

paths:
  /.well-known/govp.txt:
    get:
      tags: [well-known]
      summary: The domain's identity record
      description: |
        The issuing domain's own GOVP-1 record: its public key, publisher name
        and metadata. This is the URL every record's `Canonical` field points
        at, and the identity a verifier pins.
      operationId: getIdentity
      responses:
        "200":
          description: A GOVP-1 record in wire format.
          headers:
            Access-Control-Allow-Origin:
              schema: { type: string, const: "*" }
          content:
            text/plain:
              schema: { type: string }
              example: |
                Version: GOVP-1
                Canonical: https://node.example/.well-known/govp.txt
                Publisher: node.example
                Asset-Type: document
                Asset-ID: site-identity
                Asset-SHA256: 71e5bb01f249ad18a551a218b0b5b9eb40263c64ae1c8f37dc48951622a54734
                Profile: website-integrity
                Generated-At: 2026-05-31T10:00:00Z
                GOVP-ID: GOVP-DOC-39969a54190e
                Evidence: https://node.example/evidence/site-identity.txt
                Public-Key: A6EHv/POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg=
                Signature: uq+dPdfTRlEPPVOosgWAjpjAU+FO0E5SDl5Wo5m9RVK8Ysk/mAN907neMZMD7ISly/VF5Rxcq0JyKwHmjg9YAg==
        "404":
          description: The domain does not publish a GOVP identity.

  /.well-known/govp/index.json:
    get:
      tags: [well-known]
      summary: Discovery index
      description: The list of records this domain publishes, for verifiers, witnesses and peers.
      operationId: getDiscoveryIndex
      responses:
        "200":
          description: Discovery index.
          content:
            application/json:
              schema:
                type: object
                properties:
                  records:
                    type: array
                    items:
                      type: object
                      properties:
                        govp-id: { type: string }
                        url: { type: string, format: uri }

  /.well-known/govp/{govpId}.govp:
    get:
      tags: [well-known]
      summary: A single record
      description: |
        One self-contained GOVP-1 record. Fetching from this URL enables the
        canonical-binding check: the fetched URL must equal the record's
        `Canonical` field.
      operationId: getRecord
      parameters:
        - name: govpId
          in: path
          required: true
          schema:
            type: string
            pattern: '^GOVP-(DOC|DATA|MODEL|AGENT|PIPE|BENCH)-[0-9a-f]{12}$'
          example: GOVP-DOC-39969a54190e
      responses:
        "200":
          description: The record in wire format.
          content:
            text/plain:
              schema: { type: string }
        "404":
          description: No such record on this domain.

  /wp-json/govp/v1/records:
    get:
      tags: [reference-api]
      summary: List published records
      operationId: listRecords
      parameters:
        - name: page
          in: query
          schema: { type: integer, minimum: 1, default: 1 }
        - name: per_page
          in: query
          schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
      responses:
        "200":
          description: A page of records.
          headers:
            X-WP-Total:
              description: Total number of records.
              schema: { type: integer }
            X-WP-TotalPages:
              description: Total number of pages.
              schema: { type: integer }
          content:
            application/json:
              schema:
                type: object
                properties:
                  records: { type: array, items: { type: object } }
                  total: { type: integer }
                  pages: { type: integer }
        "429":
          description: Rate limited (per-IP limiting on public endpoints).

  /wp-json/govp/v1/verify:
    post:
      tags: [reference-api]
      summary: Server-side verification fallback
      description: |
        Verifies a submitted record server-side (libsodium) and returns the
        per-check breakdown. Exists so that a browser without native Ed25519
        never yields "could not verify" — the result is identical to the
        client-side check, which the conformance self-test proves.
      operationId: verifyRecord
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [record]
              properties:
                record:
                  type: string
                  description: The GOVP-1 record in wire format.
      responses:
        "200":
          description: Verification result — always a per-check breakdown, never a bare boolean.
          content:
            application/json:
              schema:
                $ref: 'https://govp.io/govp/schemas/govp-1.schema.json#/$defs/verificationResult'

  /wp-json/govp/v1/badge:
    get:
      tags: [reference-api]
      summary: Public badge data for a record
      description: Public record metadata used to render a verification badge. Exposes no secrets.
      operationId: getBadge
      parameters:
        - name: id
          in: query
          required: true
          schema: { type: string }
          example: GOVP-DOC-39969a54190e
      responses:
        "200":
          description: Badge data.
          content:
            application/json:
              schema: { type: object }
        "404":
          description: No such record.

components:
  schemas:
    Govp1Record:
      $ref: 'https://govp.io/govp/schemas/govp-1.schema.json'
