Realtime
Wahoo provides bounded Server-Sent Events (SSE) and WebSocket primitives. Generated realtime routes return 501 Not Implemented until the application adds policy.
Work in progress: Do not use Wahoo in production yet.
SSE Topics
Use a topic for tenant- or subject-scoped events. Authorize the request before you select the topic handler.
events := realtime.NewHub(
realtime.WithMaxClients(100),
realtime.WithMaxStreamAge(10*time.Minute),
)
topic, err := events.Topic("tenant:ten_123")
if err != nil {
return err
}
runtime.Handle("GET /events", authenticated(topic))
events.PublishTo("tenant:ten_123", realtime.Event{
ID: "evt_123",
Name: "project.updated",
Data: []byte(`{"project_id":"prj_123"}`),
})
The hub has a default 1000-client limit, 15-minute stream lifetime, 25-second heartbeats, one buffered event per client, and slow-client eviction. Event data is copied before delivery. SSE clears the normal HTTP write deadline for the selected long-lived response.
Publish is global. Use it only for intentionally global events. The hub is local to one Go process. Publish through a shared system when you run more than one instance, then deliver to each instance's authorized local clients.
Required Application Policy
- Authenticate the session before the SSE handler attaches.
- Resolve and authorize the tenant before selecting a topic.
- Apply per-principal and per-IP connection limits.
- Publish only tenant-scoped data.
- Bound event payload size before you publish it.
WebSockets
realtime.WebSocket keeps the request-host same-origin behavior from coder/websocket, sets a 1 MiB message limit, and applies a 15-minute connection context. It does not authenticate, rate limit, serialize writes, or provide shared fanout.
runtime.Handle("GET /ws", authenticated(realtime.WebSocket(handleSocket, &websocket.AcceptOptions{
OriginPatterns: []string{"app.example.com"},
})))
Do not use realtime.Echo() in a public service. Do not expose internal errors through WebSocket close reasons.
Test
go test -race ./...
Test authorization before connection setup, tenant isolation, client limits, idle timeouts, message-size limits, and write serialization.