Dissecting the Secret Scanning Bug That Took Down GitHub

Arpit Bhayani

Arpit Bhayani

Jun 03, 2022 • 7 min read

Play

Dissecting the Secret Scanning Bug That Took Down GitHub

In April 2021, GitHub suffered a 53-minute outage where users around the world were unable to create new repositories. The incident was particularly striking because the failure did not originate from Git internals, load balancers, or server capacity issues. Instead, the root cause stemmed from Secret Scanning—an auxiliary background feature designed to detect leaked credentials.

This incident provides a textbook case study on the perils of unknown synchronous dependencies, monolith-to-microservice migration artifacts, and failure domain leakage.


1. What Is Secret Scanning and Why Does It Exist?

Modern applications rely heavily on external integrations: cloud providers (AWS, GCP, Azure), managed databases, cache clusters, and SaaS vendors. These integrations require sensitive credentials—API tokens, private keys, database connection strings, and OAuth secrets.

When developers inadvertently commit hardcoded secrets into a Git repository, significant vulnerabilities emerge:

  • Public Repositories: Leaked credentials can be scraped within seconds by automated bots, leading to unauthorized AWS instance provisioning (e.g., cryptocurrency mining), data breaches, or ransomware demands.
  • Private Repositories: Even if private, a compromised team member account, repository permission change, or upstream platform breach exposes the credentials.

How Secret Scanning Works

To mitigate these risks, GitHub runs background checks on pushed code:

  1. Pattern and Entropy Matching: Scans commits using regex patterns and Shannon entropy algorithms to detect high-randomness strings characteristic of cryptographic keys and tokens.
  2. Registry and Notification: Records detected secrets in an internal database and alerts repository owners or token-issuing partners (e.g., AWS, Slack) to automatically revoke compromised credentials.

To support this, GitHub maintains a Secret Scanning Service with dedicated persistence to track repositories, scan configurations, and detected findings.


2. The Anatomy of the Outage

The Incident

In April 2021, GitHub observed elevated API error rates across its repository creation endpoint. For roughly 53 minutes, any user or automated CI/CD pipeline attempting to initialize a new repository received HTTP 5xx errors.

The official incident disclosure identified the trigger:

“…an issue was caused by a bug from a recent data migration to isolate secret scanning tables into their own cluster…”

The Architecture Before the Incident

Like many platforms that grew rapidly, GitHub’s architecture began as a monolith sharing a unified relational database cluster. Over time, high-traffic tables are extracted into dedicated database clusters to avoid CPU, memory, and I/O contention.

Historically, the repository creation flow looked like this:

sequenceDiagram
    autonumber
    actor User
    participant API as Repository Service / Monolith
    participant SharedDB as Shared Database Cluster

    User->>API: POST /user/repos (Create Repository)
    API->>SharedDB: INSERT INTO repositories (...)
    API->>SharedDB: INSERT INTO secret_scanning_targets (...)
    API-->>User: 201 Created

Because both the repositories table and the secret_scanning_targets table lived inside the same shared database cluster, writing to both was seamless and treated as a single unified transaction or execution flow.

The Data Migration

To handle scale, GitHub initiated a migration to split out the secret scanning tables from the main cluster into an isolated database cluster.

flowchart TD
    subgraph Original Monolithic Cluster
        T1[repositories table]
        T2[users table]
        T3[pull_requests table]
        T4[secret_scanning table]
    end

    T4 -.->|Migrated to isolate load| NewCluster

    subgraph NewCluster [Dedicated Secret Scanning Cluster]
        T4_isolated[secret_scanning table]
    end

Once the table was relocated, the application code had to point its secret scanning writes to the new cluster. Immediately following this rollout, repository creation broke globally.


3. The Root Cause: Hidden Coupling and Direct Database Access

Two critical architectural flaws collided to cause the outage:

1. Dual-Writing from a Monolithic Layer (Service Encapsulation Violation)

In an ideal microservices design, a Repository Service does not directly touch the database of another domain. It calls the Secret Scanning Service via an API, or emits an event.

However, in architectures undergoing transition from a monolith, old code paths often bypass service interfaces and establish direct database connections across domains.

When the secret scanning table moved to a new cluster, the repository creation handler attempted to write directly to this newly partitioned database. Due to a bug (such as connection pooling failures, network security policies, missing whitelist rules, or stale connection strings), the write failed.

2. Synchronous Execution on the Critical Path

Secret scanning registration was an unknown synchronous dependency on the critical path of repository creation:

flowchart LR
    A[User Request] --> B[Repository Creation Logic]
    B --> C[Write to Repositories DB]
    C --> D[Synchronous Write to Secret Scanning DB]
    D --> E[HTTP 201 Response]
    
    style D fill:#f88,stroke:#c00,stroke-width:2px

Because the database write was executed synchronously within the lifecycle of the incoming HTTP request:

  • When the write to the secret scanning cluster failed or timed out, the request handler threw an exception.
  • The transaction aborted, or an unhandled exception triggered a 500 Internal Server Error.
  • The entire repository creation workflow was blocked.

Repository creation should only depend on core invariants: allocating storage, creating the repository metadata record, and initializing access permissions. Registering a repository for periodic secret scanning is an auxiliary concern. An outage in an auxiliary system should never cascade into a total failure of the primary business capability.


4. Remediation and Incident Resolution

Short-Term: Rollback First, Root-Cause Later

When critical platform functionality goes down, trying to debug and hotfix in production increases Mean Time to Recovery (MTTR). GitHub opted for a fast rollback of the migration, reverting the application configuration so that traffic pointed back to the original operational setup.

Rolling back immediately restored repository creation while isolating the migration bug for offline investigation.

Long-Term: Asynchronous Event-Driven Decoupling

To permanently remove the dependency, GitHub updated the application architecture to decouple repository creation from secret scanning.

Instead of a synchronous dual-write, the interaction should follow an asynchronous event-driven model:

sequenceDiagram
    autonumber
    actor User
    participant API as Repository Service
    participant RepoDB as Core Repository DB
    participant Broker as Message Broker (Kafka / SQS)
    participant Scanner as Secret Scanning Consumer
    participant ScanDB as Secret Scanning DB

    User->>API: POST /user/repos
    API->>RepoDB: INSERT INTO repositories
    API->>Broker: Publish Event: 'RepositoryCreated'
    API-->>User: 201 Created

    Note over Broker,Scanner: Asynchronous Processing
    Broker->>Scanner: Consume 'RepositoryCreated'
    Scanner->>ScanDB: INSERT INTO secret_scanning_targets

Why This Architecture Is Resilient:

  • Zero Critical-Path Coupling: If the Secret Scanning database is down, undergoing maintenance, or suffering network partitioning, new repositories can still be created without interruption.
  • Guaranteed Eventual Consistency: Events buffered in the message queue can be retried automatically via dead-letter queues (DLQs) and consumer backoff strategies until the downstream cluster recovers.
  • Isolated Blast Radius: Failures are constrained strictly to the secret scanning domain.

5. Architectural Lessons for Distributed Systems

ConceptAnti-PatternBest Practice
Work IngestionSynchronously doing side-effects in the API request loopUse event emitters and background queues for non-essential tasks
Database AccessCross-service direct writes to foreign database tablesStrict domain boundaries; services exclusively own their storage
Blast RadiusFailure in an auxiliary feature brings down core business flowsGraceful degradation; auxiliary systems must fail silently or queue up
Incident ResponseAttempting live bug fixing during high-severity downtimeAlways prioritize safe, tested rollbacks over hotfixing

Key Takeaways

  1. Identify Unknown Dependencies: Complex systems inevitably accumulate blind spots. System dependency maps and tracing (e.g., OpenTelemetry) are essential for identifying hidden synchronous touchpoints before performing infrastructure migrations.
  2. Protect the Critical Path: Ask for every operation: “If this step fails, must the user’s request fail?” If the answer is no, it does not belong in the synchronous request cycle.
  3. Decouple via Events: Asynchronous messaging (Kafka, SQS, RabbitMQ) provides an architectural shock absorber, decoupling processing rates, infrastructure availability, and deployment lifecycles between distinct services.
Arpit Bhayani

Principal Engineer II at Razorpay - building Agent Studio, Ex-staff engg at GCP Memorystore & Dataproc, Creator of DiceDB, ex-Amazon Fast Data, ex-Director of Engg. SRE and Data Engineering at Unacademy. I spark engineering curiosity through my no-fluff engineering videos on YouTube and my courses