A Python-based Security Token Service (STS) for the GitHub API.
Workloads with OIDC tokens (GitHub Actions, Azure AD, GCP, AWS, Kubernetes, Okta, …) exchange them for short-lived, scoped GitHub installation tokens. No PATs required.
Supports multiple GitHub Apps with YAML-based configuration (ideal for Kubernetes ConfigMaps).
Inspired by octo-sts/app — see NOTICE for attribution.
Workload → OIDC Token → github-sts → Scoped GitHub Token
Workload github-sts GitHub
│ │ │
│ GET /sts/exchange │ │
│ ?scope=org/repo │ │
│ &app=my-app │ │
│ &identity=ci │ │
│ Authorization: Bearer │ │
│─────────────────────────>│ │
│ │ Validate OIDC sig/exp │
│ │ Load trust policy │
│ │ Evaluate claims │
│ │ Request install token ──>
│ │<─────────────────────────│
│<─────────────────────────│ │
│ { token, permissions } │ │
A pre-built image is available from GitHub Container Registry:
docker run -p 9999:8080 \
-e PYGITHUBSTS_GITHUB_APP_ID="your_app_id" \
-e PYGITHUBSTS_GITHUB_APP_PRIVATE_KEY="$(cat /path/to/private_key.pem)" \
ghcr.io/alexandreodelisle/py-github-sts:latestThe Helm chart is published to the GitHub Container Registry OCI repository:
# Create credentials secret
kubectl create secret generic github-sts-credentials \
--from-literal=github-app-id="YOUR_GITHUB_APP_ID" \
--from-file=github-app-private-key=/path/to/private_key.pem
# Install from OCI registry
helm install github-sts \
oci://ghcr.io/alexandreodelisle/py-github-sts/github-sts-chart \
--set github.existingSecret="github-sts-credentials"See the chart README for full configuration options including Ingress/HTTPRoute setup.
# Prerequisites: Python 3.14+, uv (https://docs.astral.sh/uv/)
uv sync
export PYGITHUBSTS_GITHUB_APP_ID=your_app_id
export PYGITHUBSTS_GITHUB_APP_PRIVATE_KEY="$(cat /path/to/private_key.pem)"
uv run python -m uvicorn github_sts.main:app --host 0.0.0.0 --port 9999curl http://localhost:9999/health
# {"status":"ok"}Exchange an OIDC token for a scoped GitHub token:
curl -sf -H "Authorization: Bearer $OIDC_TOKEN" \
"http://localhost:9999/sts/exchange?scope=org/repo&app=default&identity=ci"For complete usage examples — including GitHub Actions (native OIDC), Azure AD / Entra ID (CLI and workflows), and more — see EXAMPLES.md.
Policies are fetched directly from GitHub repositories at:
{base_path}/{app_name}/{identity}.sts.yaml
Default: .github/sts/default/{identity}.sts.yaml
Exact match (most secure):
issuer: https://token.actions.githubusercontent.com
subject: repo:org/repo:ref:refs/heads/main
permissions:
contents: read
issues: writeRegex patterns (flexible):
issuer: https://login.microsoftonline.com/<tenant-id>/v2.0
subject_pattern: "[a-f0-9-]+" # Azure AD object ID
claim_pattern:
azp: "<client-id>" # restrict by app registration
permissions:
contents: readRestrict to specific workflow (least-privilege):
issuer: https://token.actions.githubusercontent.com
subject_pattern: "repo:org/repo:.*"
claim_pattern:
job_workflow_ref: "org/repo/.github/workflows/deploy\\.yml@.*"
permissions:
deployments: write
statuses: write| Field | Type | Description |
|---|---|---|
issuer |
string (exact) | OIDC iss claim |
subject |
string (exact) | OIDC sub claim |
subject_pattern |
regex | Used when subject is absent |
claim_pattern |
map[str→regex] | Match any additional JWT claims |
permissions |
map[str→read|write|admin] | GitHub App permissions to grant |
github-sts uses YAML-based configuration, ideal for Kubernetes ConfigMaps. See config/github-sts.example.yaml for a complete example.
export PYGITHUBSTS_CONFIG_PATH=/etc/github-sts/config.yamlEnvironment variables with PYGITHUBSTS_ prefix override YAML config.
| Env var | Default | Description |
|---|---|---|
PYGITHUBSTS_CONFIG_PATH |
— | Path to YAML config file |
PYGITHUBSTS_GITHUB_APP_ID |
required | GitHub App numeric ID |
PYGITHUBSTS_GITHUB_APP_PRIVATE_KEY |
required | PEM string or path to file |
PYGITHUBSTS_GITHUB_APP_NAME |
default |
App name for env-configured app |
PYGITHUBSTS_POLICY_BASE_PATH |
.github/sts |
Base path in repos for policies |
PYGITHUBSTS_POLICY_CACHE_TTL_SECONDS |
60 |
0 = disable |
PYGITHUBSTS_OIDC_ALLOWED_ISSUERS |
— | Comma-sep allowlist (empty = any) |
PYGITHUBSTS_JTI_BACKEND |
memory |
memory | redis |
PYGITHUBSTS_JTI_REDIS_URL |
— | Redis connection (if backend=redis) |
PYGITHUBSTS_AUDIT_FILE_PATH |
./audit.log |
Audit log file path |
PYGITHUBSTS_AUDIT_ROTATION_POLICY |
daily |
daily | size |
PYGITHUBSTS_SERVER_LOG_LEVEL |
INFO |
Log level |
PYGITHUBSTS_METRICS_ENABLED |
true |
Enable/disable metrics |
GET /metrics — Prometheus text format
| Metric | Type | Description |
|---|---|---|
pygithubsts_requests_total |
Counter | HTTP requests by method/path/status |
pygithubsts_request_duration_seconds |
Histogram | HTTP latency |
pygithubsts_requests_in_flight |
Gauge | Concurrent requests |
pygithubsts_token_exchanges_total |
Counter | Exchange attempts by scope/identity/result |
pygithubsts_token_exchange_duration_seconds |
Histogram | Exchange latency |
pygithubsts_oidc_validation_errors_total |
Counter | OIDC failures by issuer/reason |
pygithubsts_policy_loads_total |
Counter | Policy loads by backend/result |
pygithubsts_policy_cache_hits_total |
Counter | Cache hits |
pygithubsts_policy_cache_misses_total |
Counter | Cache misses |
pygithubsts_github_api_calls_total |
Counter | GitHub API calls by endpoint/result |
pygithubsts_github_tokens_issued_total |
Counter | Tokens issued by scope/permissions |
Check for linting issues:
uv run ruff check .Format code:
uv run ruff format .Check formatting without applying:
uv run ruff format --check .Check import organization:
uv run ruff check --select=I .All tests:
uv run pytestSpecific test file:
uv run pytest tests/test_policy.py -vSpecific test:
uv run pytest tests/test_policy.py::TestTrustPolicyExactMatch::test_exact_match_passes -vWith coverage:
uv run pytest --cov=src/github_stsThe project uses Ruff for linting and formatting:
- E/W: pycodestyle (PEP 8)
- F: Pyflakes (undefined names, etc.)
- I: isort (import organization)
- C4: flake8-comprehensions
- B: flake8-bugbear (common bugs)
- UP: pyupgrade (modern Python syntax)
- RUF: Ruff-specific rules
Configuration is in pyproject.toml under [tool.ruff]
We welcome contributions that:
- Improve security
- Enhance usability
- Add observability features
- Extend policy evaluation capabilities
- Improve documentation
MIT License — See LICENSE
Inspiration:
- octo-sts/app — Original Go implementation
GitHub:
Standards:
Tools: