What is collected
Thirty-four families, ninety-five measurements. This page is what each family is for; the measurements reference is every tag and field.
The families are also grouped, and groups: in the configuration switches a
whole area on or off. The binary prints the grouping it actually uses, which is
the one to trust:
ghchronicle -groupsaccount the account itself: its lifetime numbers, its profile, its keys, its spending and what it does in other people's repositories account, achievements, billing, history, keys, outbound, profile, totalsaudience who is looking at the projects, who starred them and who copied them forks, stars, trafficci continuous integration and deployment: runs, jobs, steps, artifacts, caches and deployments actions, artifacts, deployments, joblogs...Audience
Section titled “Audience”traffic collects the only data GitHub genuinely throws away. Views and
clones live for exactly fourteen days and then cease to exist anywhere. The
whole window is re-read and rewritten on every sweep, each day stamped with its
own date, so a collector that was off for a week loses nothing as long as it
comes back inside the window.
Referrers and popular paths are different: the API returns a top-ten snapshot with no dates at all, so they are stamped at the start of the UTC day and read as “who was sending traffic when we asked”.
stars reconstructs the star curve from its beginning, from two endpoints.
GitHub’s daily star history gives the stars a repository gained on each day,
back to the week it was created, to anyone who can see the repository, so it is
read for every repository the sweep collects and written as gh_star_day: whole
on the first sweep, and after that one request for the newest thirty weeks, a
free 304 unless one of their days gained or lost a star or a new week began.
The daily star bars count it in every store but Prometheus, and in InfluxDB and
PostgreSQL so does the star curve, so the entire history is there after the
first run: a chart that goes back years, not one that starts the day the
collector was installed. GitHub Enterprise Server does not serve the history,
so there those panels stay empty. The stargazer list says who.
Asked with the star media type it returns a starred_at per user, written as
gh_star, walked in full once; after that only the newest hundred are read,
since new stars land at the end, and they are read for every repository at once
in one GraphQL query per ten. That is the difference between one point and 280
calls for a repository with 28,000 stars. GitHub has served the list only to a
repository’s admins and collaborators since July 2026, which the account always
is on its own repositories and on those of an organisation it administers; any
other repository has its stars counted per day and nobody named (see
what GitHub will not give).
forks collects who forked and when. The repository snapshot carries a
fork count, which says how many but never when or by whom. The list is walked
through REST on the first sweep of a fresh install and in a backfill; after
that the newest hundred of every repository ride in the same kind of batch
the stars do. A fork row is not static the way a star is: it carries the
fork’s own stars and how long since it was pushed, so a repository the batch
reports holding more than a hundred forks is walked through REST as well,
which refreshes those on up to five hundred forks as it always did.
Repositories
Section titled “Repositories”repo collects what a repository is right now, plus the things that
accumulate on GitHub’s side: languages by bytes, topics, community health, and
release downloads per asset, which is what tells a Linux build from a macOS
one. Bytes per language matter because the dominant-language label cannot show
a repository shifting from one language to another over time.
settings collects the configuration that changes, and how well the parts
of it that talk to the outside world are working. One thing here is a real time
series rather than a snapshot: webhook deliveries carry a status code and a
latency.
rulesets collects the changelog of every ruleset, one row per saved
version with an actor and a date, which is the only record GitHub keeps of the
moment a protection was turned off. The ruleset itself, what it enforces and
who may walk past it, is a daily snapshot in repo; this is the history that
snapshot’s days_since_change only summarizes.
branches collects the live branch list, one row per branch carrying the
age of its tip. Nothing else answers “which branches were abandoned”: GitHub’s
own list sorts by name and forgets, so a branch whose last commit is four
months old looks exactly like one pushed this morning. It deliberately does not
ask which pull requests point at a branch, which is what makes that query
expensive; the join belongs in the panel.
inventory collects the three per-repository policy surfaces that change on
a scale of months: what the GITHUB_TOKEN of a workflow is allowed to do, how
old every stored secret is, and whether code scanning is switched on by GitHub
rather than by a workflow of its own. Four core requests per repository a day.
All three are settings rather than events, so they are stamped at the start of
the UTC day and a change reads as the day the value moved.
policyfiles records which governance files a repository carries,
SECURITY.md, CODEOWNERS, dependabot.yml and FUNDING.yml, and when each
of them last changed. Whether most of them exist today is already in
gh_repo_policy; when they changed is nowhere else, and .github/dependabot.yml
is in no other measurement at all, which is what makes “is this repository
receiving dependency updates by either route” a question the data can answer.
Development
Section titled “Development”issues collects pull requests and issues one by one, not as counts. A
count of open pull requests says nothing about how the work actually goes; the
interesting numbers are durations. How long until someone reviewed it, how long
until it merged, how big the diff was, how many review rounds it took. One
GraphQL query per repository covers both: a sweep walks what was updated in
the last two cadences, ten at a time, and the first sweep of each UTC day
reaches back to the day’s read before it and also reads every open item,
whatever it was last updated, which is the only read that rewrites an open
pull request nobody touched.
commits collects the commit history with its size and its signature. This
is what replaces stats/code_frequency, which answers 202 with an empty body
forever on a personal account. GraphQL gives lines added and removed per
commit, attributed to an author and dated to the commit rather than to a week,
and the signature comes with it for the same query.
issueevents collects the transitions rather than the state. issues says
what a pull request ended up as; this says when it was labelled, closed,
reopened, renamed or had a review requested. A reopening exists in no other
measurement. The repository-level list, /issues/events, is the reference for
what an event is and how it is named, and it embeds the whole issue in every
event: about a megabyte per page of a hundred, of which the collector keeps
three per cent. So a sweep asks GraphQL for the timeline of the issues and
pull requests updated in its window, ten items a page because the gateway was
measured to drop timelines silently at twenty, and a backfill walks the
per-issue endpoint, /issues/{n}/events, which is the same rows without the
issue. Measured over a week of two repositories, the timeline agrees with the
list on every event of every type it can name, field for field; a pull
request in a stack is read through its own list because added_to_stack has
no timeline type, and the one thing the list sees that this does not is a
commit referencing an issue nobody has touched, three events in 2,217.
The three of them ask only where something moved. One GraphQL query per
twenty-five repositories first asks each when the head of its default branch
was committed and when its newest issue and its newest pull request were
updated, and a family asks nothing of a repository where nothing moved since
its own window: on the account this was measured on, about half of the issues
and issueevents queries and 96 per cent of the commits ones. The day’s read
of every open item of issues and a backfill are read everywhere.
Cost of a sweep has the count.
deps collects the dependency graph, off by default. Two shapes of the
same subject: the SBOM as a photograph, which is a licence histogram, and the
difference between two commits, which says what entered and left and what
advisory it carried. Only aggregates are stored, because one dependency bump is
three hundred and seventy changes and six rows say the same thing.
discussions collects the forum half of a repository. Discussions are
invisible to every issue and pull request endpoint, and an answered question is
a support cost that never appears in the issue numbers. A repository whose
forum is switched off is never asked: the listing that discovered it already
said so, and the query costs the same whether or not there is anything to page.
planning collects labels and milestones. A milestone is the only place
GitHub records an intention with a due date, and its completion percentage is
computed server side.
activity collects a repository’s own activity log. It is the only place a
force push is recorded: the public event feed does not distinguish one, and
nothing else says that a branch was created or deleted or that a merge was a
squash rather than a rebase. It is as perishable as traffic, a hundred entries
covered twenty-six hours on the busiest repository measured.
Continuous integration
Section titled “Continuous integration”actions collects workflow runs as dated facts. A run belongs at the
instant it completed, not at the instant we noticed, which is what makes “how
long did CI take last Tuesday” answerable.
Run-level timing hides where the time went: a run that takes twenty minutes because one job waited eighteen for a runner looks exactly like one that spent eighteen executing. Only the job level separates them, and only the job level names the runner and the steps, so the jobs of each run are expanded at one extra request per run. Once: the jobs of a finished attempt never change, so a run whose jobs this process already wrote is not listed again, and a re-run is a new attempt that is. An ordinary sweep reads the run list in pages of thirty and pages on while they are full of runs newer than its window; the first sweep after start and a backfill read pages of a hundred.
artifacts collects what the workflows left behind, with sizes and
expiry.
joblogs collects the text a failed job printed. It is the one thing here
that is a log rather than a measurement, and it answers the question a chart
never can: not “the build failed” but why. Only failures, and only their last
forty lines. Off by default.
deployments collects the newest deployments of every repository, which is
the surface a delivery dashboard reads. One GraphQL point per five
repositories: five rather than ten because the gateway gives up on a query it
cannot finish in about ten seconds, and this one asks for connections rather
than plain numbers.
Security
Section titled “Security”security counts open alerts by severity and state, and records
explicitly which features are switched on. That last part is why no data
and no alerts are distinguishable: without it, a repository with Dependabot off
looks exactly like one with nothing to fix.
analyses collects the code scanning analyses themselves, not just the
alerts. An alert says what is wrong now. An analysis says the scan ran, when,
on which commit, with which version of the tool, and how many results it found,
which is what answers “did the scan actually run on that release”. GitHub
prunes them, so they have to be captured while they are there.
Account
Section titled “Account”account is one GraphQL query and the cheapest thing in the project. It
returns the full 366-day contribution calendar, every contribution total, the
per-repository commit breakdown and the sponsors block for one point of a
five thousand point budget. The same data over REST would be dozens of calls
and would not include the calendar at all.
profile collects packages, gists, social accounts and the follower graph.
Packages come from REST deliberately: GraphQL reports zero packages for an
account while REST lists them.
outbound is the other half of everything else here. Every other family
measures what the account owns; this measures what it reads and what it
contributes to: the stars it gave, the pull requests it opened in other
people’s repositories, and those repositories themselves, how many stars each
has and whether it is private. All of it is GraphQL, one point a page: the starred
list, five issue searches, the two comment walks and the accepted answers the
account wrote, which are read apart from the comments so that an answer
accepted weeks after it was written is not missed. A sweep reads up to five
pages of the starred list, every page of the two searches for what is still
open, and the other three, which are ordered by what moved last, back to a
cadence before the sweep before: their first page, unless more than a hundred
items moved in that time. A backfill reads the starred list and those three to
the end as well.
totals asks GitHub for the numbers that are true since the beginning:
pull requests merged ever, commits ever, issues opened ever, and the whole life
of each repository. It is the one family that exists because of how a store
reads rather than because of what GitHub offers. Every other measurement here
is a row per fact, which is the right shape for “how many in July” and the
wrong one for “how many ever”: answering that from rows means scanning the
whole table, and InfluxDB 3 Core refuses a query that would open more than its
file limit, forty thousand where this was measured. Search reports a total for
any query and GraphQL reports one for any connection, so one query of ten
aliased searches, one REST search for
the commit count and one batched query give a number that is one row and is
right on the first sweep of a fresh install. The archived repositories the
default filter sets aside are in it too, from the query that dates their
archive, so their stars and forks stay in the account’s totals.
ratelimit is the only measurement the collector takes of itself: what is
left in each of GitHub’s fifteen independent budgets and when each resets.
GET /rate_limit costs nothing at all, and without it a family skipped for
want of budget looks exactly like a family with nothing to report.
keys collects the account’s own SSH and GPG keys: which have never been
used, and when the key that signs every commit expires.
stats collects commits per week and the hour-of-week punch card, the two
stats endpoints that actually answer for a personal account.
history walks every past year’s contribution calendar, one GraphQL point
per year, back to the day the account was created. Off by default because it
only needs to happen once.
achievements collects the badges on the public profile page, and it is the
one family that does not come from the API at all: GitHub lists achievements in
neither REST nor GraphQL, so the page is read every hour as an anonymous
visitor, without the token and charged to no budget. Beside each badge it
writes how far the account is from that badge’s next tier, which does come from
the API. The parser is strict on purpose: when GitHub redesigns the page the
family writes nothing and says so once, rather than writing wrong numbers that
would look exactly like right ones.
Activity and cost
Section titled “Activity and cost”events collects the account’s activity feed, the most perishable surface
GitHub has. It keeps the last three hundred events, none older than thirty days,
and nothing else records that a repository was
starred, forked, watched or pushed to at a given minute.
notifs collects the inbox. Like the event feed it is a window, not a
history: GitHub keeps inbox notifications for three months unless they are
saved, and per_page is silently capped at 50 whatever is asked for.
billing collects what the account actually spent, day by day, per
product, SKU and repository, which is the only place that says which repository
burned the minutes. Gross, discount and net are all kept rather than one being
derived from the others, because the net is not always zero: on the account
this was developed against it carries the monthly credit.