AsyncAPI — SSE stream
The Server-Sent Events schema for the live web UI: the event-received and status messages the dashboard and log screens subscribe to.
⬇ Download the raw asyncapi.yaml · view on GitHub
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://github.com/joestump/switchboard/blob/main/LICENSE
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