CosmoStreams
Cosmo Streams moves subscription infrastructure into the Router, so your services only publish events and respond to HTTP requests. No connection management required.
The Problem
Without Cosmo Streams, each service that supports subscriptions manages its own connection state, WebSocket servers, and subscriber tracking. Scaling and infrastructure choices become constrained by persistent connections.
See capabilitiesEach subgraph that serves subscriptions holds long-lived connections from the Router, one per distinct set of forwarded auth headers (so often one per user), plus subscriber tracking and state.
Subscriber count influences how services scale. As connections grow, every participating service must account for the persistent load.
Persistent connections limit infrastructure choices. Many compute platforms aren't designed to maintain long-lived connections.
How It Works
A service publishes an event when data changes. No WebSocket server required.
The Router receives the event from Kafka, NATS, or Redis.
Events carry the entity type and key; the Router resolves the remaining fields from your subgraphs.
The Router delivers the result to matching subscribers.
Capabilities
Move connection state to the Router. Keep services HTTP-based and stateless.
No WebSocket servers in subgraphs
Subgraphs handle HTTP requests and publish events. They do not manage persistent client connections. Cosmo Streams moves connection management to the Router.
Compatible with serverless
Services remain request-based, so they can run on Lambda, Cloud Run, and other short-lived compute platforms. The Router maintains client connections.
Works with your existing broker
The Router connects to Kafka, NATS (including JetStream), or Redis. No additional messaging layer required. Your services publish events with the entity __typename and key; Cosmo Streams exposes them as GraphQL subscriptions and resolves the other fields from your subgraphs.
Subscription logic centralized
Filter events in the schema with @openfed__subscriptionFilter, or write a Router custom module for per-subscriber decisions. The Router does not check whether a client may subscribe to a topic. Add that check in a SubscriptionOnStart handler.
Brokers
Each provider is configured under events.providers in the Router config and referenced by ID from the schema directives.
From the docs
~150-200MB
Memory for 10,000 idle clients on one Router, with 0% CPU while no messages are published.
1 trigger
Subscriptions on the same topic share one trigger. It shuts down when the last of them unsubscribes.
0
WebSocket servers in your subgraphs. The Router fetches fields from subgraphs over plain HTTP.
5
Client subscription protocols: graphql-ws, Server-Sent Events, multipart HTTP, and the legacy subscriptions-transport-ws and absinthe.
The Router uses epoll and kqueue where the OS supports them. Read how in Scaling GraphQL Subscriptions in Go, and how the Router handles subscriptions in Native Subscriptions with Cosmo Router.
Filtering and authorization
Handlers are Router custom modules written in Go. SubscriptionOnStart, OnReceiveEvents, and OnPublishEvents need Router 0.266.0 or later, SubscriptionOnCreate needs 0.327.0, and BeforeEventsDispatch needs 0.336.0. The Router does not authorize subscribers against topics on its own.
@openfed__subscriptionFilterDeclarative filter in the schema. Supports IN, combined with AND, OR, and NOT.
SubscriptionOnStartRuns once when a client subscribes. Use it to check permissions, rate-limit, or send an initial event.
OnReceiveEventsRuns when events arrive from the broker. Use it for per-subscriber decisions.
BeforeEventsDispatchRuns once per batch of events, before dispatch to any subscriber.
OnPublishEventsRuns before the Router sends events to the broker.
SubscriptionOnCreateRuns before a subscription is registered and can change its event configuration. Experimental.
When to Use Cosmo Streams
Serverless, event-driven backends, or per-user subscription filtering. Cosmo Streams centralizes connection management in the Router.
Serverless
Your services run on Lambda, Cloud Run, or similar. Cosmo Streams lets services remain request-based while the Router maintains connections to clients.
Event-driven
Services publish to Kafka, NATS, or Redis. Cosmo Streams exposes events that carry the entity type and key as GraphQL subscriptions, without adding subscription infrastructure to each service.
Per-user data
The same subscription should return different results by user. Cosmo Streams filters events per subscriber in the Router, and a custom module can authorize each subscription when it starts.
FAQ
A client subscribes. The Router subscribes to the matching NATS subject, Kafka topic, or Redis channel. When an event arrives, custom module handlers run if you have them, the Router resolves the rest of the GraphQL selection from your subgraphs over HTTP, and it streams the result to each matching subscriber.
All three support subscribe and publish. NATS also supports Request/Reply, and JetStream stream and consumer configuration, so messages sent while a subscription is inactive can be consumed later, as long as the consumer still exists. Kafka supports SASL/PLAIN and SASL/SCRAM authentication, and topics must already exist. Redis supports pub/sub channels and the Redis Cluster client, and Valkey works in its place.
No. The Router holds client connections and connects to the broker. Subgraphs publish events and answer plain HTTP requests, so they stay stateless and can run on Lambda or Cloud Run.
Filter with the @openfed__subscriptionFilter directive (IN, combined with AND, OR, and NOT) or an OnReceiveEvents custom module. The Router does not check whether a client may subscribe to a topic. Write that check in a SubscriptionOnStart handler, which runs once when a client subscribes. SubscriptionOnStart needs Router 0.266.0 or later.
The docs report 10,000 clients connected to one Router using about 150-200MB of memory, with 0% CPU while idle. Subscriptions on the same topic share one trigger.
By default the Router starts anyway. If a NATS or Redis broker is unreachable, it logs the problem and disables only the fields backed by that provider, and the rest of the graph keeps serving. Those fields recover without a restart once the broker is reachable. The Kafka client connects lazily, so an unreachable Kafka broker does not stop the Router from starting. Set skip_unavailable_providers: false to make the Router fail to start when a provider is not defined, or when a NATS or Redis broker is unreachable.
No. Cosmo connects to your existing Kafka, NATS, or Redis setup.
Yes. Cosmo Streams was called Event-Driven Federated Subscriptions (EDFS). The broker directives still use the edfs__ prefix, for example @edfs__natsSubscribe.
More in the Cosmo Streams docs. Subscriptions run on the Cosmo federation router.
Get started
Cosmo Streams moves subscription infrastructure into the Router. Your services stay HTTP-only.