Rotation SOP — Infisical Service Token / Machine Identity
Mode: operator-assisted Last validated: 2026-04-24 UTC Validation method: read-only-docs Average duration: 6m Required role: superadmin
Applies to: legacy INFISICAL_SERVICE_TOKEN and any new machine-identity tokens minted for Raxx infrastructure. Used by Heroku apps, CI workflows, and the console to read secrets from vault.raxx.app.
Migration in progress: Infisical service tokens are deprecated in favor of machine identities. New tokens should be machine-identity tokens; legacy service tokens should be migrated on next rotation cycle. Track legacy tokens in the routing matrix with a "migrate to machine-identity" flag.
When to run
- Scheduled rotation (cadence: every 90 days)
- Operator-initiated (suspected compromise, off-cycle)
- After incident (employee offboarding, accidental log of token)
- BEFORE planned migration from legacy service tokens to machine identities (combine the migration with the next rotation)
Prerequisites
- [ ] Infisical web UI access at
https://vault.raxx.app/loginvia SSO - [ ] Project + environment for the token known
- [ ] Existing token in Infisical with history (yes, we use Infisical to track our Infisical tokens — bootstrap problem; see Known gotchas)
- [ ] Downstream consumer list (every Heroku app + CI workflow that uses Infisical CLI/SDK to read secrets)
- [ ] Decision: legacy service token (deprecated) vs machine identity (preferred)
Steps
1. Pre-rotation checks
# Confirm current token works (using Infisical CLI)
INFISICAL_TOKEN="$CURRENT_TOKEN" infisical secrets list --projectId="$INFISICAL_PROJECT_ID" --env=prod | head -3
# Expect: a list of secret names (no values needed for this check)
2. Generate the new credential
Machine identity (preferred):
1. Navigate to https://vault.raxx.app/.
2. Project → Access Control → Machine Identities.
3. Click Create identity (if creating a new one) or select an existing identity → Auth Configuration → Token Auth → Create token.
4. Configure:
- Name: raxx-prod-rotation-2026-04-24
- Auth method: Token Auth (simplest service-token replacement)
- TTL: 90 days (matches rotation cadence)
- Permissions: scoped to read-only on the project
5. Click Create. Copy the token value immediately.
Legacy service token (deprecated — only use for direct in-place rotation of an existing legacy token, not for new tokens): 1. Project → Access Control → Service Tokens (legacy section, may be hidden behind a deprecation banner). 2. Generate new service token with the same scope as the old one. 3. Copy.
3. Validate the new credential
NEW_TOKEN="..."
INFISICAL_TOKEN="$NEW_TOKEN" infisical secrets list --projectId="$INFISICAL_PROJECT_ID" --env=prod | wc -l
# Expect: a count matching step 1.
For machine-identity tokens, also confirm the token's auth method works for the consumer:
# CLI-based check
INFISICAL_TOKEN="$NEW_TOKEN" infisical secrets get HEROKU_API_KEY --projectId="$INFISICAL_PROJECT_ID" --env=prod --plain | head -c 8
# Expect: the first 8 chars of the secret value (sanity that the token can read).
4. Store in Infisical (bootstrap problem note)
The new Infisical token is itself a secret — but we use Infisical to store secrets. Avoiding circularity:
# Option A: store the new Infisical token in Infisical (works because the OLD token is still valid until step 7)
infisical secrets set INFISICAL_SERVICE_TOKEN="$NEW_TOKEN" \
--projectId="$INFISICAL_PROJECT_ID" --env=prod \
--token="$OLD_TOKEN"
For first-time bootstrap or if no working token exists, the operator stores the token in their secrets manager (1Password, OS keychain) directly.
5. Propagate to downstream consumers
| Consumer | How |
|---|---|
| Heroku apps (Raptor, console, etc.) | heroku config:set INFISICAL_SERVICE_TOKEN="$NEW_TOKEN" -a <app> |
| GitHub Actions (workflows that pull from Infisical) | gh secret set INFISICAL_SERVICE_TOKEN -b "$NEW_TOKEN" |
| Operator local zshrc | DM via Slack D0AJ7K184TV |
6. Verify downstream
# After dyno restart, the consumer should successfully fetch its other secrets at boot
heroku logs --tail -a raxx-api-prod | grep -iE 'infisical|secret_fetch'
# Expect: successful secret fetch logs, no 401s.
# Test the application's actual workload
curl -sS https://api.raxx.app/health/dependencies | jq '.infisical'
# Expect: {"ok": true}
7. Revoke the old credential
Machine identity:
1. https://vault.raxx.app/ → Project → Access Control → Machine Identities → old identity → Revoke (or delete the specific token under it, leaving the identity active).
Legacy service token: 1. Project → Access Control → Service Tokens (legacy section) → old token → Delete.
Verify:
INFISICAL_TOKEN="$OLD_TOKEN" infisical secrets list --projectId="$INFISICAL_PROJECT_ID" --env=prod
# Expect: 401 / unauthorized
8. Audit log entry
action: secret.rotate.completed
actor: <admin_id>
context: {
"secret_name": "INFISICAL_SERVICE_TOKEN",
"token_type": "machine-identity" or "service-token-legacy",
"method": "operator-assisted-vault-ui"
}
Rollback
Until step 7, both old and new tokens are valid. To roll back: 1. Revert Heroku config vars to OLD token (operator may need to recover it from local secret store if Infisical-stored history is unavailable due to bootstrap chicken-and-egg). 2. Restart consumers. 3. Skip step 7. Investigate.
After step 7 (revoke), the old token is dead. Generate a fresh new token — be aware of the bootstrap problem if no other working token exists.
Vendor doc references
- Machine identities: https://infisical.com/docs/documentation/platform/identities/machine-identities
- Service tokens (deprecated): https://infisical.com/docs/documentation/platform/identities/service-token
- Token Auth method: https://infisical.com/docs/documentation/platform/identities/token-auth
- Vault host:
https://vault.raxx.app/
Known gotchas
- Service tokens are deprecated. Migrate to machine identities on next rotation. Track in routing matrix.
- Bootstrap problem. The Infisical token is the master key for fetching other secrets. If lost, operator recovers via the Infisical UI directly (using SSO login, not a token). Always keep at least one token in a separate secret store (1Password, OS keychain) for break-glass recovery.
- TTL is configurable on machine identities. Set explicitly; don't rely on default. 90 days matches our cadence.
- Multiple tokens per machine identity are supported — overlap during rotation is easy.
- Vault host availability matters. If
vault.raxx.appis down, rotation cannot proceed. Coordinate with vault host runbook (docs/ops/runbooks/vault.md— to be created if absent) before scheduling. - The Infisical CLI itself caches credentials. After rotating, run
infisical login(or unsetINFISICAL_TOKEN) to ensure fresh auth on operator workstation. - Machine identities support multiple auth methods (Token, OIDC, AWS IAM, etc.). Token Auth is the simplest replacement for legacy service tokens; consider OIDC/IAM for cloud-native consumers in a future migration.