Infrastructure Reference β
This guide provides implementation details for fullsend's infrastructure components: the OIDC token mint, Workload Identity Federation (WIF), and secrets deployment. For basic installation instructions, see the Getting Started guides.
Token Mint (OIDC) β GCF Cloud Function β
Managed by:
fullsend mint deploy,fullsend mint enroll,fullsend mint unenroll,fullsend mint status,fullsend mint add-role,fullsend mint remove-role,fullsend mint token
The mint is a GCP Cloud Function that exchanges GitHub OIDC tokens for scoped GitHub App installation tokens. This eliminates long-lived PATs from the system.
Mint Architecture β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Token Mint Flow β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β GitHub Actions Workflow β
β βββββββββββββββββββββββ β
β β id-token: write β β
β β βββββββββββββββββββ β β
β β β Request OIDC JWT β β β
β β ββββββββββ¬βββββββββ β β
β ββββββββββββΌββββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β POST /v1/token β β
β β Authorization: Bearer <OIDC JWT> β β
β β Body: { "role": "coder", "repos": ["my-repo"] } β β
β ββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β GCF: Token Mint β β
β β β β
β β 1. Prevalidate OIDC JWT β β
β β ββ Check iss == token.actions.githubusercontent.com β
β β ββ Extract repository_owner β ALLOWED_ORGS check β
β β β (explicit org list, or * for public mint mode) β
β β ββ Validate job_workflow_ref provenance β
β β (tight: .fullsend / upstream / per-repo; β
β β public: upstream fullsend-ai/fullsend only) β
β β β β
β β 2. STS Token Exchange β β
β β ββ POST securitytoken.googleapis.com β β
β β β grant_type=urn:ietf:params:oauth: β β
β β β grant-type:token-exchange β β
β β ββ WIF pool validates OIDC token β β
β β ββ Returns GCP federated access token β β
β β β β
β β 3. Lookup PEM from Secret Manager β β
β β ββ Secret name: fullsend-{role}-app-pem β β
β β ββ Returns PEM private key bytes β β
β β β β
β β 4. Generate GitHub App JWT β β
β β ββ Sign with PEM key (RS256) β β
β β ββ App ID from ROLE_APP_IDS env β β
β β ββ 10-minute expiry β β
β β β β
β β 5. Find Installation β β
β β ββ GET /app/installations β β
β β ββ Match by org login β β
β β β β
β β 6. Create Scoped Installation Token β β
β β ββ POST /installations/{id}/access_tokens β β
β β ββ Scope to requested repos[] β β
β β ββ Apply RolePermissions() minimum set β β
β β β β
β ββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β Response: { "token": "ghs_...", "expires_at": "..." } β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββRole Permissions Matrix β
The mint enforces minimum permission sets per role. Tokens cannot exceed these scopes. Custom roles can be registered via the standalone mint's CUSTOM_ROLE_PERMISSIONS env var β see the standalone mint guide for details.
| Role | contents | pull_requests | issues | actions | checks | workflows | actions_variables | organization_projects | metadata |
|---|---|---|---|---|---|---|---|---|---|
| fullsend | write | write | β | write | β | write | read | β | read |
| triage | read | β | write | β | β | β | β | β | read |
| coder | write | write | write | β | read | β | β | β | read |
| review | read | write | write | β | read | β | β | β | read |
| fix | write | write | write | β | β | β | β | β | read |
| retro | read | write | write | read | β | β | β | β | read |
| prioritize | read | β | write | β | β | β | β | write | read |
Mint Security Controls β
Mode is inferred from ALLOWED_ORGS β there is no separate trust-mode flag. See ADR 0059 for the full decision.
Tight mint (default): explicit comma-separated org list (no *).
- ALLOWED_ORGS: Only listed orgs may mint tokens
- ALLOWED_WORKFLOW_FILES: Fail-closed allowlist of workflow filenames (use
*to allow any basename) - job_workflow_ref validation:
.fullsendconfig repo,fullsend-ai/fullsendupstream reusables, or registered per-repo workflows (PER_REPO_WIF_REPOS) - PER_REPO_WIF_REPOS: Repos using dedicated WIF providers (repo-scoped isolation)
Public mint: ALLOWED_ORGS is *.
- ALLOWED_ORGS: Any org may mint (cross-org isolation still enforced at installation lookup)
- job_workflow_ref validation: Only
fullsend-ai/fullsend/.github/workflows/(any ref β tag, branch, or SHA) - PER_REPO_WIF_REPOS: Leave unset or empty (GCF mint: all repos use
WIF_PROVIDER_NAME) - ALLOWED_WORKFLOW_FILES: Basename gate is not applied in public mode
- mint enroll: Succeeds without changing mint configuration (org registration is unnecessary); mint unenroll for individual orgs is rejected
GCF mint (STS verification) only: The hosted Cloud Function uses STSVerifier, which exchanges each OIDC JWT with GCP STS against WIF_PROVIDER_NAME. A permissive WIF provider (CEL that does not enumerate orgs/repos) must back that env var, or STS will reject tokens from orgs outside the provider's attributeCondition even when mintcore prevalidation passes. Use mint deploy --public to provision ALLOWED_ORGS=* and permissive WIF together; tight-mode mint deploy (default) and mint enroll continue to use org-scoped WIF. Redeploys must match the mint mode (--public for public, omit for tight).
Standalone mint (JWKS verification): cmd/mint uses JWKSVerifier β direct GitHub JWKS signature checks with no STS or WIF. Public mode is fully determined by ALLOWED_ORGS and workflow provenance in mintcore; WIF provisioning is not applicable.
- Minimum permissions: Tokens are scoped to the role's minimum permission set, not the App's full permissions (both modes)
Multi-Org Support β
A single mint instance can serve multiple orgs:
- Tight mode:
EnsureOrgInMint()additively appends orgs toALLOWED_ORGS - Public mode:
ALLOWED_ORGS=*β no per-org registration required; rollback to tight mode is config-only (replace*with an explicit org list) ROLE_APP_IDSmaps{role}to GitHub App IDs (shared across all enrolled orgs)- Org isolation at token issuance uses the OIDC
repository_ownerclaim and GitHub App installation lookup β not per-org app ID entries
Status Endpoint β
GET /v1/status returns the configured roles available for the authenticated caller's org.
- Authentication: Bearer OIDC JWT (same as
/v1/token) - Authorization: Any valid OIDC token from an allowed org β no role restriction
- Response:json
{"org": "my-org", "roles": ["coder", "review", "triage"]} - Use case: Workflow diagnostics β discover which roles are available before requesting a token
- Security: Returns only the requesting org and its role names (not app IDs, not other orgs' roles)
Inference β Agent Platform with Workload Identity Federation β
Managed by:
fullsend inference provision,fullsend inference deprovision,fullsend inference status
Inference authentication uses GCP Workload Identity Federation (WIF) to allow GitHub Actions to authenticate to Agent Platform without service account keys.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Inference Authentication Flow β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β GitHub Actions Runner β
β βββββββββββββββββββββββ β
β β OIDC JWT β β
β β (id-token: write) β β
β ββββββββββββ¬βββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββ β
β β GCP Security Token Service (STS)β β
β β β β
β β WIF Pool: fullsend-inference β β
β β WIF Provider: github-oidc β β
β β β β
β β Validates OIDC issuer: β β
β β token.actions.githubusercontent.com β
β β β β
β β Attribute mapping: β β
β β sub β assertion.sub β β
β β repo β assertion.repository β β
β ββββββββββββ¬βββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββ β
β β Federated Access Token β β
β β (short-lived, auto-rotated) β β
β ββββββββββββ¬βββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββ β
β β Agent Platform API β β
β β β β
β β Project: FULLSEND_GCP_PROJECT_IDβ β
β β Region: FULLSEND_GCP_REGION β β
β β β β
β β Models: β β
β β - claude-haiku-4-5 β β
β β - claude-sonnet-4-6 β β
β β - claude-opus-4-6 β β
β βββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββWIF Provisioning β
During installation, the GCF provisioner creates:
- Service Account β For the Cloud Function identity
- WIF Pool β
fullsend-inferencefor inference,fullsend-poolfor mint - WIF Provider β Maps GitHub OIDC claims to GCP attributes
- IAM Bindings β Grants
roles/aiplatform.userto federated identities - Per-repo providers (per-repo mode) β Scoped WIF provider per repository via
mintcore.BuildRepoProviderID()
GitHub Secrets & Variables Deployment β
Individual values can be updated with
fullsend github set <target> <key> <value>. See Operations for the full configuration management guide.
Secrets and variables are deployed at different scopes depending on the installation mode.
Per-Org Mode Secrets/Variables β
Org-level variable:
FULLSEND_MINT_URLβ URL of the token mint Cloud Function
.fullsend repo variables (per role):
FULLSEND_{ROLE}_CLIENT_IDβ GitHub App client ID
.fullsend repo secrets (inference):
FULLSEND_GCP_PROJECT_IDβ GCP project for inferenceFULLSEND_GCP_WIF_PROVIDERβ WIF provider resource name
.fullsend repo variables (inference):
FULLSEND_GCP_REGIONβ GCP region for inference (default:global)
.fullsend repo variable (dot-repo fix):
FULLSEND_MINT_URLβ Duplicate of org variable (dot-prefixed repos can't read org-level variables)
Per-Repo Mode Secrets/Variables β
Target repo secrets:
FULLSEND_GCP_PROJECT_IDFULLSEND_GCP_WIF_PROVIDER
Target repo variables:
FULLSEND_MINT_URLFULLSEND_GCP_REGIONFULLSEND_PER_REPO_INSTALLβ Flag indicating per-repo mode (set to "true")
Secrets Layer Behavior β
- Install (OIDC mode): No-op β PEMs are stored in GCP Secret Manager, not as repo secrets. Only client IDs are written as repo variables.
- Analyze: Checks that expected secrets/variables exist. Cannot verify secret values (GitHub Secrets API is write-only for values). Flags stale secrets from pre-OIDC deployments.
- Uninstall: Deletes repo secrets and variables for all managed names.
Inference Layer Behavior β
- Install: Unconditionally writes secrets and variables (no way to check if values changed since GitHub doesn't expose secret values).
- Analyze: Checks presence of
FULLSEND_GCP_PROJECT_ID,FULLSEND_GCP_WIF_PROVIDER,FULLSEND_GCP_REGION.
GCF Provisioner Flow β
The GCF provisioner handles full GCP infrastructure deployment:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GCF Provisioner: Provision() Flow β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββ β
β β Get GCP project β resourcemanager.projects.get β
β β number β β
β βββββββββββ¬ββββββββββ β
β βΌ β
β βββββββββββββββββββββ β
β β Create Service β fullsend-mint@{project}.iam β
β β Account β (skip if exists) β
β βββββββββββ¬ββββββββββ β
β βΌ β
β βββββββββββββββββββββ β
β β Create WIF Pool β fullsend-inference (or fullsend-pool) β
β β β (skip if exists) β
β βββββββββββ¬ββββββββββ β
β βΌ β
β βββββββββββββββββββββ β
β β Create WIF β github-oidc β
β β Provider β OIDC issuer: β
β β β token.actions.githubusercontent.com β
β β β (skip if exists) β
β βββββββββββ¬ββββββββββ β
β βΌ β
β βββββββββββββββββββββ β
β β Grant Agent β roles/aiplatform.user β
β β Platform access β on the inference project β
β β to federated IDs β β
β βββββββββββ¬ββββββββββ β
β βΌ β
β βββββββββββββββββββββ β
β β Store PEMs in β fullsend-{role}-app-pem β
β β Secret Manager β once per agent role (shared) β
β βββββββββββ¬ββββββββββ β
β βΌ β
β βββββββββββββββββββββ β
β β Deploy Cloud β Source: embedded mint code β
β β Function β SHA256 hash comparison to skip β
β β β redundant deploys β
β β β Env vars: β
β β β ALLOWED_ORGS β
β β β GCP_PROJECT_NUMBER β
β β β WIF_POOL_NAME β
β β β WIF_PROVIDER_NAME β
β β β ROLE_APP_IDS β
β β β OIDC_AUDIENCE β
β βββββββββββ¬ββββββββββ β
β βΌ β
β βββββββββββββββββββββ β
β β Health check β Exponential backoff polling β
β β β POST /v1/token (expect 401) β
β βββββββββββ¬ββββββββββ β
β βΌ β
β Return: FULLSEND_MINT_URL = https://{region}-{project}. β
β cloudfunctions.net/fullsend-mint β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββSource Hash Optimization β
The GCF provisioner avoids redundant Cloud Function deployments by computing a SHA256 hash of the source zip and comparing it to metadata stored on the deployed function. Only deploys when the hash changes.
See Also β
- Getting Started β Standard per-repo installation
- Mint service administration β Deploying and managing the token mint
- Standalone Mint β Running the mint without GCP, with custom agent roles
- Advanced setup β Alternative installation paths and setup flags
- Running agents locally β Run agents locally (binary download, GCP credentials, per-agent env vars)
