Infisical CI Secrets Migration
Centralized secret management for GitHub Actions via self-hosted Infisical.
Architecture
Section titled βArchitectureβGitHub Actions Workflow β βββ secrets.INFISICAL_UNIVERSAL_AUTH_CLIENT_ID ββ βββ secrets.INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET βββ 3 GitHub Secrets (only) βββ secrets.INFISICAL_PROJECT_ID ββ β βΌ .github/actions/infisical-secrets/action.yml β 1. Validate creds β 2. Install Infisical CLI β 3. infisical login --method=universal-auth β 4. infisical export --format=dotenv β 5. Append to $GITHUB_ENV + mask values βΌ All downstream steps see secrets as env varsGitHub Secrets (only 3 required)
Section titled βGitHub Secrets (only 3 required)β| Secret | Purpose |
|---|---|
INFISICAL_UNIVERSAL_AUTH_CLIENT_ID |
Machine identity client ID |
INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET |
Machine identity client secret |
INFISICAL_PROJECT_ID |
Infisical project identifier |
GITHUB_TOKEN is auto-provided by GitHub and not managed by Infisical.
Infisical Secret Paths
Section titled βInfisical Secret PathsβAll CI secrets are organized under /ci/ with environment-scoped slugs
(staging, production).
| Path | Secrets | Used By |
|---|---|---|
/ci/shared |
TURBO_TOKEN |
validate.yml (release.yml uses the ordermatic-release-bot GitHub App via RELEASE_APP_ID + RELEASE_APP_PRIVATE_KEY rather than RELEASE_PAT) |
/ci/cloudflare |
CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_PAGES_API_TOKEN |
deploy-email-worker, deploy-content-mcp, deploy-partner-data-api, deploy-tidycal-hubspot-sync, graphify-nightly, deploy-docs (Pages token) |
/ci/gcp |
GCP_SA_KEY |
gcp-deploy |
/ci/clerk |
PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY |
deploy-branch-gate, validate, version-aware-build, flagsmith-seed, staging-canary (E2E) |
/ci/posthog |
PUBLIC_POSTHOG_KEY, PUBLIC_POSTHOG_HOST |
gcp-deploy / Cloud Build (/build/webapp) |
/ci/eval |
EVAL_SERVICE_TOKEN, LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, R2_* |
eval, match-correctness-eval |
/ci/flagsmith |
FLAGSMITH_API_URL, FLAGSMITH_ADMIN_EMAIL, FLAGSMITH_ADMIN_PASSWORD, FLAGSMITH_ENVIRONMENT_KEY |
flagsmith-seed |
/ci/e2e |
STAGING_TEST_EMAIL, STAGING_TEST_PASSWORD, E2E_FLAG_OVERRIDE_SECRET |
staging-canary |
/ci/database |
DATABASE_URL (env-scoped: staging has STAGING_DATABASE_URL, production has PROD_DATABASE_URL) |
db-maintenance, flagsmith-seed, stuck-order-monitor |
/ci/integrations |
GEMINI_API_KEY, STAGING_WEBAPP_URL |
graphify-nightly, version-aware-build |
Migrated Workflows
Section titled βMigrated Workflowsβ| Workflow | Secret Paths | Notes |
|---|---|---|
eval.yml |
/ci/eval |
E2E test credentials + Langfuse |
deploy-email-worker.yml |
/ci/cloudflare |
Both staging + production jobs |
deploy-docs.yml |
/ci/cloudflare |
Pages deploy; token needs Pages:Edit |
deploy-tidycal-hubspot-sync.yml |
/ci/cloudflare |
Single production deploy |
deploy-branch-gate.yml |
/ci/clerk |
Typecheck jobs (webapp + internal) |
validate.yml |
/ci/shared, /ci/clerk |
TURBO_TOKEN + Clerk key |
version-aware-build.yml |
/ci/clerk, /ci/cloudflare, /ci/integrations |
Build + deploy jobs |
db-maintenance.yml |
/ci/database |
Environment-scoped (staging/production) |
flagsmith-seed.yml |
/ci/flagsmith, /ci/clerk, /ci/database |
Multi-path, identity sync |
graphify-nightly.yml |
/ci/cloudflare, /ci/integrations |
Gemini + Cloudflare R2 |
match-correctness-eval.yml |
/ci/eval |
Langfuse keys |
gcp-deploy.yml |
/ci/gcp |
GCP service account |
release.yml |
GitHub App (ordermatic-release-bot) |
Uses RELEASE_APP_ID + RELEASE_APP_PRIVATE_KEY for PR creation and auto-merge (replaced RELEASE_PAT) |
staging-canary.yml |
/ci/e2e |
Playwright E2E secrets |
stuck-order-monitor.yml |
/ci/database |
Read-only prod DB |
auto-deploy-main.yml |
/ci/e2e |
Post-promotion smoke tests |
Workflows NOT Migrated (no secrets needed)
Section titled βWorkflows NOT Migrated (no secrets needed)βThese workflows use only GITHUB_TOKEN (auto-provided) or no secrets at all:
detect-versions.ymltest-migrations.ymlcodeql.ymldeploy-dagster.yml(uses GITHUB_TOKEN only)deploy-temporal.yml(uses GITHUB_TOKEN only)
Setup Instructions
Section titled βSetup Instructionsβ1. Create Machine Identity in Infisical
Section titled β1. Create Machine Identity in Infisicalβ- Go to
https://infisical.ordermatic.co> Project Settings > Machine Identities - Create a new identity (e.g.,
github-actions-ci) - Enable Universal Auth method
- Copy the Client ID and Client Secret
2. Create Folder Structure in Infisical
Section titled β2. Create Folder Structure in InfisicalβCreate the following folders under both staging and production environments:
/ci/shared/ci/cloudflare/ci/gcp/ci/clerk/ci/posthog/ci/eval/ci/flagsmith/ci/e2e/ci/database/ci/integrations
3. Populate Secrets
Section titled β3. Populate SecretsβCopy the current GitHub Secrets values into the corresponding Infisical paths. See the pathβsecret mapping table above for which secrets go where.
4. Grant Machine Identity Access
Section titled β4. Grant Machine Identity AccessβGrant the github-actions-ci identity read access to /ci/* in both
staging and production environments.
5. Configure GitHub Secrets
Section titled β5. Configure GitHub SecretsβAdd these 3 secrets to the GitHub repository:
INFISICAL_UNIVERSAL_AUTH_CLIENT_ID= Client ID from step 1INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET= Client Secret from step 1INFISICAL_PROJECT_ID= your Infisical project ID
6. Remove Old GitHub Secrets
Section titled β6. Remove Old GitHub SecretsβAfter verifying all workflows work with Infisical, remove the ~40 individual secrets from GitHub Settings > Secrets.
Composite Action Usage
Section titled βComposite Action Usageβ# In your workflow job:env: INFISICAL_UNIVERSAL_AUTH_CLIENT_ID: ${{ secrets.INFISICAL_UNIVERSAL_AUTH_CLIENT_ID }} INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET: ${{ secrets.INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET }} INFISICAL_PROJECT_ID: ${{ secrets.INFISICAL_PROJECT_ID }}
steps: - uses: actions/checkout@v4
- name: Fetch secrets from Infisical uses: ./.github/actions/infisical-secrets with: environment: staging # or 'production' secret-path: /ci/cloudflare # path to your secrets
# All secrets are now available as env vars - name: Use secrets run: echo "Token is masked: $CLOUDFLARE_API_TOKEN"