How this site handles personal data, on what lawful basis, and how to object. This is the notice referred to from every page that reports on people.
UK Public Sector GitHub Observatory. Last updated 14 August 2026.
This notice explains what personal data this project processes, why, and what you can do about it. It is written to satisfy Articles 13 and 14 of the UK GDPR, and it is deliberately in plain English rather than legal boilerplate.
Short version: this project reads the commit history of public GitHub repositories owned by UK public bodies, to describe how the public sector builds software. Commit history contains names and email addresses. The published site contains neither. The open dataset contains aggregates and irreversible pseudonyms for almost everyone in it — with one deliberate exception, set out below: in-house developers identified by a .gov.uk commit address are published by their real address, not a pseudonym. If you would rather not be in it, at either level, you can say so and be removed.
Who is responsible
Controller: Rose Traviss, Data Torturing Solutions. Contact: <[email protected]>
There is no Data Protection Officer; the project is not of a scale or nature that requires one under Article 37.
What data, and where it comes from
Everything is collected from public GitHub repositories owned by UK public sector organisations, using GitHub's public API. Nothing is collected from you directly, which is why Article 14 applies rather than Article 13. No authentication, no private repositories, no file contents — the clones are "blobless", meaning commit and directory structure are fetched but file bodies never are.
From each repository's git history:
| Data | Where it comes from | |---|---| | Author display name | the author header of each commit | | Author email address | the author header of each commit | | Commit counts, first and last commit dates | derived | | Which repositories and organisations you contributed to | derived | | Date and time of day of each commit, in the timezone the authoring machine was set to | the author header of each commit | | Working-pattern aggregates — commits per day, hour of day, day of week, and whether commits fell on a bank holiday | derived | | Which other contributors were active in the same repository in the same week | derived |
Git records these in every commit. They are served to anonymous users by GitHub and by any clone of the repository. They cannot be removed retrospectively without rewriting history and changing every subsequent commit hash.
A second, separate source feeds the domain-security dataset: the public DNS records of every registered .gov.uk domain.
| Data | Where it comes from | |---|---| | SPF and DMARC policy | TXT records on the domain and on _dmarc.<domain> | | DMARC report addresses | the rua= and ruf= tags inside that DMARC record | | HTTPS, HSTS, nameserver, security.txt presence | response headers and well-known paths |
Almost all of this describes a domain rather than a person. The rua=/ruf= addresses are the exception, and are covered separately below.
What it is used for
To describe engineering practice across the UK public sector in aggregate: how many people work on public code, how work moves between organisations, how conventions differ, and what the estate discloses to an outside observer.
No decision is made about any individual. There is no profiling, no scoring of people, no automated decision-making, and no attempt to evaluate anyone's work or performance.
Lawful basis
Legitimate interests — Article 6(1)(f). A Legitimate Interests Assessment has been completed and is available on request.
The interest is the public interest in understanding how public money is spent building public software, and in being able to scrutinise the engineering practices of public bodies. The Post Office Horizon scandal is the reference point for why the aggregate view of government software matters and why it should not depend on the goodwill of the organisation being examined.
The balancing test turns on this: the processing describes organisations, not people. Individuals appear only as anonymous contributors to a total. The published outputs are engineered so that no individual can be identified from them, which is what keeps the impact on any one person minimal — for the site, and for almost everyone in the open dataset. One population is carved out of that protection deliberately; see "The .gov.uk exception" below, which carries its own, narrower balancing test.
What is published, and what is not
Published, for everyone:
- Aggregate counts and percentages
- Organisation-level figures
- Irreversible pseudonyms in the open dataset —
HMAC-SHA256(identity, secret),
where the secret is never published — except the population described in "The .gov.uk exception", who are published by real address instead
Never published, for anyone, with one exception stated in full below:
- Display names on any cross-organisation page, except the top 100
committers page (see "Named top-committer pages")
- Any link between a pseudonym and a person
- Email addresses for anyone outside the .gov.uk exception below
The .gov.uk exception
The open dataset publishes the real commit email address, not a pseudonym, for a specific, narrow population: in-house developers at UK public bodies, where all of the following hold —
- the commit's email domain is
gov.ukor a.gov.uksubdomain - the commit is not on a forked repository — a fork carries someone else's
project history, and its committers never worked for the body publishing it
- the person made more than one commit to that organisation, excluding forks —
a single commit is a drive-by contribution, not evidence of being in-house, and the public-interest case below does not cover it
- the person has not objected (see "Your rights" — this is checked first and
removes the row from publication entirely, it does not merely fall back to a pseudonym)
Why this population and not everyone: the pseudonym exists to describe organisations without describing people. For this population specifically, the work is the public record — who built and maintains the software that public money paid for, at the organisations directly accountable for it, is itself a matter of public interest and accountability, in the same way that officials' names appear against decisions in other public contexts. Pseudonymising this group would remove exactly the accountability the project exists to provide, while a random open-source contributor whose repository was forked by a department, or someone's single out-of-hours commit, has no equivalent public role and stays behind a pseudonym.
This is a separate, additional balancing test to the one above, not a relaxation of it: it applies only to this population, only for the reason given, and objection removes a person from it entirely — the same right everyone else in the dataset has, exercised the same way.
Additional protections in the code, not just in policy:
- Organisation pairs with fewer than three people in common are suppressed, so no
published figure can describe a single individual
- See "Names and identity" below for how a person's commits are joined together,
and what that can get wrong
Named top-committer pages
/people lists the 100 identities - human and automated - with the most commits across every organisation in this dataset, by real name, each with its own page showing which organisations and repositories they reached. This is a deliberate, later exception to the rule stated above ("no display names on any cross-organisation page"), added 18 August 2026, and it is written here in the same terms as every other decision in this notice rather than left as a quiet contradiction of it.
What this page is, in the terms of the balancing test above. Everywhere else on this site, the argument for withholding names on a cross-org page is that the processing describes organisations, not people, and that keeping individuals anonymous is what keeps the impact on any one person minimal. A named, cross-organisation top-100 page is the opposite of that: it is a profile of a person, built from the same commit metadata Finding 04 (in the project's own security analysis) describes as "a phishing list, sorted by employer" when held by someone else. Publishing it does not change what the underlying data is; it changes who has been shown a person-shaped view of it, and the people it shows the most about are, by construction, the ones who have contributed the most - which is a different population from the .gov.uk exception above and is not limited to it. This page can include a personal Gmail address holder, a .gov.uk in-house developer, and an automated account, ranked together on the same criterion: raw commit count.
Why it exists anyway. Rose, the data controller, decided to build it after this was explained plainly, including the specific tension with the "no profiling of individuals" line elsewhere in this notice. That decision is recorded, not re-argued, here — see HANDOVER.md for the fuller account of how it was reached. The formal position is that this addition has not yet been reflected in an updated Legitimate Interests Assessment, and until it is, this section is the operative description of what the page does and why, pending that update.
Objection works exactly the same way as for everyone else in this notice. Anyone listed on /people who objects is removed from that page - not merely pseudonymised - at the next weekly rebuild, using the same one-step route under "Your rights" below. Automated identities (bots, CI accounts) are not people and have no rights under this notice; they are included on the page because the request this was built for asked for bots to be shown alongside humans, marked as such.
GitHub profile enrichment
For the human identities on /people that GitHub's own commit API could resolve to an account, this site links to that account's public GitHub username and shows whichever of bio, company, location, personal website and Twitter/X handle that person has made public on their GitHub profile. Added 19 August 2026, this is a second and larger step beyond the page itself, and is written down separately because it is a different kind of thing, not just more of the same one.
Why this is different from everything else in this notice. Every other piece of data this project processes - names, email domains, commit counts, which organisations someone worked in - comes from git commit metadata this project already held, collected once from public repositories. This does not: pipeline/fetch-github-profiles.py makes new, live lookups against GitHub's API, specifically to compile a username, employer, location, personal website and social media handle for each of the top 100 committers into one place. A name-plus-social-media directory of the most prolific identifiable engineers in UK government is close to exactly what this project's own security analysis (Finding 04, see HANDOVER.md) calls "a phishing list, sorted by employer" when held by someone else - this section exists because that comparison was put to Rose plainly, twice, before this was built, and she chose to build it anyway both times.
What is, and is not, fetched. Only GitHub's own public Users API for the one account GitHub itself associates with a person's commits - the same association GitHub already shows next to that commit on github.com. Nothing is scraped from Twitter/X, a personal website, LinkedIn, or anywhere else; only the handle or URL GitHub's own profile page already displays. A commit's email is checked against a GitHub-generated noreply address first (the username is embedded in it directly, and is public because GitHub generated it that way), and against GitHub's commit-author resolution second. Nothing is guessed: a person GitHub could not resolve is left with no GitHub section on their page at all, not a placeholder.
Bots are never enriched. The fetcher skips every identity classed as automated - a bot's GitHub profile, if it has one, is not a social presence to be catalogued.
The formal position is the same as "Named top-committer pages" above: this has not yet been reflected in an updated Legitimate Interests Assessment, and this section is the operative description in the meantime.
Objection removes this too, the same one step as everything else in this notice - see "Your rights" below. It also stops the fetcher considering that identity again on a future run.
Working patterns and the collaboration network
For the same named top-100 cohort, this project now derives when each of those people works: how many commits they made on each individual day, what hour of the week those commits fall in, how much of their work lands outside ordinary hours or at weekends, and whether they committed on bank holidays. It also maps who committed to the same repository in the same week as whom. Added 19 August 2026.
Why this is a different kind of processing, even though it is not new collection. Unlike the GitHub profile enrichment above, nothing new is fetched — every figure comes from commit timestamps this project already held. That makes it cheaper to do and no less significant. A commit count says how much someone contributed; a per-day, per-hour series says when they were at a keyboard, which days they were not, and who they were working alongside at the time. That is a record of a person's working rhythm rather than of their output, and it can be read for things it was not built to answer — whether someone works evenings, whether they went quiet for a fortnight, whose name stops appearing next to whose. It is written down separately here because it should be assessed as what it is.
What is deliberately not built. The brief that produced this asked for school-holiday patterns per person alongside bank holidays. That is not built and the code says so at the point where it would go (pipeline/build-activity.py). Matching a named individual's quiet periods against school terms has one plausible output — an inference about whether that person has school-age children — which is a guess about someone's family circumstances, is not a fact about how public money is spent, and would be made about people who will never see it. The seasonal question is instead answered at organisation level: commit volume by week of year, per organisation, which shows summer and half-term capacity dips directly and identifies nobody. If the per-person version is ever wanted, it needs a recorded controller decision first, not a new flag.
Bank holidays are matched, and carefully. The source is GOV.UK's published feed, and all three divisions — England and Wales, Scotland, Northern Ireland — are reported, because they genuinely differ. Which one applies to a given person is not knowable from a commit, so none is inferred. Rates are computed only inside the feed's covered years and inside each person's own active period, and are shown against that person's own ordinary-weekday rate, so "committed on 4% of bank holidays" can be read next to "and on 6% of ordinary weekdays" rather than on its own.
The times are weaker evidence than they look, and this is stated wherever they are used. Git records the timezone the authoring machine was set to. A CI runner or a container is on UTC, and so are plenty of laptops, which moves a British summer evening an hour earlier. Rebases, squashes and imports from other forges rewrite author dates wholesale. Any single person's hour-of-day profile is indicative, not a timesheet, and must not be treated as evidence about an individual's actual working hours.
"Same repository in the same week" is co-presence, not collaboration. Two people can appear as an edge without having exchanged a word. Buckets with more than 25 active contributors are excluded entirely, and edges are weighted down by how crowded the repository was, so that a pair alone in a repository counts for more than a pair among twenty — but the label is still co-presence and the data does not support a stronger one.
Scope, forks and objection. Limited to the same named cohort as the top-committer pages, for the same reason: extending per-person timing analysis to all ~39,000 non-fork identities would be a much larger step, not an implementation detail. Forked repositories are excluded throughout. Objection removes a person from all of it — the cohort, the network and the underlying series — in the same one step as everything else in this notice, and the next person moves up rather than a gap being left.
As with the two sections above, this has not yet been reflected in an updated Legitimate Interests Assessment, and this section is the operative description in the meantime.
Names and identity
Git records a name and an email address on every commit, and one person commonly has several addresses — a work laptop and a home one, an address that changed when they changed employer, GitHub's privacy address. To describe a person's work at all, those have to be joined up.
Three signals are used, in order of confidence:
- GitHub's numeric account id, where the address is a GitHub privacy
address. This is the strongest identifier available and survives a change of name, employer and address.
- An exact email match.
- An exact display name — but only where the name has at least two parts and
is not a machine, role or placeholder name (root, ubuntu, jenkins, unknown, a bare first name).
The third signal is new, and it is a deliberate reversal. This notice previously said identity was never resolved on display name, on the grounds that "John Smith" is not an identifier and merging two of them invents a career for someone. That reasoning still holds, and it is why the rule requires a full name and rejects machine names — matching on any exact name would have merged 39% of all identities in this dataset, including 84 different people committing as root and 29 different people called matt.
What the old rule missed is that the opposite error is also an accuracy failure under Article 5(1)(d). Splitting one person in two because they committed from two laptops publishes a career at half its true size, twice over. One contributor appeared on this site as two people with 13,676 and 5,981 commits; he is one person with 19,657.
What this still gets wrong, stated plainly: two people who genuinely share a full name and both commit to UK public sector repositories are treated as one person. This project cannot tell them apart from commit metadata, and does not claim to. If this has happened to you, the correction and objection routes below apply, and either will separate or remove you.
Effect on published pseudonyms. A merged person now has one pseudonym where they previously had several. Pseudonyms remain stable from this change onwards; the change itself is a one-off discontinuity, and it is dated in the public dataset's own commit history.
Effect on objections. An objection made before this change was recorded against a single address. Those objections continue to be honoured, and now suppress every address belonging to the same person rather than only the one they wrote from. No objection has to be made again.
Domain report addresses
The domain-security dataset publishes DMARC records verbatim, including the rua= and ruf= mailboxes that receive reports of mail sent using that domain. Across the .gov.uk estate that is 3,685 domains and 1,984 distinct addresses.
The overwhelming majority are role mailboxes — [email protected] alone appears 2,828 times. A small number are individuals, roughly forty-five of the form firstname.lastname@, and some of those are at parish councils where the clerk is the only member of staff. Those are personal data, and they are published rather than redacted.
Why published rather than redacted: the address is the finding, not incidental to it. The question this dataset exists to answer is whether reports of domain spoofing reach anyone who can act on them. A domain whose DMARC reports go to a central government service is in a different position from one sending them to a single named individual at a body with no security team, and from one where the address no longer resolves at all. Reducing every address to its host answers a weaker question and hides exactly the cases that matter most. These addresses are also, in every instance, published by the body itself in a DNS record specifically so that mail from strangers can be sent to them.
The lawful basis is the same legitimate interests basis as the rest of the project, Article 6(1)(f), with the same balancing test as "The .gov.uk exception" above: a narrow population, holding an identifiable public technical role, where the public interest is in the role rather than the person.
Notification. Article 14 applies here as elsewhere — the data was not collected from the people concerned. Individuals identified in these records are notified by email at the address in question, which is the address the record itself designates for contact.
Objection works the same way as for everyone else in this notice, and is set out under "Your rights" below. An objection removes the address from the published record.
Who else gets it
The open dataset is published openly under CC BY 4.0 so that others can check and build on the analysis. Anyone who does so becomes a controller for what they build — including, for the .gov.uk exception population, a controller of real email addresses, not just pseudonyms.
Pseudonymised data is still personal data under UK GDPR. Pseudonymisation reduces risk; it is not an exemption. Data published by real address carries the full weight of GDPR with no reduction from pseudonymisation at all, which is why the .gov.uk exception is deliberately narrow rather than the default.
No data is sold, and none is used for marketing, recruitment, or advertising.
How long it is kept
The observation log is append-only and is intended to be kept indefinitely: the point of a repeated survey is to show change over time, and deleting history would destroy the only thing it is for.
Email addresses are held in the private analysis repository, and are retained only while needed to resolve identity between runs. For the .gov.uk exception population described above, the address is also published in the open dataset; for everyone else it is never published. Erasure requests are honoured regardless — see below.
One copy is held off the analysis machine. When the commit extractor changes, the next run re-derives the whole estate from scratch rather than incrementally — hours of processing that produce the only internally consistent snapshot of everyone's commit timing. That snapshot is archived once, to private object storage, so the analysis can be repeated without re-deriving it. It is not a rolling backup: one archive per extractor version, recorded in a local ledger, and nothing is archived weekly.
That archive holds the same categories as the analysis repository — addresses, commit dates and times — and is treated the same way, with three specific protections. Objections are applied as the archive is written, so an objector's rows are absent from it rather than merely ignored on the way out; the process refuses to produce an archive at all if it cannot check the objection list. The object is written private and encrypted, and is then fetched anonymously to prove it is not publicly readable — a bucket configured to serve its contents publicly would otherwise turn one upload into a disclosure, and the upload would report success either way. And an expiry date is stamped on the object when a retention period is configured.
An objection or erasure request received after an archive was written is honoured against the archive as well. The archive is rebuilt — which applies the objection as the new copy is written — and the superseded object is then deleted, in that order, so there is never a window with no archive and never a lasting copy that still contains rows somebody asked to have removed. The archive is therefore not a route by which data survives a request the live dataset has already honoured.
Your rights
You have the right to:
- Be told what is held about you (access, Article 15)
- Correct it if it is wrong (rectification, Article 16)
- Have it erased (Article 17)
- Restrict how it is used (Article 18)
- Object to it entirely (Article 21)
Objection and erasure are honoured without argument. Because the basis is legitimate interests rather than consent, Article 21 gives you the right to object, and the practical answer is simply to add your identity to the suppression list: your commits stop contributing to any figure and your row — pseudonym or, for the .gov.uk exception population, real address — is removed from the published dataset entirely at the next weekly refresh.
You do not have to give a reason, and nothing is asked in return.
To exercise any of these, email the contact address above.
One honest limitation: the data originates in git history published by other organisations. Removing you from this project does not remove your email address from their repositories, and nobody here can do that. You would need to ask them.
Complaints
If you are unhappy with how this has been handled you can complain to the Information Commissioner's Office: <https://ico.org.uk/make-a-complaint/>, or 0303 123 1113. You are welcome to raise it here first, but you do not have to.
Being told directly
Article 14 requires that people are told when their data was not collected from them. Notifications are sent to affected committers in small daily batches, using the addresses already present in the commit metadata. Sending is automated: a scheduled process on weekday mornings sends a ramped daily allowance (starting at 5, rising to 20 over two weeks), and stops itself if the bounce rate or the objection rate rises above a set threshold, in which case a human reviews before it resumes. The daily volume, the notice text, and the one-step objection route are exactly what a human sends manually with this project's tools; only the act of clicking "send" is automated, and every run is logged. See HANDOVER.md, "The auto-send decision", for why this was chosen over sending by hand, and NOTIFY-deliverability.md for the sender authentication this depends on.
This is done with GitHub's permission, since GitHub's Acceptable Use Policy otherwise prohibits using addresses obtained from the platform for unsolicited email.
Each message identifies the project, explains the basis, links to this notice, and gives a one-step route to object. Notification is recorded so that the same person is not contacted twice.
The notice states this generically, as a category, not as something derived about the specific recipient: anyone with a .gov.uk commit address, on a non-forked repository, with more than one commit to that organisation, should read this notice as meaning their real address — not a pseudonym — appears in the open dataset, and that objecting removes it. This is a category statement true of the whole population it describes, not a personalised message about what any one recipient specifically did.