Raxx · internal docs

internal · gated ↑ index

Rotation SOP — DreamHost API Key

Mode: operator-assisted Last validated: 2026-04-24 UTC Validation method: read-only-docs Average duration: 5m Required role: ops

Applies to: DREAMHOST_API_KEY. Used by any infrastructure that performs DNS or DreamObjects management against DreamHost.

Confirmed manual. DreamHost's API itself can list/add/remove API keys via the api-list_keys, api-add_key, api-remove_key metacommands — but these also require an API key with * (all-commands) permission, which is a chicken-and-egg situation for first-time rotation. In practice, the panel UI is the authoritative path; the API metacommands can be used for routine subsequent rotations once we have a "rotation-only" master key set up.

When to run

Prerequisites

Steps

1. Pre-rotation checks

# Confirm current key works (replace with a benign read command from the key's allowed list)
curl -sS "https://api.dreamhost.com/?key=$CURRENT_KEY&cmd=api-list_accessible_cmds&format=json" | jq '.result'
# Expect: "success"

2. Generate the new credential

Panel path (preferred): 1. Navigate to https://panel.dreamhost.com/?tree=home.api. 2. Click "Add a New API Key" (or equivalent). 3. Select the same permission set as the key being rotated (or a more restrictive set if scope reduction is desired). 4. Click Generate. 5. Copy the new key value immediately — shown once.

API path (advanced — only if a separate rotation-only master key already exists):

# Using a master key with `*` permissions to provision a new application key
curl -sS "https://api.dreamhost.com/?key=$MASTER_KEY&cmd=api-add_key&user=$DH_USER&commands=dns-list_records,dns-add_record,dns-remove_record&format=json" | jq '.result'
# Capture the returned key value.

3. Validate the new credential

NEW_KEY="..."
curl -sS "https://api.dreamhost.com/?key=$NEW_KEY&cmd=api-list_accessible_cmds&format=json" | jq '.data | length'
# Expect: a non-zero command count, matching the permission set.

Test a representative read against the consumer's actual workload:

# Example: DNS list
curl -sS "https://api.dreamhost.com/?key=$NEW_KEY&cmd=dns-list_records&format=json" | jq '.result'
# Expect: "success"

4. Store in Infisical

infisical secrets set DREAMHOST_API_KEY="$NEW_KEY" \
  --projectId="$INFISICAL_PROJECT_ID" --env=prod

5. Propagate to downstream consumers

Consumer How
DNS automation (Heroku app or cron) heroku config:set DREAMHOST_API_KEY="$NEW_KEY" -a <app>
GitHub Actions (DNS workflows) gh secret set DREAMHOST_API_KEY -b "$NEW_KEY"
Operator local DM via Slack D0AJ7K184TV

6. Verify downstream

# Trigger the consumer's DNS sync job (or wait for the next scheduled run)
heroku run --app <app> python -m scripts.dns_sync_dry_run
# Expect: no auth errors.

If the consumer is a cron job, monitor the next scheduled execution's logs.

7. Revoke the old credential

Panel path: 1. Panel → API Keys → locate the OLD key. 2. Click "Remove Key" (right-hand side of the row). 3. Confirm.

API path:

curl -sS "https://api.dreamhost.com/?key=$MASTER_KEY&cmd=api-remove_key&key=$OLD_KEY&format=json" | jq '.result'
# Expect: "success"

Verify:

curl -sS "https://api.dreamhost.com/?key=$OLD_KEY&cmd=api-list_accessible_cmds&format=json" | jq '.result, .reason'
# Expect: result="error", reason indicating invalid key

8. Audit log entry

action: secret.rotate.completed
actor: <admin_id>
context: {
  "secret_name": "DREAMHOST_API_KEY",
  "method": "operator-assisted-panel" or "operator-assisted-api"
}

Rollback

Multiple keys can coexist on a DreamHost account. Until step 7:

  1. Revert Heroku config vars to the OLD key (from Infisical history).
  2. Restart consumer apps.
  3. Skip step 7. Investigate.

After step 7 (removal), the old key is unrecoverable. Generate a fresh new key.

Vendor doc references

Known gotchas