# The envelope and delivery guarantees every Moonitoring event shares. Within v1 it only gains fields.
asyncapi: 3.0.0
info:
  title: Shared Event Envelope
  version: 1.0.0
  description: The envelope every Moonitoring event uses, the delivery guarantees every topic gives and the headers
    a webhook delivery carries. Referred to by the event contract of every system.
defaultContentType: application/cloudevents+json
channels: {}
operations: {}
components:
  schemas:
    EventEnvelope:
      type: object
      description: 'CloudEvents 1.0, JSON format. `type` is the event name with its version, `source` the system,
        `subject` the id of the entity concerned. The same envelope travels on Pub/Sub and in a webhook delivery;
        `data` is thin: ids and the few fields a consumer needs to decide whether to read more through the API.'
      properties:
        specversion:
          type: string
          const: '1.0'
        id:
          type: string
          description: 'Unique per event. A consumer ignores an id it has already processed: delivery is at least
            once.'
        source:
          type: string
          description: The producing system, e.g. //moonitoring.com/farm.
        type:
          type: string
          description: <entity>.<what happened>.v<major>, e.g. data_issue.opened.v1.
        subject:
          type: string
          description: The id of the entity the event is about.
        time:
          type: string
          format: date-time
          description: When the change was committed.
        datacontenttype:
          type: string
          const: application/json
        moonitoringorganization:
          type: string
          format: uuid
          description: 'Extension: the Organization the event concerns, when it concerns one. Also a Pub/Sub attribute,
            so subscriptions can filter by it.'
        moonitoringsubscription:
          type: string
          format: uuid
          description: 'Extension, set only by access on a test or a redelivery it publishes to platform.external.v1:
            the WebhookSubscription the event is for (decided 2026-11-05). Also a Pub/Sub attribute; every external
            subscription''s filter accepts its own id, so the event reaches that subscription only.'
        moonitoringwriteseq:
          type: integer
          description: 'Extension, farm events only: the write number that made the change available.'
        data:
          type: object
      required:
      - specversion
      - id
      - source
      - type
      - time
      - data
  messageTraits:
    Event:
      contentType: application/cloudevents+json
      headers:
        type: object
        description: Pub/Sub message attributes, so a subscription can filter without reading the body.
        properties:
          ce-type:
            type: string
          ce-source:
            type: string
          ce-id:
            type: string
          moonitoring-organization:
            type: string
            format: uuid
          moonitoring-subscription:
            type: string
            format: uuid
            description: Only on a test or a redelivery for one WebhookSubscription.
        required:
        - ce-type
        - ce-source
        - ce-id
    ExternalDelivery:
      contentType: application/json
      headers:
        type: object
        description: 'Headers of a Pub/Sub push delivery to a registered HTTPS endpoint (decided 2026-10-30, D9).
          The body is the Pub/Sub push envelope: `message.data` is the CloudEvent, base64; `message.attributes`
          carries ce-type, ce-source, ce-id and moonitoring-organization; `subscription` names the subscription.'
        properties:
          Authorization:
            type: string
            description: '`Bearer <OIDC token>` signed by Google for the delivery service account of the platform.
              The receiver checks the signature against Google''s keys, the issuer accounts.google.com, the `aud`
              equal to the audience shown on its subscription and the `email` of the delivery service account. No
              HMAC, no fixed sender addresses.'
        required:
        - Authorization
  channelBindings: {}
x-moonitoring-delivery:
  transport: Google Cloud Pub/Sub, one topic per system named <system>.events.v1; the ingestion work queue and the
    dead-letter topics are separate.
  wakeup: 'Nothing runs while there is no work (decided 2026-11-05): the backend publishes its outbox rows right after
    the commit, <system>-relay publishes what is left when Cloud Scheduler calls it every minute, and every
    subscription inside the platform pushes to its consumer, a Cloud Run service that scales to zero.'
  publication: 'Through the outbox: the event row is written in the same transaction as the change and relayed to
    Pub/Sub afterwards, so no change is saved without its event and no event is sent for a rolled-back change.'
  guarantee: At least once. Each consumer keeps the ids it processed and ignores a repeat. Order is not guaranteed
    between events, except on channels with an ordering key.
  retention: 7 days on the topic; a consumer that was down longer rebuilds its cache from the owning system's snapshot
    API, then resumes.
  dead_letter: Configure a delivery-attempt threshold of 5 to forward unacknowledged messages to
    <system>.events.dead.v1. Pub/Sub counts attempts and forwards on a best-effort basis, not at an exact
    attempt count or elapsed time. The subscription needs the required IAM permissions; platform monitoring
    must alert the owning team when failed deliveries reach the dead-letter subscription.
  compatibility: Events only gain fields. A breaking change is a new version, published alongside the old one.
  outside: 'Events marked x-moonitoring-webhook are deliverable outside the platform (decided 2026-10-30, D9): a
    relay of the system that owns them also publishes them to the topic platform.external.v1, where each registration of the Access Webhooks API
    is a Pub/Sub subscription filtered by Organization and event type. Push subscriptions call the customer''s HTTPS
    endpoint with a Google-signed OIDC token; pull subscriptions are read by the customer''s own Google service
    account. Pub/Sub retries with exponential backoff and forwards to a dead-letter topic according to the
    configured attempt threshold, on a best-effort basis. The platform must implement the policy to pause
    delivery and notify its owner after 24 hours of continuous failures. A retained dead-letter subscription
    and an application read/redelivery path are required for the proposed 30-day failure history; these are
    implementation requirements, not automatic Pub/Sub guarantees.'
