University of Oxford · API Governance Rules

University of Oxford API Rules

Spectral linting rules defining API design standards and conventions for University of Oxford.

19 Rules error 17 warn 2
View Rules File View on GitHub

Rule Categories

oxford

Rules

error
oxford-operator-declared
Every OpenAPI in this repository must declare info.x-operator. A university is a federation of buyers; without an explicit operator an artifact silently credits the institution for whatever it describes.
$.info
error
oxford-operator-is-institution
Only institution-operated contracts are saved under this slug. A tenant relationship is recorded in apis.yml as an apis[] entry, never as a saved contract; a vendor's generic contract belongs in the vendor's own repository.
$.info.x-operator
error
oxford-operator-evidence-required
The operator claim must carry the evidence that settles it — a registrable domain, a DNS resolution, or a self-reported origin host in the API's own response.
$.info
error
oxford-provenance-block-required
Every document must state how API Evangelist came to hold it. An unmarked artifact is credited to the institution as if they published it.
$.info
error
oxford-provenance-method-controlled
Provenance method must come from the controlled vocabulary.
$.info.x-provenance
error
oxford-provenance-source-required
The provenance source must name the URLs the document was derived from.
$.info.x-provenance
error
oxford-no-vendor-servers
No server in an Oxford-attributed contract may point at a shared vendor platform. This is the check that would have caught the June 2026 cohort's Figshare misattribution.
$.servers[*].url
error
oxford-servers-on-institution-domain
Every server must sit under a University of Oxford registrable domain. Oxford's surfaces live on ox.ac.uk and its subdomains; anything else is somebody else's engineering.
$.servers[*].url
error
oxford-no-placeholder-servers
Placeholder, staging and localhost hosts are never a surface.
$.servers[*].url
error
oxford-info-contact-required
Oxford's endpoints self-report real administrative contacts (ora-dev@bodleian.ox.ac.uk, ota@bodleian.ox.ac.uk). Carry them.
$.info
error
oxford-info-description-substantive
Descriptions must be substantive enough to state what the surface is and whether Oxford documents it.
$.info
error
oxford-info-version-required
$.info
error
oxford-operation-id-required
$.paths[*][get,post,put,patch,delete]
warn
oxford-operation-id-camel-case
$.paths[*][get,post,put,patch,delete].operationId
error
oxford-operation-tagged
Every operation carries exactly the tags of its own surface. Tag hygiene matters here for a specific reason: refine-openapis splits by tag, and the June 2026 cohort turned one vendor contract into eleven apparent APIs that way.
$.paths[*][get,post,put,patch,delete]
warn
oxford-operation-summary-required
$.paths[*][get,post,put,patch,delete]
error
oxford-operation-has-200
$.paths[*][get,post,put,patch,delete].responses
error
oxford-no-invented-security
No Oxford institution-operated surface authenticates. A security scheme appearing in one of these documents would be an invention, not a finding.
$.components
error
oxford-no-global-security
$

Spectral Ruleset

Raw ↑
# authorship: generated by API Evangelist tooling. Stamped 2026-08-19.
# An unmarked file is NOT assumed to be ours.
x-method: generated
x-generated: '2026-08-19'
x-source: >-
  Authored against the four DERIVED OpenAPI documents in ../openapi/, which describe University
  of Oxford institution-operated surfaces. Oxford publishes no OpenAPI and no governance
  ruleset; this ruleset governs OUR descriptions of Oxford's surfaces, and its most important
  rules are the provenance rules — they exist to stop a derived document from ever being read
  as something Oxford published, and to stop a vendor's contract from ever being filed under
  this slug.
x-operator: institution
# University of Oxford Spectral ruleset.
# Spectral 6.x conventions. Built-in functions only (truthy, pattern, defined, casing,
# undefined, xor) so the ruleset runs with no custom JS.
formats:
  - oas3
rules:
  # --- PROVENANCE: the rules this cohort exists for --------------------------
  oxford-operator-declared:
    description: >-
      Every OpenAPI in this repository must declare info.x-operator. A university is a
      federation of buyers; without an explicit operator an artifact silently credits the
      institution for whatever it describes.
    message: info.x-operator MUST be set (institution | tenant)
    severity: error
    given: $.info
    then:
      field: x-operator
      function: truthy

  oxford-operator-is-institution:
    description: >-
      Only institution-operated contracts are saved under this slug. A tenant relationship is
      recorded in apis.yml as an apis[] entry, never as a saved contract; a vendor's generic
      contract belongs in the vendor's own repository.
    message: info.x-operator MUST be "institution" for a contract stored in openapi/
    severity: error
    given: $.info.x-operator
    then:
      function: pattern
      functionOptions:
        match: "^institution$"

  oxford-operator-evidence-required:
    description: >-
      The operator claim must carry the evidence that settles it — a registrable domain, a DNS
      resolution, or a self-reported origin host in the API's own response.
    message: info.x-operator-evidence is required alongside info.x-operator
    severity: error
    given: $.info
    then:
      field: x-operator-evidence
      function: truthy

  oxford-provenance-block-required:
    description: >-
      Every document must state how API Evangelist came to hold it. An unmarked artifact is
      credited to the institution as if they published it.
    message: info.x-provenance is required
    severity: error
    given: $.info
    then:
      field: x-provenance
      function: truthy

  oxford-provenance-method-controlled:
    description: Provenance method must come from the controlled vocabulary.
    message: info.x-provenance.method MUST be one of searched|generated|derived|probed|none
    severity: error
    given: $.info.x-provenance
    then:
      field: method
      function: pattern
      functionOptions:
        match: "^(searched|generated|derived|probed|none)$"

  oxford-provenance-source-required:
    description: The provenance source must name the URLs the document was derived from.
    message: info.x-provenance.source is required
    severity: error
    given: $.info.x-provenance
    then:
      field: source
      function: truthy

  oxford-no-vendor-servers:
    description: >-
      No server in an Oxford-attributed contract may point at a shared vendor platform. This is
      the check that would have caught the June 2026 cohort's Figshare misattribution.
    message: server url MUST NOT be a vendor platform host
    severity: error
    given: $.servers[*].url
    then:
      function: pattern
      functionOptions:
        notMatch: "(figshare\\.com|elsevierpure\\.com|exlibrisgroup\\.com|instructure\\.com|corehr\\.com|symplectic\\.co\\.uk|dataverse\\.org|openrepository\\.com)"

  oxford-servers-on-institution-domain:
    description: >-
      Every server must sit under a University of Oxford registrable domain. Oxford's surfaces
      live on ox.ac.uk and its subdomains; anything else is somebody else's engineering.
    message: server url MUST be under an ox.ac.uk host
    severity: error
    given: $.servers[*].url
    then:
      function: pattern
      functionOptions:
        match: "^https://([a-z0-9-]+\\.)*ox\\.ac\\.uk(/.*)?$"

  oxford-no-placeholder-servers:
    description: Placeholder, staging and localhost hosts are never a surface.
    message: server url MUST NOT be a placeholder or non-production host
    severity: error
    given: $.servers[*].url
    then:
      function: pattern
      functionOptions:
        notMatch: "(example\\.|localhost|127\\.0\\.0\\.1|staging|\\.test$|\\.invalid)"

  # --- INFO / METADATA -------------------------------------------------------
  oxford-info-contact-required:
    description: >-
      Oxford's endpoints self-report real administrative contacts (ora-dev@bodleian.ox.ac.uk,
      ota@bodleian.ox.ac.uk). Carry them.
    message: info.contact is required
    severity: error
    given: $.info
    then:
      field: contact
      function: truthy

  oxford-info-description-substantive:
    description: >-
      Descriptions must be substantive enough to state what the surface is and whether Oxford
      documents it.
    message: info.description is required
    severity: error
    given: $.info
    then:
      field: description
      function: truthy

  oxford-info-version-required:
    message: info.version is required
    severity: error
    given: $.info
    then:
      field: version
      function: truthy

  # --- OPERATIONS ------------------------------------------------------------
  oxford-operation-id-required:
    message: Every operation MUST declare an operationId
    severity: error
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: operationId
      function: truthy

  oxford-operation-id-camel-case:
    message: operationId MUST be camelCase
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].operationId
    then:
      function: casing
      functionOptions:
        type: camel

  oxford-operation-tagged:
    description: >-
      Every operation carries exactly the tags of its own surface. Tag hygiene matters here for
      a specific reason: refine-openapis splits by tag, and the June 2026 cohort turned one
      vendor contract into eleven apparent APIs that way.
    message: Every operation MUST be tagged
    severity: error
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: tags
      function: truthy

  oxford-operation-summary-required:
    message: Every operation MUST have a summary
    severity: warn
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: summary
      function: truthy

  oxford-operation-has-200:
    message: Every operation MUST document a 200 response
    severity: error
    given: $.paths[*][get,post,put,patch,delete].responses
    then:
      field: '200'
      function: truthy

  # --- HONESTY ABOUT WHAT OXFORD DOES NOT OFFER -----------------------------
  oxford-no-invented-security:
    description: >-
      No Oxford institution-operated surface authenticates. A security scheme appearing in one
      of these documents would be an invention, not a finding.
    message: components.securitySchemes MUST NOT be present — no Oxford surface authenticates
    severity: error
    given: $.components
    then:
      field: securitySchemes
      function: undefined

  oxford-no-global-security:
    message: A root-level security requirement MUST NOT be declared
    severity: error
    given: $
    then:
      field: security
      function: undefined