Skip to content

Auditing Git and API Activity

Lesson 3 of 9Advanced13 min readGit at Scale & Enterprise Engineering · Enterprise OperationsVerified: GitHub audit log documentation, September 2026

“Has anybody cloned this repository?” is the question you will be asked after a credential is found in it, or after somebody leaves under a cloud.

It is answerable for seven days, and only then.

Git events cover clone, fetch and push. GitHub documents that “The audit log retains Git events for seven days.” On GitHub Enterprise Cloud they are available via the REST API, within that window.

API request events are a different, newer category. Documented as being in public preview and subject to change, available only for Enterprise Managed Users enterprises and GitHub Enterprise Cloud with data residency, delivered by streaming only, and requiring enablement in the audit log settings.

Both are high volume, substantially higher than administrative events.

Both must be streamed to be retained in any useful sense — the seven-day window for Git events is shorter than the time it typically takes to know you have a question.

They are the only record of code access. Everything else in the audit log records administration; Git events record somebody actually taking a copy of the code.

The questions they answer:

“Was this repository cloned after the credential was committed?” The single most important question after a secret exposure, because it determines whether you are rotating a credential defensively or responding to a probable compromise.

“What did this person access before they left?” A departing employee’s clone activity in their final weeks.

“What did this compromised token do?” A token used to clone repositories is a different incident from one used to open a pull request.

“Which repositories does this automation actually use?” A less dramatic but frequently useful question when rationalising credentials.

Push events answer a related question: what was written, by whom, and when — complementing but not duplicating Git history, because history records the commit and the event records the transfer.

Knowing the shape determines what questions are answerable.

The repository. Which repository was cloned, fetched or pushed to.

The actor. The account that performed the operation — a person, or the owner of the credential used.

The operation. Clone, fetch or push. The distinction matters: a clone is a full copy, a fetch is an update to an existing one, and a push is a write.

The timestamp. Which is what lets you correlate against an exposure window.

Contextual information about the request, which may include source details depending on configuration and platform.

What it does not contain: the content transferred, the branches or paths involved, or anything about what happened to the copy afterwards.

Which shapes the queries. “Who cloned this repository between these dates” is directly answerable. “Which files did they get” is not — though it is inferable from the repository’s state at that time, which Git history provides.

And it shapes attribution. An operation performed with a shared credential is attributed to that credential’s owner. A pipeline using an engineer’s personal access token produces Git events naming that engineer, thousands of times, which both obscures their real activity and misattributes the automation. This is one more reason machine identity matters — see machine identities.

Git events are generated by every clone and every fetch, which in a CI-heavy enterprise means every pipeline run, on every repository, every time.

That volume is large. An enterprise with a thousand repositories and active CI can generate more Git events in a day than administrative events in a month.

Which has cost consequences if the destination charges by ingestion. It has essentially none if the destination is object storage.

And signal consequences. A view of Git events dominated by CI runners is not useful for investigation without filtering.

Filter by actor type when reading. Human clones are a small fraction of the total and are usually what an investigation is about. Automation clones matter when the automation is the subject.

Do not filter them out at ingestion. The case where automation activity is the question — a compromised app, an unexpected runner — is exactly the case where you would regret having discarded it.

Documented as being in public preview and subject to change.

Restricted availability: Enterprise Managed Users enterprises, and GitHub Enterprise Cloud with data residency.

Streaming only. Not available through the interface or the standard audit log query path.

Requires enablement in the audit log settings.

What they add: the request layer beneath the semantic events. Where an administrative event says “a team member was added”, an API request event says which endpoint was called, by which credential.

The use cases are narrower and real:

Understanding automation behaviour. What an app or a script actually does, as opposed to what it is documented to do.

Detecting unusual access patterns. A credential suddenly enumerating repositories it has never touched.

Rate limit and performance investigation. Which caller is consuming the budget.

Attribution at the request level, which matters when several processes share an identity — though the better fix for that is to stop sharing identities.

Because this capability is frequently oversold internally, it is worth being clear about what having it does and does not achieve.

It does not prevent anything. A person with read access can clone. Git events record that they did; nothing stops them. Prevention, where it is possible at all, is at the access-granting stage.

It does not detect exfiltration reliably. A single clone of a single repository by somebody who legitimately has access is indistinguishable from normal work. The detections above catch patterns, and a careful actor produces no pattern.

It does not reach copies already made. Somebody who cloned a repository last year has it, and no control reaches that copy.

What it does achieve is scoping. After an event, it converts “we don’t know who accessed this” into a specific list with dates. That is the difference between an incident response that is proportionate and one that assumes the worst.

And it achieves accountability. People behave differently when activity is recorded, and the existence of the record is itself a control — provided people know it exists, which is an argument for saying so in your acceptable use policy rather than keeping it quiet.

Frame the business case around scoping, not around detection. “We will catch data theft” is a claim that will not survive contact with a real incident. “We will be able to say exactly who accessed this and when” is true, valuable, and defensible.

  1. Enable Git events in your stream. If streaming is already configured, this may be a checkbox. If it is not, configure streaming first — nothing else here is possible without it.

  2. Choose a destination that tolerates the volume. Object storage for retention; a filtered subset to a SIEM if detection needs it.

  3. Verify events arrive, and look at their shape. Field names and structure are what your queries will be written against.

  4. Set the destination retention to match the questions you need to answer. For exfiltration investigation, that is likely years rather than months.

  5. Enable API request events if available for your configuration and you have a use for them. Consider the volume.

  6. Write the standard queries — clone activity by repository, clone activity by actor, push activity by actor — before you need them.

  7. Test with a known event. Clone a sandbox repository, find the event in your destination.

  8. Document what is retained, for how long, and how to query it, so that whoever handles the next incident is not starting from nothing.

The scenario that justifies this entire capability, worked through, because the sequence is what determines whether the seven-day window is enough.

A secret is found in a repository. Discovered by a scan, by a developer, or by an external report.

The first question is when it was committed. Git history answers this exactly — the commit that introduced it, with its date.

The second question is who could see it. Repository visibility during that period, the members and collaborators with access, and whether it was ever internal or public. Visibility change events answer the last part.

The third question is who took a copy. Git clone and fetch events across the exposure window. And this is where the seven-day window usually defeats you — if the secret was committed four months ago, the clone events from that period exist only if you were streaming.

The fourth question is what the credential could reach, which is not a GitHub question at all.

The response does not wait for the answers. Rotate first — the credential rotation ordering is the same regardless of what the audit data shows.

But the answers determine the scope of the incident. A credential exposed in a private repository with three members and no clones outside CI is a different disclosure from one in an internal repository cloned by forty accounts.

Without Git event history, that determination cannot be made, and the responsible position is to assume the worse case. Which means more rotations, wider notifications, and a larger incident — every time.

That is the cost of not streaming, expressed concretely: not a missing report, but a permanently over-scoped incident response.

Whether the clone was legitimate. An event records that a person with access exercised that access. Determining intent is the investigation, not the data.

What was in the repository at the time. The event names the repository; the content is in Git history at the relevant commit.

Whether the copy still exists. A clone made two years ago may be on a laptop, in a backup, or deleted. Nothing observable from GitHub answers this.

Read access other than Git operations. Browsing code in the interface is not a Git event.

Which is the honest boundary: these events tell you that a transfer happened, by whom, and when. Everything about consequence is inference, and the inference is usually the important part.

Say so in incident reports. “The repository was cloned by three accounts in the exposure window” is a fact. “The credential was exfiltrated” is a conclusion, and the distinction matters when the report is read later.

What API request events reveal about automation

Section titled “What API request events reveal about automation”

Beyond incident response, the most practical use of API request events is understanding what your automation actually does — which is reliably different from what it is documented to do.

Endpoint patterns show intent. An app documented as a status reporter that calls repository content endpoints is doing more than reporting statuses. That may be legitimate and it is worth knowing.

Volume shows dependency. An integration making a hundred thousand requests a day is load-bearing whether or not anybody thinks of it that way. It is also a candidate for optimisation.

Failure patterns show fragility. Requests returning errors, retried repeatedly, indicate an integration that is limping rather than working — and it will eventually stop limping.

Breadth shows over-permissioning. An app with access to all repositories that only ever touches four is over-installed, and the request data is the evidence for narrowing it.

Unfamiliar callers show drift. Credentials making requests that nobody on the platform team can account for. Every estate has some, and finding them is the first step to rationalising machine identity.

Rate limit consumption shows the shape of the problem when somebody reports being throttled. It is usually one caller, and it is usually not the one complaining.

None of this requires an incident. A quarterly look at API request data, where available, is a cheap way to understand an estate’s automation — which is otherwise almost entirely undocumented.

For these categories in particular, “how long” deserves a deliberate answer rather than a default.

Git events: as long as an exfiltration question could be asked. Which, realistically, is years. Departures, disputes and breach notifications all arrive long after the fact. Object storage makes multi-year retention affordable.

API request events: shorter is defensible. Their value is largely operational — understanding and debugging automation — and that value decays quickly. A few months is usually enough, though a detection use case may argue for longer.

Consider the data protection dimension. Both categories record individual activity in detail. Long retention of personal activity data is a decision that belongs with whoever owns data protection in your organisation, not with whoever configures the stream.

Which can cut both ways. A data protection assessment might require shorter retention than your security team wants, or might specifically require the retention to support breach investigation. Either way it is a conversation to have deliberately rather than a default to inherit.

Document the decision and the reason. “Git events retained for three years to support breach investigation, approved by [the data protection owner]” is a defensible position. An unexamined lifecycle rule is not.

Assuming Git events share the 180-day retention. They are retained for seven days.

Not streaming them. The only route to retention.

Streaming them into an ingestion-charged destination without considering volume. An expensive surprise.

Filtering out automation at ingestion. The case you will regret.

Relying on API request events without verifying availability. Preview, restricted configurations, streaming only.

Reading a clone event as evidence of exfiltration. It is evidence of a transfer.

Setting destination retention shorter than the questions you will ask. Exfiltration questions arrive years later.

No documented query path. The data exists and nobody can use it.

Once Git and API events are landing somewhere queryable, a small number of detections deliver most of the value.

Clone volume anomaly by actor. A person or credential cloning substantially more repositories than usual, or than their peers. This is the closest thing to an exfiltration signal available, and it has false positives — a new joiner setting up, a developer running a fleet-wide search. Tune it, do not discard it.

Clone from an unusual location. Where source information is available, a clone from a country or network the actor has never used.

First clone of a sensitive repository. For a small set of genuinely sensitive repositories, any clone by an actor who has never cloned it before is worth a look. Low volume, and a strong signal on the repositories where it matters.

Push to a repository the actor does not normally touch. Particularly a push modifying workflow files, which is the highest-value target for somebody with a stolen credential.

Credential enumerating repositories. An API pattern of listing many repositories in sequence, from a credential that normally touches few. A reconnaissance signature.

Activity from a suspended or departed account. Should be impossible, and worth alerting on precisely because it should be impossible.

Keep the set small and tuned. Two detections that people trust are worth more than ten that are muted, and the volume of these event categories means an untuned detection will produce noise immediately.

And accept the limits. These detect patterns, not intent. Every one of them will fire on legitimate activity, and the value is in the investigation it prompts rather than the alert itself.

Administrative events tell you who changed the system. Git events tell you who took a copy of the code, and only for seven days. That asymmetry — the highest-consequence question having the shortest window — is why streaming Git events is not an optimisation but the whole capability.

  • Git events cover clone, fetch and push, and GitHub retains them for seven days
  • On GitHub Enterprise Cloud they are available via the REST API within that window
  • API request events are in public preview, limited to EMU and data residency enterprises, and streaming-only
  • Both categories are substantially higher volume than administrative events
  • Git events are the only record of code access, and answer the question asked after every credential exposure
  • Filter automation when reading, not at ingestion
  • Destination retention for these events should match the timescale of exfiltration investigations
  • The events record transfers; conclusions about intent are inference and should be labelled as such

Use an enterprise you have access to.

  1. Determine whether Git events are being streamed. Predict: are they?

  2. Query for Git events from ten days ago. Predict: what happens?

  3. Query for clone activity on one repository over the last week. Separate human from automation actors.

  4. Estimate your daily Git event volume from that sample.

  5. If your destination charges by ingestion, calculate the annual cost of streaming them.

  6. Determine whether API request events are available for your configuration.

  7. Write the query you would run if a credential were found in a repository, and time yourself constructing it.

  8. Document what is retained, for how long, and where — as one page.

Engineering Team Onboarding SystemA 30-day Git and GitHub programme with standards templates, assessments and governance checklists.