Event streaming over HTTP
HTTP-native event streams
Durable, ordered streams you talk to with plain HTTP. Publish a batch with
POST, subscribe with a long-lived GET. No WebSocket
upgrade, no custom wire protocol, no broker to run.
curl -N https://runnev.dev/v1/streams/DQCXqM5x6cySlmtvACNZIm \
-H "Accept: text/event-stream" \
-H "Authorization: Bearer rnv_test_2f8c41a6d90b47e3ba15c7e08d3f6a24"
The whole model in three requests
A stream is an ordered, durable log addressed by a base62 id. Publishers append batches at a sequence number. Subscribers hold one HTTP response open and receive batches as server-sent events, each tagged with its sequence so they can resume exactly where they left off.
There is no broker to operate and nothing to install on the server side of your infrastructure. If it can make an HTTP request, it can use Runnev.
curl https://runnev.dev/v1/streams \
-H "Authorization: Bearer $RUNNEV_API_KEY" \
-d '{"name":"orders-eu","retention_seconds":86400}'
curl https://runnev.dev/v1/streams/cBczepiZSW8GJaCae0xIj7/41823 \
-H "Authorization: Bearer $RUNNEV_API_KEY" \
-d '{"events":[{"type":"order.paid","id":"o_5521","amount":1999}]}'
# 202 {"seq":41823,"accepted":1,"cursor":41823,"duplicate":false}
curl -N "https://runnev.dev/v1/streams/cBczepiZSW8GJaCae0xIj7?cursor=41800" \
-H "Accept: text/event-stream" \
-H "Authorization: Bearer $RUNNEV_API_KEY"
A live stream, right now
This is the public demo stream: a read-only feed of synthetic order events that our
own test harness publishes around the clock. The connection below is a single
long-lived GET held open in your browser, reconnecting from its last
cursor if it drops.
- 41802 11:02:07.114 128 events order.paid eu-west
- 41803 11:02:09.881 116 events order.created us-east
- 41804 11:02:12.640 131 events order.paid ap-south
- 41805 11:02:15.402 124 events order.refunded eu-west
- 41806 11:02:18.219 119 events order.created eu-central
The demo key is public and rate limited to 60 requests per minute per IP. It can only read this one stream. See Subscribing for the wire format.
Why not WebSockets
WebSockets are a good fit when you control both ends and the network between them. Event delivery to the open internet is a different problem, and the upgrade handshake is the part that breaks.
Intermediaries drop upgrades
Corporate forward proxies, some enterprise firewalls, and a few CDN
configurations refuse or mishandle the Upgrade: websocket
handshake. A plain HTTP request passes through the same hops untouched.
Serverless can't hold a socket
Function runtimes bill and time out on wall-clock duration and were never meant to pin a raw socket open. They can, however, stream an HTTP response body, which is all a subscriber needs.
Mobile radios kill idle sockets
Carriers reap idle connections aggressively to save radio power. A stream with a periodic keepalive frame and cheap cursor-based resume survives a flaky link far more gracefully than a socket that must be renegotiated from scratch.
HTTP/2 multiplexing is free
Over HTTP/2 and HTTP/3, many concurrent streams share one connection with header compression and flow control you did not have to build. One subscriber to forty streams is forty logical streams on a single socket.
A healthy Runnev subscriber holds a single response open for as long as it needs, and a
busy publisher sends thousands of small POSTs a minute. Both are
normal, supported operating modes. The platform, the SDKs, and the docs are built
around long-lived connections and high write rates rather than treating them as
edge cases.
What you get
Ordered, durable streams
Every batch has a monotonic sequence. Streams are retained for a window you configure, in time and in bytes.
Cursor resume & replay
Reconnect with ?cursor=N and continue exactly after sequence N.
Replay any single batch by sequence.
Raw binary batches
Publish application/octet-stream and we never look inside. Bring
protobuf, msgpack, Avro, or your own framing.
Idempotent publishes
Writes are keyed on (stream, seq). Retry a publish and the second
attempt returns duplicate: true instead of a second copy.
HTTP/2 and HTTP/3
Delivered over modern HTTP on the edge, with the older HTTP/1.1 streaming path kept working for clients that need it.
Three SDKs, no broker
Zero-dependency clients for JavaScript, Python, and Go. Nothing to deploy, no queue to babysit, no ZooKeeper.
What Runnev is not
Naming the limits up front saves everyone a support ticket. Runnev is a stream, not a work queue.
- No per-consumer acknowledgement. Subscribers track their own cursor. There is no server-side "this consumer processed batch N" and no redelivery to a specific consumer.
- No cross-stream ordering. Order is guaranteed within a single stream. Two events on two different streams have no defined relative order.
-
No exactly-once. Delivery is at-least-once. Because publishes are
idempotent on
(stream, seq), deduplication on the read side is a one-line check, and we document exactly how to do it. -
Batches cap at 8 MiB. A single publish body over 8 MiB
is rejected with
413. Split large payloads across sequences.
Questions
Do I need to run a broker or an agent?
No. Runnev is a hosted HTTP API. Your publishers make POST requests
and your subscribers make one long GET. There is nothing to install
on your side beyond an optional SDK, and the SDKs are thin wrappers over the same
HTTP calls.
How is this different from server-sent events on my own server?
The subscribe side is server-sent events; that is deliberate, because SSE is plain HTTP and travels everywhere. What Runnev adds is the durable, ordered log behind it: sequence numbers, cursor resume, replay, retention, and idempotent writes, so a subscriber that disconnects for an hour catches up instead of missing everything.
What happens when a subscriber falls behind?
Nothing happens to the publisher. Batches are buffered in the stream up to its retention window. A slow subscriber reads at its own pace; if it disconnects it resumes from its cursor. If it falls behind the retention window it skips the evicted batches, and the gap is visible as a jump in sequence numbers.
Can I publish binary data?
Yes. Create the stream in raw mode and publish
application/octet-stream bodies. We store and deliver the bytes
verbatim and never parse them. See
Publishing.
Are open subscribe connections billed?
No. Billing is on published events and egress bytes. Holding a subscription open, even for days, does not cost anything by itself. See Pricing.
Which languages are supported?
Any language with an HTTP client. We publish first-party SDKs for JavaScript, Python, and Go, and the API reference is complete enough to write your own client in an afternoon.