When repositories have no owner
GitHub's internal github organization held over 14,000 repositories as of early 2025, with more than 11,000 non-archived at that time. Attached to the bulk of these — the ones backing a production service — was no clear ownership signal. The gap became unavoidable during the company's secret scanning remediation sweep. Rotating a leaked credential is only safe when you know whose repository it lives in and who can act on it; routing that remediation work by guessing from commit history or Slack chatter was slow and risky.
The fix took about six weeks: validate an owner for every active repository, archive roughly 8,000 that were simply unused, and make ownership a mandatory field at repository creation so the problem never rebuilds silently.
Why service ownership didn't translate to repository ownership
GitHub's internal Service Catalog tracked ownership for deployed services: which repository each service lived in, the owning team, an executive sponsor, and support contacts. That made the catalog an excellent starting point for incident response, on-call routing, and compliance scoping — all workflows that begin with a service. The catch was that the service-to-repository link was many-to-one, meaning a service could map to a single repository while a single repository could hold multiple services. If you knew the service, you could find its owners. If you knew only the repository, you had to invert that lookup, and it only succeeded when the repository happened to back a service.
Everything else — team repositories, docs sites, internal tools, one-off experiments, personal projects — had no durable owner at all. Working with any of those repositories meant reading a README, inspecting commit history, or asking around in Slack. That's tolerable for occasional case-by-case contact; for a security scrub that fans out org-wide, the ambiguity becomes an operational hazard.
Ownership as structured metadata
The design decision was to make repository ownership a first-class, queryable property instead of burying it in a OWNERS file or a central registry. GitHub custom properties fit naturally: they live at the organization level, are visible across repositories, and can drive rulesets and policies. Two properties were introduced:
ownership-type, a single-select with three values: “Service Catalog,” “Hubber Handle,” or “Team.” These map to every realistic kind of owner inside GitHub: a service with on-call rotation, a team sharing a docs repository, or an individual's experiment.ownership-name, a free-text field whose values a GitHub App validates against live data. Hubber handles must match current organization members; teams must exist and have at least two members; Service Catalog entries must exist. Formatting was intentionally permissive —@my-teamandmy-teamboth pass — because the point was to make adding ownership effortless and to let validation catch nonexistent teams, ex-employees, and decommissioned services.
The push to 100% coverage
Before anyone was asked to take action, a periodic sync populated the two properties for every repository backed by a Service Catalog entry — about 1,500 repositories, roughly 4 percent of the active total. The remaining work was cleaning up the long tail of unowned repositories.
The enforcement loop ran as a GitHub App backed by a Kubernetes CronJob, since the logic needed access to Service Catalog, the GitHub API, and internal systems beyond what a GitHub Actions workflow could reach. The first scheduled run went out on a Saturday morning, and the reaction was immediate: ownership-warning issues appeared across repositories and Slack lit up. At a global company, a Saturday morning run is a daytime run somewhere.
After a 30-day grace period, repositories that still hadn't been claimed were archived. Archiving was the chosen default specifically because it's reversible and non-destructive. Nothing is deleted, the repository becomes read-only, and GitHub Actions stops. An owner can unarchive, add an owner, and resume. That reversibility let the team archive aggressively instead of adjudicating edge cases. Once the initial wave passed, the enforcement window tightened from 30 days to one hour for any repository that managed to dodge ownership requirements.
Sharp edges and guardrails
Two incidents during the rollout pointed to design gaps worth correcting. The first involved a repository owned by nobody, archived during the enforcement pass, that Datadog still had configured to file issues into as part of a monitoring workflow. When Datadog hit a closed door, the monitoring system alerted the owning team, who escalated. What that surfaced was a notification problem, not an archiving problem: ownership issues were being created in repositories no one was watching. The fix was to @-mention repository administrators and, as a fallback, assign all users with write access to each ownership issue so the right people saw it immediately.
The second incident was a data reliability scare. The app was already resilient to a full Service Catalog outage, but stale or corrupted data from a partially healthy catalog could look like a mass loss of Service Catalog ownership — potentially triggering archiving of repositories whose owners were still valid. To guard against catastrophic batch runs, the app now performs a pre-flight tally before taking action. If a run would open more issues or archive more repositories than a conservative threshold, the entire run bails out and a Datadog monitor fires instead. Independently unreachable systems are skipped, not assumed to have changed.
Outcome: fewer, truthful repositories
At steady state GitHub was left with roughly 3,000 active repositories and 11,000 archived, up from around 3,000 archived at the start. The whole transition — from first enforcement run to steady state — took under 45 days. Every active repository either has a validated owner or gets flagged within an hour of losing one. Many of the archived repositories were long dead: abandoned experiments, finished hackathon projects, even one-person prototypes from 2008. Archiving them shrunk the attack surface and made the inventory reflect reality.
The durability of each owner type
Keeping coverage at 100% meant adding the two properties as required fields on the repository creation page and across internal tooling and automation. Each ownership type then carries its own retention characteristics:
- Service Catalog: ownership follows service lifecycle. When a service is decommissioned, its repositories are archived — the intended outcome.
- Team: validated to exist with at least two members; stable enough for most purposes.
- Individual (Hubber): only invalidated on departure, which usually means those personal repositories should be archived anyway. A repository that matters beyond one person's tenure should belong to a team or a service.
Applying the model to other orgs
The whole pattern is reproducible today on any GitHub organization:
- Define the taxonomy. Services, teams, and individuals covered GitHub's needs; other organizations may need only one or two categories.
- Create custom properties at the organization level.
ownership-typeas a single-select,ownership-nameas free text. Both are queryable through the API. - Seed from an existing asset inventory. Any service catalog or repository registry already known to back a service can pre-populate the properties, getting instant coverage for the majority of meaningful repositories.
- Make the properties required at creation time. This keeps the inventory clean with no remediation loop going forward.
- Grace period before aggressive action. Use a warning issue with a firm deadline (30 days worked), then archive repos that go unclaimed. Archiving is a reversible default, so safe to apply broadly.
- Expect data sources to fail. Batch operations that open issues or archive repositories need guardrails like pre-run thresholds and redundant notification paths — built from day one, not after the two incidents.



