Skip to content
GitmoapdocsBeta
Concept· Why does it work like that?Draft

Live streams

Overview

Three things can be watched as they change instead of polled: a merge request, a pipeline run's status, and a running step's log. Each is a stream of server-sent events you open with an ordinary authenticated request; it tells you within seconds when something moves, and a dropped connection resumes exactly where it left off.

A stream speeds up what polling would tell you; it does not replace it. Streams are refused at capacity and closed on a deadline by design, so keep a slower poll as the fallback and the worst case is a little latency rather than a stale page.

How it works

The three streams

WatchPathData eventIts dataIts idScope
A merge request/repositories/{repo_id}/merge-requests/{mr_id}/streamrevision{} — re-read the merge requestan opaque revisionmerge_request:read
A run's status/repositories/{repo_id}/pipeline-runs/{pipeline_run_id}/streamstatusthe run's whole statusthe run's version, such as v17pipeline:read
A step's log/repositories/{repo_id}/pipeline-runs/{pipeline_run_id}/nodes/{node_run_id}/logs/streamlinesan array of log recordsthe last line number in the framepipeline:read

The merge-request stream carries nothing on purpose: what a merge request page shows depends on who is reading it, and one stream's frames go to everyone watching. When its revision moves, re-read through the ordinary endpoints. It does not notice a push to the target branch, a pipeline result, or an edit to an existing comment's text, so poll for those.

Authenticating

The token is read only from the Authorization: Bearer header — never from the query string, never from a cookie. The browser's built-in EventSource cannot send headers, so it can open a stream only on a public resource without credentials. For anything else, read the stream with fetch and parse the frames yourself.

Frames and events

retry: 5000
id: v17
event: status
data: {"status":"running", …}
:ping
event: end
data: {}
You seeIt meansWhat to do
retry: 5000 on the first framethe reconnect delay the server suggests (merge-request and run-status streams)use it, or your own back-off
:ping every 15 secondsa keep-alive comment, so a quiet stream is not mistaken for a dead oneignore it
revision, status or linesthe resource movedact on it, and remember its id
endit is finished — the run ended, the step's log is complete, or the merge request was deletedstop; do not reconnect
resetthe stream cannot continue from here — you fell behind, your access changed, or the connection reached its deadlinere-read through the ordinary endpoint, then reconnect

A closed merge request is not finished, because it can be reopened, so its stream does not end when it closes.

Resuming

Send the id of the last event you received as the Last-Event-ID header when you reconnect. There is no session on the server: the id is the whole cursor. A merge-request or run-status stream opens without repeating what you already hold. A log stream resumes at the line after the one you name. On a first connection, from_line starts it at a line of your choosing; without either, it starts with the last 500 lines. The header wins when both are sent.

Status codes

StatusMeaningWhat to do
200a streamread it
204finished, and you already hold the last of it — the run's final version, or the step's last linestop; never retry
401no valid credentialsign in again or use a valid token
403, 404not allowed, or not therestop; the ordinary read reports the real error
503 with Retry-After: 30the server is watching as many merge requests or runs as it willwait at least that long, add jitter, and poll meanwhile

Every connection ends on purpose

A stream is authorized once, when it opens, so every connection has a deadline. A merge-request or run-status stream is closed after 10 to 12 minutes; a log stream after 30, and it re-checks your access every minute in between. Each ends with reset, which is not an error: reconnect with Last-Event-ID, and the reconnect checks your access again. Two people watching the same merge request are cut off at the same moment, so add jitter of your own to the reconnect.

A log stream also counts the bytes it sends toward your egress budget, and ends when that budget runs out.

From the CLI

shellscript
gitmoap pipeline-runs stream-status --repo-id acme/api --pipeline-run-id <run-id>

gitmoap mr stream, gitmoap pipeline-runs stream-status and gitmoap pipeline-runs stream-node-run-logs print frames as they arrive. Replace <run-id> with the run's id. Add --last-event-id with the last id you saw to resume.

  • Pipelineswatching a run, following a step's log, and the status poll to fall back on
  • Merge requestswatching a merge request, and what its stream does not see
  • Cachingthe ETag that makes the fallback poll cheap
  • Errorsthe 503 and when to retry