The monorepo's event stream (JSON pages, or a WebSocket)
Every change a screen shows is an event, written in the same step as
the change itself: a seq, a kind, and the changed row in its REST
shape, with the client-side collection, key and op (upsert or
delete) it applies to, so a client store (TanStack DB) can apply it
directly. Every event is kept; the one limit is the monorepo object's
storage (10 GB per Durable Object; an event is 0.3 to 1.3 KB).
Kinds, by collection:
-
branches:
branch.opened,push,branch.updated,merged(upsert),branch.deleted(delete). The row has the branch's stored fields only (name, head, author, title, description, state and the merge facts) and the event sayspartial: true:ahead,behind,base,mergeableandchecksare computed per request, so refetch getBranch for them. -
default_heads:
push(the first push of main) anddefault_head.updated(a merge). Row{repo, default_branch, default_head}. When main moves, every open branch's ahead, behind and mergeable change: refetch the branch list. -
statuses:
status(Status row). -
ci_builds:
ci.build(CiBuild row, upsert), on the build's repo. -
ci_jobs:
ci.job(CiJob row, upsert; key<build>/<job>), on the build's repo. -
ci_connections:
ci.connection(CiConnection row; delete on disconnect, row{id}). -
tags:
tag.created(a Tag row with itsrepo; also a moved tag),tag.deleted(delete, row{repo, name}). -
releases:
release.created,release.updated(also a file added or deleted),release.published(a Release row),release.deleted(delete, row{repo, tag}). -
reviews:
review(Review row). -
artifacts:
artifact.added,artifact.versioned(an Artifact row),artifact.deleted(delete, row{repo, branch, id, name}); each carriesbranch. -
artifact_comments:
artifact_comment.added,.edited,.resolved,.reopened(an ArtifactComment row; the thread's root for a state change; a body over 16 KB is null), andartifact_comment.deleted(delete with{id, artifact, thread}, or an upsert with the body null for a root that keeps its replies); each carriesbranch. -
worktrees:
worktree.reported(upsert),worktree.removed(delete). -
meta_files:
meta.saved(a meta history entry, no bytes; not sent during pando init's first upload). -
presence:
machine.connected,machine.disconnected. Row{connections: [...]}, the whole list of who is connected now (as in meta/status), plusconnection, the one that changed. -
machines:
machine.updated(upsert, a Machine row; key<login>/<credential_id>). Sent at once for a new machine, a connect, a disconnect or a change of its worktrees; for activity alone at most once a minute per machine (every activity event already names itsactorandmachine). -
issues:
issue.opened,issue.commented,issue.closed,issue.reopened,issue.updated(everything else: title, body, project, labels, assignees, links, reactions, a comment edited or deleted, a merge naming a closed issue). Key<n>, withnumber,row(the IssueRow),body_version,items(the new timeline items; a comment's body is null over 16 KB), and when they applyremoved(item ids),comment(a comment edited or whose reactions moved) andreactions(the issue's own). -
labels:
label.updated(upsert, a Label,fromits name before a rename; delete, row{name}).
Topics (?topics=, comma-separated, at most 10): all (default),
issues (every issue and label event), issue:<n> (one issue's;
also repo:<repo> for its project and its linked branches' repos),
main (every default_heads event), main:<repo>, repo:<repo>,
meta (meta_files and presence), kind:<kind>, feed (the
activity lines as the person's feed shows them, as
{"type": "feed", "n", "event": FeedEvent} frames: the home uses it
for a monorepo of more than 100 members, and a monorepo's pages may).
pando worktree follow needs only main:<repo>.
The feed topic's lines are numbered n in the order they go out
(a push's line goes out when its commit count is known, so n is not
seq order). caught_up then carries feed_n, the last number
sent. A socket that drops reconnects with feed_since=<the last n it saw> and first gets every line sent after it (at most 500, else one
{"type": "feed_resync", "feed_n"}: refetch, then reconnect with that
feed_n).
Presence (the home's machines_active and worktrees_active): a
follower sends, after the socket opens and whenever a worktree is
added or removed on its machine,
{"type": "hello", "machine": "<machine>", "worktrees": ["<worktree id>", ...]}:
the ids it reported with putWorktree for its branch worktrees in this
monorepo (not read-only mains), at most 1,000, each 1 to 64 of
A-Z a-z 0-9 _ -. machine is optional (default: the credential's
name). Each hello replaces the last one of that socket; presence ends
when the socket closes. The hello also marks the socket's credential
connected in listMachines, with these worktrees. Answer: {"type": "hello_ok", "machine", "worktrees", "machines_active", "worktrees_active"}, or
{"type": "error", "message"} (the socket stays open).
Without Upgrade: JSON, ?since=<seq>&limit=<n> (at most 500), or the
latest limit with no since, oldest first either way. The pull also
takes before=<seq> (only events before it: page back through the
log with the first event's seq), repo=<repo> (that repo's events;
meta for the meta layer's) and kinds=<kind>,<kind> (at most 20),
each answered from an index on (repo, seq) or (kind, seq), and with
topics (matched over at most 5,000 rows). With Upgrade: websocket: events after
?since= (at most 500, else one {"type": "resync", "seq", "oldest_seq"} frame: refetch, then resume from seq), then
{"type": "caught_up", "seq"}, then live {"type": "event", "seq", ...}
frames. The text ping gets pong. Close code 4404: the monorepo was
deleted. Close code 4403: you no longer have access to it (an owner
removed you, you left the org that owns it, or a transfer moved it
away from that org); do not reconnect.
Numbers and shas: seq, oldest_seq are stream sequence numbers. In
an event, head and before (push and default_head events) are commit
shas. Set EVENT_STREAM false in
worker/src/monorepo/stream.ts and it answers 501 event_stream_on_hold.
https://app.gitpando.com/v1/monorepos/{owner}/{monorepo}/events