add realtime
go tool gpsystem add realtime
Adds cmd/realtime: a dedicated server that holds client streaming connections (WebSocket, with an HTTP-streaming/SSE fallback) and delivers messages published through notify/broadcast from any process. It carries no business dependencies, so it scales freely and independently of the API/worker processes; see Realtime for the architecture and scaling story.
Explicit, retrofit-style command, not a new project default: like add worker, it is opt-in and adds a Traefik-exposed service.
What it generates
func main() {
cfg := envconf.MustLoad[config.Config]()
ctx := context.Background()
err := realtime.Run(ctx, cfg.Realtime, func(topics *realtime.TopicAuth) error {
// Authorize shared topic channels here, e.g.:
// topics.Allow("announcements", func(id *rbac.Identity) bool { return true })
// gpsystem:topics
return nil
},
// gpsystem:closers
)
if err != nil {
log.Fatal(err)
}
}
Realtime realtime.Config
realtime.Config reuses REDIS_* (queue.Config) and JWT_* (auth.Config); no new secrets to manage. See the Realtime page for the full REALTIME_* table.
compose.yml and .env.example
Adds a realtime service (path-routed under /realtime on the project's existing APP_HOST, so no new DNS entry or CORS case): no container_name (so --scale works), a Traefik health-check label pointed at /realtime/healthz (so a draining instance is evicted from the load-balancer pool), responseForwarding.flushInterval=-1 (so streamed bytes flush immediately through the proxy).
realtime:
build: { context: ., dockerfile: Dockerfile, target: development, args: { CMD: realtime } }
depends_on:
redis:
condition: service_started
stop_grace_period: 30s
labels:
- traefik.http.routers.shopRealtime.rule=Host(`${APP_HOST}`) && PathPrefix(`/realtime`)
- traefik.http.services.shopRealtime.loadbalancer.healthCheck.path=/realtime/healthz
REALTIME_LISTEN_ADDR=:3000
REALTIME_DRAIN_TIMEOUT=20s
REALTIME_CHANNEL_PREFIX=<project>
REALTIME_MAX_CONNECTIONS=0
Scale it independently of every other service:
docker compose up -d --scale realtime=3
Authorizing shared topics
Every connection is server-side subscribed to its own user:<subject> channel automatically; no code needed for that. Shared topics are opt-in: register a callback in cmd/realtime/main.go at the // gpsystem:topics anchor, and a client subscribes to it by name. A topic with no registered Allow is never subscribable; see Realtime.
Wiring a notification to it
add realtime only wires the gateway itself. Run it before your first add notification to have the broadcast channel wired into the shared notify.Sender automatically; if notifications already exist, add notify.ChannelBroadcast to their Via and the broadcast construction by hand (see the note on add notification).
Older projects
Like add worker, add realtime writes at the gpsystem:imports/gpsystem:config anchors in platform config and the gpsystem:services/gpsystem:env anchors in compose.yml/.env.example; on a project predating those files' anchors, it prints the block to paste instead of failing.