asyncapi: 3.0.0 # switchboard — SSE event schema. # The `/events` endpoint (defined as an HTTP GET in docs/specs/openapi.yaml) is a # text/event-stream. This document specifies the *messages* that flow over it: the named # SSE events the browser subscribes to via htmx-ext-sse, and their payload shapes. # # Direction: the application (server) SENDS; the browser (and any other SSE client) RECEIVES. # The `x-sse-event-name` on each message is the value of the SSE `event:` field an # `hx-ext="sse"` element targets with `sse-swap="..."`. # # Payload shapes intentionally mirror docs/specs/openapi.yaml #/components/schemas so the # HTTP, SSE, and MCP (docs/specs/mcp-tools.md) surfaces never drift. info: title: switchboard SSE stream version: 0.1.0 description: | Server-Sent Events broadcast to the local web UI when switchboard accepts a new event or when the status strip needs refreshing. Transport is SSE over the HTTP `/events` endpoint; reconnection is standard SSE (the server advertises `retry:` = `sse_retry_ms`, and clients may resume with `Last-Event-ID`). license: name: MIT url: https://opensource.org/license/mit defaultContentType: application/json servers: local: host: 127.0.0.1:8080 protocol: http description: | Loopback-bound default (MVP). The SSE stream is served at `GET /events` (see openapi.yaml). If exposed on the LAN it MUST sit behind Caddy forward_auth (ADR-0001). channels: events: address: /events title: Live event stream (SSE) description: | A single `text/event-stream`. The server emits named SSE events (`event-received`, `status`) and periodic keep-alive comments. DOM elements subscribe to individual event names via `htmx-ext-sse` (`sse-swap="event-received"`, `sse-swap="status"`). messages: eventReceived: $ref: '#/components/messages/EventReceived' statusSnapshot: $ref: '#/components/messages/StatusSnapshot' bindings: http: # SSE has no dedicated AsyncAPI binding; it is GET-established and long-lived. method: GET bindingVersion: '0.3.0' operations: broadcastEventReceived: action: send title: Broadcast a newly accepted event description: | Emitted once per accepted event (from ANY ingestion path — signed HTTP, generic HTTP, or the Redis consumer) immediately after it is persisted. The log screen prepends a row; the dashboard updates its per-provider counters. channel: $ref: '#/channels/events' messages: - $ref: '#/channels/events/messages/eventReceived' broadcastStatus: action: send title: Broadcast a status-strip snapshot description: | Emitted on a heartbeat interval and whenever ingestion activity transitions the connection indicator between `receiving` and `idle`. Drives the dashboard health strip. channel: $ref: '#/channels/events' messages: - $ref: '#/channels/events/messages/statusSnapshot' components: messages: EventReceived: name: event-received title: New event accepted summary: A webhook/queue event was accepted and persisted. contentType: application/json x-sse-event-name: event-received payload: $ref: '#/components/schemas/EventSummary' examples: - name: githubPush payload: id: 4213 provider: github event_type: push trust_mode: signed verified: true payload_size: 8421 received_at: '2026-07-05T18:03:21.442Z' - name: dockerhubUnverified payload: id: 4214 provider: 'generic:dockerhub' event_type: null trust_mode: token verified: false payload_size: 512 received_at: '2026-07-05T18:04:02.101Z' StatusSnapshot: name: status title: Status-strip snapshot summary: Connection state + per-provider recent counts and last-seen timestamps. contentType: application/json x-sse-event-name: status payload: $ref: '#/components/schemas/StatusSnapshot' examples: - name: receiving payload: connection: receiving generated_at: '2026-07-05T18:04:05.000Z' providers: - name: github trust_mode: signed enabled: true recent_count: 12 last_event_at: '2026-07-05T18:03:21.442Z' - name: 'generic:dockerhub' trust_mode: token enabled: true recent_count: 1 last_event_at: '2026-07-05T18:04:02.101Z' - name: 'redis:deploys' trust_mode: queue enabled: true recent_count: 3 last_event_at: '2026-07-05T17:58:40.000Z' schemas: TrustMode: type: string description: How the event's authenticity is established (ADR-0003). enum: [signed, token, open, queue] EventSummary: type: object description: | Compact event row. Identical to openapi.yaml #/components/schemas/EventSummary and to the MCP `EventSummary` (mcp-tools.md). No payload/headers on the stream — the detail view / MCP `get` fetches those on demand. required: [id, provider, trust_mode, verified, payload_size, received_at] properties: id: { type: integer, format: int64 } provider: { type: string } event_type: type: [string, 'null'] trust_mode: $ref: '#/components/schemas/TrustMode' verified: type: boolean description: true ONLY for a signed provider whose signature passed. payload_size: type: integer description: Raw body size in bytes. received_at: type: string format: date-time StatusSnapshot: type: object description: Dashboard health-strip payload. required: [connection, generated_at, providers] properties: connection: type: string enum: [receiving, idle] description: '`receiving` if events arrived within the activity window, else `idle`.' generated_at: type: string format: date-time providers: type: array items: type: object required: [name, trust_mode, enabled, recent_count] properties: name: { type: string } trust_mode: { $ref: '#/components/schemas/TrustMode' } enabled: { type: boolean } recent_count: type: integer description: Count of events in the recent window (e.g. last 24h). last_event_at: type: [string, 'null'] format: date-time