Raxx · internal docs

internal · gated ↑ index

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

Prerequisites

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 ControlMachine Identities. 3. Click Create identity (if creating a new one) or select an existing identity → Auth ConfigurationToken AuthCreate 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 ControlService 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

Known gotchas