Transmit — Server-Sent Events
Transmit provides Server-Sent Events (SSE) for real-time server-to-client push. Subscribe to channels with authorization, get lifecycle hooks, and scale to multiple instances with Redis transport. Included by default when creating a new app with nimbus new.
§ Overview
For bidirectional WebSockets and channel subscriptions, use the Reverb plugin instead of Transmit.
§ Installation
$ nimbus plugin:install transmit
Or add manually:
import "github.com/CodeSyncr/nimbus/plugins/transmit"
app.Use(transmit.New(nil))
§ Configuration
| Environment Variable | Description | Default |
|---|---|---|
TRANSMIT_PATH | Route prefix for SSE endpoints | __transmit |
TRANSMIT_PING_INTERVAL | Keep-alive ping frequency (e.g. 30s, 1m) | disabled |
TRANSMIT_TRANSPORT | Multi-instance transport (redis) | none (in-memory) |
REDIS_URL | Redis connection URL for transport | redis://localhost:6379 |
§ Auto-Registered Routes
| Method | Route | Purpose |
|---|---|---|
GET | /__transmit/events | Establish SSE connection, receive UID |
POST | /__transmit/subscribe | Subscribe to a channel |
POST | /__transmit/unsubscribe | Unsubscribe from a channel |
§ Broadcasting
Push data to all subscribers of a channel from anywhere in your application:
import "github.com/CodeSyncr/nimbus/plugins/transmit"
func (ctrl *ChatController) Send(c *http.Context) error {
message := c.Body("message")
userID := auth.User(c).ID
data := map[string]any{
"message": message,
"user_id": userID,
"sent_at": time.Now(),
}
// Broadcast to all subscribers
transmit.Broadcast("chats/1/messages", data)
// Broadcast to all EXCEPT the sender (exclude by UID)
transmit.BroadcastExcept("chats/1/messages", data, c.Get("transmit_uid"))
return c.JSON(data)
}
You can also query subscribers:
// Get all subscriber UIDs for a channel
subscribers := transmit.GetSubscribers("chats/1/messages")
fmt.Printf("Active listeners: %d\n", len(subscribers))
§ Channel Authorization
Guard channels with pattern-based authorization. The pattern uses :param syntax to extract dynamic segments:
import "github.com/CodeSyncr/nimbus/plugins/transmit"
// Private user channel — only the owner can subscribe
transmit.Authorize("users/:id/notifications",
func(ctx *http.Context, params map[string]string) bool {
user := auth.User(ctx)
return user != nil && fmt.Sprint(user.ID) == params["id"]
},
)
// Team channel — check membership
transmit.Authorize("teams/:teamId/updates",
func(ctx *http.Context, params map[string]string) bool {
user := auth.User(ctx)
if user == nil {
return false
}
return models.IsTeamMember(user.ID, params["teamId"])
},
)
// Public channel — no authorization needed (default behavior)
// Channels without an Authorize rule are open to all subscribers
How it works: When a client POSTs to /__transmit/subscribe, the channel name is matched against registered authorization patterns. If a matching pattern is found, the callback decides whether to allow the subscription. Channels without any authorization rule are public.
§ Lifecycle Hooks
React to connection events for analytics, presence tracking, or cleanup:
import "github.com/CodeSyncr/nimbus/plugins/transmit"
// Client connects — receives a unique UID
transmit.OnConnect(func(uid string) {
log.Printf("Client connected: %s", uid)
// Track online users, increment stats, etc.
})
// Client disconnects (connection closed / network drop)
transmit.OnDisconnect(func(uid string) {
log.Printf("Client disconnected: %s", uid)
// Mark user offline, cleanup presence
})
// Client subscribes to a channel (after auth passes)
transmit.OnSubscribe(func(uid, channel string) {
log.Printf("UID %s subscribed to %s", uid, channel)
})
// Client unsubscribes from a channel
transmit.OnUnsubscribe(func(uid, channel string) {
log.Printf("UID %s unsubscribed from %s", uid, channel)
})
// Data broadcasted to a channel
transmit.OnBroadcast(func(channel string, payload any) {
log.Printf("Broadcast on %s: %v", channel, payload)
// Audit log, trigger side effects, etc.
})
§ Redis Transport (Multi-Instance)
By default, Transmit stores connections in-memory — so broadcasts only reach clients connected to the same server instance. For multi-instance deployments, enable the Redis transport:
TRANSMIT_TRANSPORT=redis
REDIS_URL=redis://localhost:6379
With Redis transport enabled:
- Broadcasts are published to a Redis Pub/Sub channel
- All connected instances receive and relay messages to local subscribers
- Subscribe/unsubscribe operations are synchronized across instances
- Each instance uses a unique
instanceIDto avoid echo
§ Client Setup — @codesyncr/echo
Install the official Nimbus real-time client SDK:
npm install @codesyncr/echo
Connect and subscribe to channels:
import { Echo } from '@codesyncr/echo'
const echo = new Echo({
baseURL: 'http://localhost:3333',
})
// Public channel
echo.channel('notifications')
.listen('NewMessage', (data) => {
console.log('New message:', data)
})
// Private channel (requires auth)
echo.private('projects.1')
.listen('RenderComplete', (data) => {
console.log('Render done:', data)
})
// Presence channel
echo.join('room.1')
.here((users) => console.log('Online:', users))
.joining((user) => console.log('Joined:', user))
.leaving((user) => console.log('Left:', user))
.listen('ChatMessage', (data) => console.log(data))
// Connection events
echo.onConnect(() => console.log('Connected!'))
echo.onDisconnect(() => console.log('Disconnected'))
// Leave / disconnect
echo.leave('notifications')
echo.disconnect()
Echo Configuration
| Option | Type | Default | Description |
|---|---|---|---|
baseURL | string | — | Nimbus server URL |
path | string | __transmit | Transmit route prefix |
bearerToken | string | — | Bearer token for private channels |
csrfToken | string | — | CSRF token for POST requests |
autoReconnect | boolean | true | Auto-reconnect on disconnect |
reconnectDelay | number | 1000 | Reconnect delay (ms) |
maxReconnectAttempts | number | Infinity | Max reconnect attempts |
Manual protocol (without Echo):
- Connect to
GET /__transmit/events— receive your UID in the first SSE message - Subscribe:
POST /__transmit/subscribewith{"uid": "...", "channel": "chats/1"} - Receive events on the SSE stream as JSON payloads
- Unsubscribe:
POST /__transmit/unsubscribewith the same body
§ Production Notes
- Disable response compression for
text/event-streamin your reverse proxy (Nginx:proxy_buffering off; Traefik: disable compress middleware for SSE routes) - Enable
TRANSMIT_PING_INTERVAL=30sto keep connections alive through load balancers with idle timeouts - Use Redis transport when running more than one server instance
- Monitor active connections via
transmit.GetSubscribers()for capacity planning