did-btcr2-js

ADR 030: Fetch-Based SSE over Native EventSource

Status: Accepted

Date: 2026-04-22

Branch / PR: aggregation/http-transport Depends on: ADR 028

Context

Both the broadcast advert stream and the per-DID inbox are Server-Sent Events (SSE) endpoints. The standard browser API for SSE is EventSource, which:

The inbox stream at GET /v1/actors/{did}/inbox requires authentication: otherwise any actor can subscribe to any DID’s inbox and observe metadata about who’s participating in which cohorts. That authentication needs to commit to at least (did, timestamp, nonce, path) and be signed by the DID’s key.

The missing-headers restriction on EventSource forces auth credentials into the URL, which is problematic:

Options considered

  1. Native EventSource + signed query parameter. Works everywhere but leaks credentials to logs.
  2. Native EventSource + cookie-based auth. Requires server-side sessions (rejected by ADR 029’s stateless model).
  3. Fetch + ReadableStream + manual SSE frame parsing. Headers work; we own ~40 lines of parser.
  4. WebSocket with an auth subprotocol. Full duplex; loses standard SSE tooling (proxies, curl compatibility).

Decision

Option 3. The HttpClientTransport implements SSE using fetch() + ReadableStream + a small parseSseStream async generator. Inbox subscribe requests carry auth in an Authorization: BTCR2-Sig … header. Automatic reconnection uses exponential backoff (default 1s to 30s with 20% jitter).

The parser is ~80 LOC in sse-stream.ts and handles:

Consequences

Positive

Negative

Explicitly accepted trade-offs

References