How actions/checkout Uses includeIf.gitdir for Secure Credential Isolation
The actions/checkout action isolates authentication credentials by writing tokens to a temporary UUID-named config file under RUNNER_TEMP and selectively including it via includeIf.gitdir directives, ensuring credentials are only visible to the specific repository, worktrees, and submodules during job execution.
The actions/checkout action handles repository cloning and authentication for millions of GitHub Actions workflows daily. To prevent personal access tokens from persisting in global Git configuration or leaking across repository boundaries on shared runners, the action implements a strict credential isolation mechanism using Git's includeIf.gitdir configuration directive.
The Architecture of Credential Isolation
The isolation strategy centers on keeping authentication material outside the standard Git configuration hierarchy. Instead of storing tokens in ~/.gitconfig or the repository's local .git/config, the action uses a temporary credential file that is selectively loaded only when Git operates within specific directories.
Temporary Credentials File Creation
In src/git-auth-helper.ts, the GitAuthHelper.getCredentialsConfigPath() method generates a unique file path for each workflow run. The function constructs a filename pattern git-credentials-<uuid>.config inside the RUNNER_TEMP directory (lines 25-30).
This file initially receives a placeholder header (AUTHORIZATION: basic ***) to prevent the real token from appearing in process argument lists during initial Git configuration commands. The configureToken() method then rewrites the file with the actual base64-encoded token after the configuration structure is established (lines 33-40).
Wiring Credentials with includeIf.gitdir
The action creates conditional Git configuration entries that link specific Git directories to the temporary credentials file. According to the source code in src/git-auth-helper.ts, the helper constructs configuration keys using the pattern:
const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`;
This directive tells Git to include the temporary credentials file only when the repository's Git directory matches the specified path. The action adds multiple variants to handle edge cases:
- Host repository: The primary repository at
${gitDir}.path(lines 73-81) - Worktrees: Additional entries for
.../worktrees/*.pathpatterns to support Git worktree operations - Container environments: When running inside Docker, separate entries map
/github/workspaceto a container-visible copy of the credentials file at/github/runner_temp/(lines 84-92)
Submodule and Nested Repository Support
For repositories with submodules, configureSubmoduleAuth() iterates through each submodule's .git directory and repeats the includeIf.gitdir wiring (lines 77-89). This ensures that submodule fetch operations use the same isolated credential context without exposing tokens to the parent repository's configuration or sibling submodules.
Security Guarantees and Cleanup
The includeIf.gitdir approach provides path-based isolation because Git evaluates these conditional includes based on the current working directory's repository root. Other repositories on the same runner cannot trigger the inclusion of the temporary credentials file because their Git directories do not match the scoped paths.
Automated Cleanup
After job completion, removeToken() and removeIncludeIfCredentials() execute to scrub all traces from the runner:
- Locate all
includeIf.gitdirentries referencinggit-credentials-*.configfiles - Remove these configuration keys from Git's config
- Delete the temporary file from
RUNNER_TEMP
This ensures that even if the runner is reused for subsequent jobs, no authentication material persists in Git configuration or disk.
Implementation Example
The following TypeScript excerpt illustrates how the action configures this isolation internally:
import {createAuthHelper} from './git-auth-helper.js'
import {GitCommandManager} from './git-command-manager.js'
const git = new GitCommandManager()
const authHelper = createAuthHelper(git, {
authToken: process.env.GITHUB_TOKEN,
persistCredentials: true,
nestedSubmodules: true
})
// Configure isolated credential includes for host and submodules
await authHelper.configureAuth()
await authHelper.configureSubmoduleAuth()
For manual verification or debugging in a running workflow, you can inspect the generated conditional includes:
# List all includeIf.gitdir configurations
git config --local --get-regexp 'includeIf.gitdir'
# Output will show entries like:
# includeIf.gitdir:/__w/repo/repo/.git.path /home/runner/work/_temp/git-credentials-abc123.config
Summary
- Temporary file isolation: Credentials are stored in UUID-named files under
RUNNER_TEMP, never in global or repository-persistent configuration. - Path-scoped includes: The
includeIf.gitdir:${path}.pathdirective ensures credentials are only loaded for specific repository directories, worktrees, and submodules. - Container awareness: Separate configuration entries handle the translation between host runner paths and container-internal paths (
/github/workspace). - Automatic cleanup: The
removeIncludeIfCredentials()function scrubs all references and temporary files after job execution, preventing cross-job contamination.
Frequently Asked Questions
What is includeIf.gitdir in Git?
includeIf.gitdir is a Git configuration directive that conditionally includes additional configuration files based on whether the current Git repository's directory matches a specified path pattern. When the pattern matches, Git loads the referenced config file; otherwise, it ignores those settings entirely. The actions/checkout action leverages this to create scoped "firewalls" between repositories on shared self-hosted runners.
How does actions/checkout prevent token leakage between jobs?
The action prevents leakage by never writing credentials to ~/.gitconfig or the repository's .git/config. Instead, it uses includeIf.gitdir to point to a temporary file that is deleted after the job completes. Because the include directive is scoped to a specific Git directory path, other repositories or subsequent jobs cannot access the token even if they run on the same physical runner.
Why does the action use a placeholder token initially?
The placeholder mechanism (AUTHORIZATION: basic ***) exists to prevent the real token from appearing in process argument lists. When configureToken() executes git config commands, the real token could be visible to other processes via /proc or system monitoring tools. Using a placeholder during the initial git config call, then rewriting the file directly with the actual token, minimizes the window where sensitive data appears in command-line arguments or shell history.
Are Docker container workflows protected by this mechanism?
Yes. The action detects containerized environments and creates additional includeIf.gitdir entries that map the container-internal repository path (/github/workspace/.git) to a container-accessible copy of the credentials file (/github/runner_temp/...). This ensures that Git commands running inside Docker containers can authenticate while maintaining the same isolation guarantees as host-runner operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →