Authentication
GitHub auth is the only credential Repo2RLEnv really cares about, because every pipeline starts by cloning a repo or calling the GitHub API. The other tokens (HF, LLM, E2B) pass straight through to upstream SDKs, which already resolve them on their own.
This page covers GitHub first, then the others briefly.
GitHub auth: three valid paths
Repo2RLEnv shells out to gh for clone + PR-list operations. gh itself ranks the GH_TOKEN / GITHUB_TOKEN env vars above any keychain creds, which is why the three paths below are interchangeable.
Path A: gh auth login (most ergonomic)
gh auth loginThis stores creds in the macOS Keychain (or ~/.config/gh/ on Linux), and gh is then authenticated for both public and private repos. Repo2RLEnv resolves the token with gh auth token. No env-var setup needed, and it's what most HF / OSS contributors already have.
Path B: A read-scoped Personal Access Token (no gh auth login required)
If you'd rather not run gh auth login, generate a fine-grained PAT at https://github.com/settings/tokens?type=beta with Contents: Read scope on the repos you care about. Then set it in your shell or in a .env file:
# In your shell or a .env file at project root
export GITHUB_TOKEN=ghp_xxxThis works because gh prefers GH_TOKEN / GITHUB_TOKEN from the environment over its keychain, so you never have to log in interactively.
Path C: Explicit per-repo env var
If you have multiple tokens (different orgs, different scopes) and want to be explicit, set the env-var name in your config:
repo:
url: "myorg/private-repo"
access: "private"
auth_token_env: "MY_ORG_PAT"Then export MY_ORG_PAT=ghp_xxx. Repo2RLEnv reads the name, never the value.
Resolution order
First match wins:
repo.auth_token_env(if explicitly set in config)gh auth token(ifghis on PATH andauth.use_gh_cli=true, default)$GITHUB_TOKEN- None: anonymous clone (fails with a clear error if
access="private")
Implementation: src/repo2rlenv/auth.py:resolve_github_token.
Input sources (GitHub · GitLab · local)
--repo accepts more than a GitHub owner/name:
| Input | Source | Token | Notes |
|---|---|---|---|
owner/name or https://github.com/... | GitHub | resolve_github_token chain above | unchanged (the default) |
https://gitlab.com/owner/name | GitLab | repo.auth_token_env → $GITLAB_TOKEN (public needs none) | clone via oauth2:<token>@ |
/abs/path, ./rel, ~/x, file://… (Windows: also C:\path, .\rel, UNC) | Local | none (no gh shell-out) | canonicalized to file://<abspath> |
Source-aware resolution lives in auth.py:resolve_repo_token; detection + capabilities in sources.py.
Capability gating. Each source declares which platform data it can serve (pull_requests, issues, commit_api); each pipeline declares what it requires. generate blocks an incompatible combo up front. In practice, the git/source pipelines (commit_runtime, code_instruct, equivalence_tests) run on any source. pr_diff / pr_runtime mine pull/merge requests, so they run on GitHub or GitLab (gitlab.com merge requests via the REST API). cve_patches is GitHub-only (OSV → github.com fix-commits + the GitHub commit API).
Private repos at task build time
Generation only needs the token to fetch PR metadata + diffs via gh (resolved above). But a runnable pr_diff task also clones the repo inside its Docker image at build time. For private source repos, the consumer building that image supplies the token as a Docker build arg:
harbor run -p ./datasets/<private-dataset> -a oracle --env docker \
--build-arg GITHUB_TOKEN=$GITHUB_TOKENThe emitted Dockerfile declares ARG GITHUB_TOKEN= (empty default). When it's set, the clone goes through an x-access-token:<token>@github.com/... URL, and the remote is reset to the clean URL straight after. The token never persists in git config inside the image and is never baked into a layer. Public repos need no build arg; the clone falls back to the anonymous URL.
The same generation-time token also packages bootstrap-built images for _runtime pipelines (cloned host-side and docker cp'd in, never embedded). See reference/BOOTSTRAP.md.
Failure modes
| Symptom | Cause | Fix |
|---|---|---|
gh CLI not found on PATH | gh not installed | brew install gh |
gh auth list reports "not logged in", env empty, public repo | No token resolved at all | gh auth login OR export GITHUB_TOKEN=... |
401 Unauthorized on private repo | Token has wrong scope | Regenerate PAT with Contents: Read on the repo |
404 Not Found on private repo | Token doesn't have access OR repo doesn't exist | Confirm via gh repo view <owner>/<name> |
Other services (brief)
These are passthroughs. Repo2RLEnv reads them but leaves resolution to the upstream SDK.
Hugging Face Hub
huggingface_hub auto-resolves a token from ~/.cache/huggingface/token (set by huggingface-cli login) or the HF_TOKEN env var. We don't override either default. For private dataset push, the token needs write scope on the namespace.
LLM providers
LiteLLM resolves provider keys from provider-default env vars:
| Provider | Env var |
|---|---|
| Anthropic | ANTHROPIC_API_KEY |
| OpenAI | OPENAI_API_KEY |
| Hugging Face Router | HF_TOKEN |
| Together | TOGETHER_API_KEY |
| Groq | GROQ_API_KEY |
Override with --llm-key-env VAR (or llm.api_key_env in config) if you have non-default names. Providers not in the table are resolved by LiteLLM's own per-provider lookup.
Self-hosted models need no key. Point --llm-endpoint (or llm.endpoint) at any OpenAI-compatible server and use hosted_vllm/<model> or openai/<model>. The provider-default key is never forwarded to a custom endpoint: openai/ gets a placeholder, and hosted_vllm/ honours HOSTED_VLLM_API_KEY if it's set. Pass --llm-key-env VAR to send a specific key. ollama/<model> reads OLLAMA_API_BASE on its own.
Container registry (image distribution for _runtime datasets)
repo2rlenv push distributes bootstrap images to an OCI registry when a dataset ships environment/Dockerfiles. Creds resolve from explicit env vars first:
| Registry | Env vars | Notes |
|---|---|---|
| GHCR | GHCR_TOKEN or GITHUB_TOKEN | one-time gh auth refresh -h github.com -s write:packages |
| Docker Hub | DOCKER_USERNAME + DOCKER_TOKEN (PAT) | preferred over the credstore (whose OAuth token is often pull-only); pushes under the Docker Hub user's namespace |
If no registry verifies, push falls back to inline mode: each task bakes its own rebuild recipe and stays reproducible with no registry at all. Full details: reference/REGISTRY_AUTH.md.
E2B
If you use Harbor with the E2B provider (harbor run -d <dataset> -e e2b ...), the E2B SDK reads E2B_API_KEY from env. Repo2RLEnv itself doesn't run E2B; Harbor does.
What's never stored
- Token values are never written to task directories, the lockfile, git, or logs
- Container registry credentials are sandbox-side (
docker login, IAM roles) - The spec forbids verifier-time secrets. A task that needs a paid API key to run is non-conformant