rotate-key

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Rotate API Key / Secret

轮换API密钥/密钥

Rotate a secret or API key across all locations in the OMI project.
在OMI项目的所有位置轮换密钥或API密钥。

Usage

使用方法

/rotate-key <KEY_NAME> <NEW_VALUE>
Examples:
  • /rotate-key GEMINI_API_KEY AIzaSyNewKeyHere123
  • /rotate-key OPENAI_API_KEY sk-new-key-here
If no new value is provided, ask the user for it.
/rotate-key <KEY_NAME> <NEW_VALUE>
示例:
  • /rotate-key GEMINI_API_KEY AIzaSyNewKeyHere123
  • /rotate-key OPENAI_API_KEY sk-new-key-here
如果未提供新值,请向用户询问。

Rotation Checklist

轮换检查清单

For every key rotation, work through ALL of the following locations. Skip any that don't apply to the specific key.
每次密钥轮换时,需完成以下所有位置的更新。不适用于当前密钥的位置可跳过。

1. Discovery — Find All Occurrences

1. 查找定位——找到所有出现的位置

bash
undefined
bash
undefined

Search the entire repo for the key name (env var references)

在整个仓库中搜索密钥名称(环境变量引用)

grep -r "<KEY_NAME>" --include=".env" --include=".yaml" --include=".yml" --include=".py" --include=".swift" --include=".rs" --include=".mjs" --include="*.dart" .
grep -r "<KEY_NAME>" --include=".env" --include=".yaml" --include=".yml" --include=".py" --include=".swift" --include=".rs" --include=".mjs" --include="*.dart" .

Search for the old value if known (hardcoded instances)

如果知道旧值,搜索硬编码的实例

grep -r "<OLD_VALUE>" .
undefined
grep -r "<OLD_VALUE>" .
undefined

2. macOS Keychain

2. macOS Keychain

Check if the key is stored in the macOS Keychain and update it:
bash
undefined
检查密钥是否存储在macOS Keychain中并更新:
bash
undefined

Check for existing entry (try common service name patterns)

检查现有条目(尝试通用服务名称模式)

security find-generic-password -s "<key-name-lowercase>" -w 2>/dev/null
security find-generic-password -s "<key-name-lowercase>" -w 2>/dev/null

Update: delete old, add new

更新:删除旧条目,添加新条目

security delete-generic-password -s "<key-name-lowercase>" 2>/dev/null security add-generic-password -s "<key-name-lowercase>" -a "<key-name-lowercase>" -w "<NEW_VALUE>"
undefined
security delete-generic-password -s "<key-name-lowercase>" 2>/dev/null security add-generic-password -s "<key-name-lowercase>" -a "<key-name-lowercase>" -w "<NEW_VALUE>"
undefined

3. Local .env Files

3. 本地.env文件

Common locations (check all that contain the key):
LocationPurpose
desktop/.env
Desktop app runtime
desktop/.env.app
Desktop app bundled env
desktop/.env.app.dev
Desktop app dev env
desktop/Backend-Rust/.env
Rust backend local
desktop/build/Omi Dev.app/Contents/Resources/.env
Dev build artifact
desktop/build/Omi Beta.app/Contents/Resources/.env
Beta build artifact
backend/.env
Python backend local
app/.env
Flutter app
app/.dev.env
Flutter app dev
bash
undefined
常见位置(检查所有包含该密钥的位置):
位置用途
desktop/.env
桌面端应用运行时
desktop/.env.app
桌面端应用打包环境
desktop/.env.app.dev
桌面端应用开发环境
desktop/Backend-Rust/.env
Rust后端本地环境
desktop/build/Omi Dev.app/Contents/Resources/.env
开发构建产物
desktop/build/Omi Beta.app/Contents/Resources/.env
Beta构建产物
backend/.env
Python后端本地环境
app/.env
Flutter应用
app/.dev.env
Flutter应用开发环境
bash
undefined

Update each file that contains the key

更新每个包含该密钥的文件

sed -i '' "s/<KEY_NAME>=.*/<KEY_NAME>=<NEW_VALUE>/" <file>
undefined
sed -i '' "s/<KEY_NAME>=.*/<KEY_NAME>=<NEW_VALUE>/" <file>
undefined

4. GCP Secret Manager

4. GCP Secret Manager

The backend and desktop-backend pull secrets via Kubernetes ExternalSecrets from GCP Secret Manager.
bash
undefined
后端和桌面端后端通过Kubernetes ExternalSecrets从GCP Secret Manager拉取密钥。
bash
undefined

Prod (based-hardware)

生产环境(based-hardware)

echo -n "<NEW_VALUE>" | gcloud secrets versions add <KEY_NAME> --data-file=- --project=based-hardware
echo -n "<NEW_VALUE>" | gcloud secrets versions add <KEY_NAME> --data-file=- --project=based-hardware

Dev (based-hardware-dev) — may need dev service account

开发环境(based-hardware-dev)——可能需要开发服务账号权限

echo -n "<NEW_VALUE>" | gcloud secrets versions add <KEY_NAME> --data-file=- --project=based-hardware-dev
--account=local-development-joan@based-hardware-dev.iam.gserviceaccount.com
echo -n "<NEW_VALUE>" | gcloud secrets versions add <KEY_NAME> --data-file=- --project=based-hardware-dev
--account=local-development-joan@based-hardware-dev.iam.gserviceaccount.com

Disable old versions to prevent use of leaked key

禁用旧版本以防止泄露的密钥被使用

gcloud secrets versions list <KEY_NAME> --project=based-hardware --format="table(name,state)" gcloud secrets versions disable <OLD_VERSION> --secret=<KEY_NAME> --project=based-hardware gcloud secrets versions disable <OLD_VERSION> --secret=<KEY_NAME> --project=based-hardware-dev
--account=local-development-joan@based-hardware-dev.iam.gserviceaccount.com
undefined
gcloud secrets versions list <KEY_NAME> --project=based-hardware --format="table(name,state)" gcloud secrets versions disable <OLD_VERSION> --secret=<KEY_NAME> --project=based-hardware gcloud secrets versions disable <OLD_VERSION> --secret=<KEY_NAME> --project=based-hardware-dev
--account=local-development-joan@based-hardware-dev.iam.gserviceaccount.com
undefined

5. Cloud Run Services (Direct Env Vars)

5. Cloud Run服务(直接设置的环境变量)

Some services run on Cloud Run with env vars set directly (not via K8s secrets). Check and update these:
bash
undefined
部分运行在Cloud Run上的服务直接设置了环境变量(不通过K8s密钥)。检查并更新这些变量:
bash
undefined

Check current value

检查当前值

gcloud run services describe <SERVICE_NAME> --project=based-hardware --region=us-central1 --format=json |
python3 -c "import json,sys; [print(f"{e['name']}: ...{e.get('value','')[-4:]}") for e in json.load(sys.stdin)['spec']['template']['spec']['containers'][0].get('env',[]) if '<KEY_NAME>' in e.get('name','')]"
gcloud run services describe <SERVICE_NAME> --project=based-hardware --region=us-central1 --format=json |
python3 -c "import json,sys; [print(f"{e['name']}: ...{e.get('value','')[-4:]}") for e in json.load(sys.stdin)['spec']['template']['spec']['containers'][0].get('env',[]) if '<KEY_NAME>' in e.get('name','')]"

Update — IMPORTANT: must specify --image with a valid tag, check available tags first

更新——重要提示:必须指定带有效标签的--image,先查看可用标签

gcloud container images list-tags gcr.io/based-hardware/<SERVICE_NAME> --limit=5 --sort-by=~timestamp --format="table(tags,timestamp.datetime)" gcloud run services update <SERVICE_NAME> --region=us-central1 --project=based-hardware
--image=gcr.io/based-hardware/<SERVICE_NAME>:<VALID_TAG>
--update-env-vars "<KEY_NAME>=<NEW_VALUE>"

**Known Cloud Run services with direct env vars:**

| Service | Region | Key(s) |
|---------|--------|--------|
| `desktop-backend` | us-central1 | `GEMINI_API_KEY`, `AGENT_GEMINI_API_KEY` |
gcloud container images list-tags gcr.io/based-hardware/<SERVICE_NAME> --limit=5 --sort-by=~timestamp --format="table(tags,timestamp.datetime)" gcloud run services update <SERVICE_NAME> --region=us-central1 --project=based-hardware
--image=gcr.io/based-hardware/<SERVICE_NAME>:<VALID_TAG>
--update-env-vars "<KEY_NAME>=<NEW_VALUE>"

**已知直接设置环境变量的Cloud Run服务:**

| 服务 | 区域 | 密钥 |
|---------|--------|--------|
| `desktop-backend` | us-central1 | `GEMINI_API_KEY`, `AGENT_GEMINI_API_KEY` |

6. Restart Kubernetes Deployments

6. 重启Kubernetes部署

After updating GCP secrets, restart deployments so pods pick up the new values:
bash
undefined
更新GCP密钥后,重启部署让Pod获取新值:
bash
undefined

Check which deployments use the key

检查哪些部署使用了该密钥

kubectl get deployments -n prod-omi-backend
kubectl get deployments -n prod-omi-backend

Restart relevant deployments (common ones that use env secrets)

重启相关部署(使用环境密钥的常用部署)

kubectl rollout restart deployment/prod-omi-backend-listen -n prod-omi-backend kubectl rollout restart deployment/desktop-backend -n prod-omi-backend kubectl rollout restart deployment/prod-omi-pusher -n prod-omi-backend
kubectl rollout restart deployment/prod-omi-backend-listen -n prod-omi-backend kubectl rollout restart deployment/desktop-backend -n prod-omi-backend kubectl rollout restart deployment/prod-omi-pusher -n prod-omi-backend

Add others as needed based on which services use the key

根据使用该密钥的服务按需添加其他部署

undefined
undefined

7. Codemagic CI Environment Variables

7. Codemagic CI环境变量

Codemagic stores env vars used during mobile and desktop builds. Update via the dashboard:
App ID:
66c95e6ec76853c447b8bcbb
API approach (list vars):
bash
export CODEMAGIC_API_TOKEN="$(grep CODEMAGIC_API_TOKEN ~/.zshrc | cut -d'"' -f2)"
curl -s -H "x-auth-token: $CODEMAGIC_API_TOKEN" \
  "https://api.codemagic.io/apps/66c95e6ec76853c447b8bcbb" | \
  python3 -c "
import json,sys
data = json.load(sys.stdin)['application']
for v in data.get('appEnvironmentVariables',{}).get('variables',[]):
    if v.get('key') == '<KEY_NAME>':
        print(f'Found: group={v[\"group\"]}, id={v[\"id\"]}, secure={v.get(\"secure\",False)}')
"
Dashboard approach (if API update isn't supported):
  1. Navigate to
    https://codemagic.io/app/66c95e6ec76853c447b8bcbb/settings
  2. Click "Environment variables" tab
  3. Find the variable, click the delete (trash) button, confirm deletion
  4. Add new variable: enter name, value, select the correct group (usually
    app_env
    ), check "Secret", click "Add"
Codemagic存储了移动端和桌面端构建过程中使用的环境变量。通过控制台更新:
应用ID:
66c95e6ec76853c447b8bcbb
API方式(列出变量):
bash
export CODEMAGIC_API_TOKEN="$(grep CODEMAGIC_API_TOKEN ~/.zshrc | cut -d'"' -f2)"
curl -s -H "x-auth-token: $CODEMAGIC_API_TOKEN" \
  "https://api.codemagic.io/apps/66c95e6ec76853c447b8bcbb" | \
  python3 -c "
import json,sys
data = json.load(sys.stdin)['application']
for v in data.get('appEnvironmentVariables',{}).get('variables',[]):
    if v.get('key') == '<KEY_NAME>':
        print(f'Found: group={v[\"group\"]}, id={v[\"id\"]}, secure={v.get(\"secure\",False)}')
"
控制台方式(如果不支持API更新):
  1. 访问
    https://codemagic.io/app/66c95e6ec76853c447b8bcbb/settings
  2. 点击"Environment variables"标签页
  3. 找到对应变量,点击删除(垃圾桶)按钮,确认删除
  4. 添加新变量:输入名称、值,选择正确的分组(通常是
    app_env
    ),勾选"Secret",点击"Add"

8. Codemagic
OMI_DESKTOP_APP_ENV
(Bundled Desktop .env)

8. Codemagic
OMI_DESKTOP_APP_ENV
(打包的桌面端.env)

The
OMI_DESKTOP_APP_ENV
Codemagic secret (group:
desktop_secrets
) is a base64-encoded copy of
desktop/.env.app
that gets decoded into the
.app
bundle at build time. If any key inside
.env.app
changes, this secret must also be updated.
bash
undefined
Codemagic密钥
OMI_DESKTOP_APP_ENV
(分组:
desktop_secrets
)是**
desktop/.env.app
的base64编码副本**,构建时会解码到
.app
包中。如果
.env.app
内的任意密钥发生变更,该密钥也必须更新。
bash
undefined

1. Verify desktop/.env.app has the new key value

1. 确认desktop/.env.app已经更新为新的密钥值

grep "<KEY_NAME>" desktop/.env.app
grep "<KEY_NAME>" desktop/.env.app

2. Re-encode

2. 重新编码

base64 -i desktop/.env.app | tr -d '\n' > /tmp/omi_desktop_app_env_b64.txt
base64 -i desktop/.env.app | tr -d '\n' > /tmp/omi_desktop_app_env_b64.txt

3. Update in Codemagic dashboard:

3. 在Codemagic控制台更新:

- Environment variables tab

- 环境变量标签页

- Delete old OMI_DESKTOP_APP_ENV (desktop_secrets group)

- 删除旧的OMI_DESKTOP_APP_ENV(desktop_secrets分组)

- Add new: name=OMI_DESKTOP_APP_ENV, value=<contents of /tmp/omi_desktop_app_env_b64.txt>,

- 添加新变量:名称=OMI_DESKTOP_APP_ENV,值=</tmp/omi_desktop_app_env_b64.txt的内容>

group=desktop_secrets, Secret=checked

分组=desktop_secrets,勾选Secret


**Keys bundled in `desktop/.env.app`:** `OMI_API_URL`, `DEEPGRAM_API_KEY`, `GEMINI_API_KEY`, `MIXPANEL_PROJECT_TOKEN`, `ANTHROPIC_API_KEY`

**`desktop/.env.app`中打包的密钥:** `OMI_API_URL`, `DEEPGRAM_API_KEY`, `GEMINI_API_KEY`, `MIXPANEL_PROJECT_TOKEN`, `ANTHROPIC_API_KEY`

9. Verification

9. 验证

After rotation, verify:
bash
undefined
轮换完成后,验证更新结果:
bash
undefined

Keychain

Keychain验证

security find-generic-password -s "<key-name-lowercase>" -w
security find-generic-password -s "<key-name-lowercase>" -w

All .env files

所有.env文件验证

grep "<KEY_NAME>" desktop/.env desktop/.env.app desktop/.env.app.dev desktop/Backend-Rust/.env backend/.env 2>/dev/null
grep "<KEY_NAME>" desktop/.env desktop/.env.app desktop/.env.app.dev desktop/Backend-Rust/.env backend/.env 2>/dev/null

GCP Secret Manager

GCP Secret Manager验证

gcloud secrets versions list <KEY_NAME> --project=based-hardware --format="table(name,state)"
gcloud secrets versions list <KEY_NAME> --project=based-hardware --format="table(name,state)"

Kubernetes rollout status

Kubernetes滚动发布状态验证

kubectl rollout status deployment/prod-omi-backend-listen -n prod-omi-backend --timeout=120s
undefined
kubectl rollout status deployment/prod-omi-backend-listen -n prod-omi-backend --timeout=120s
undefined

9. Remind User

9. 提醒用户

After completing rotation, remind the user to:
  • Revoke the old key in the provider's console (Google Cloud API Credentials, OpenAI dashboard, etc.)
  • Check for unauthorized usage in the provider's usage/billing dashboard during the leak window
完成轮换后,提醒用户:
  • 在服务商控制台吊销旧密钥(Google Cloud API凭证、OpenAI控制台等)
  • 在泄露窗口期内检查服务商使用/账单控制台,确认是否存在未授权使用

Key-Specific Notes

特定密钥说明

GEMINI_API_KEY

GEMINI_API_KEY

  • Used by: backend (Python), desktop app (Swift via env), desktop Rust backend, Codemagic (mobile builds + desktop deploy)
  • Codemagic groups:
    app_env
    (direct var) AND
    desktop_secrets
    (inside
    OMI_DESKTOP_APP_ENV
    base64 bundle)
  • Keychain service:
    gemini-api-key
  • GCP projects:
    based-hardware
    (prod),
    based-hardware-dev
    (dev)
  • Cloud Run:
    desktop-backend
    (us-central1, direct env var — NOT via K8s secrets)
  • K8s deployments:
    prod-omi-backend-listen
    (via ExternalSecrets)
  • Helm values reference it via
    secretKeyRef
    (no hardcoded values in helm charts)
  • IMPORTANT: Also update
    OMI_DESKTOP_APP_ENV
    in Codemagic (step 8) — it contains this key inside the base64-encoded
    .env.app
  • WARNING: Never hardcode this key in any tracked file. It has been leaked twice via committed helm values.
  • 使用方:后端(Python)、桌面端应用(Swift通过环境变量调用)、桌面端Rust后端、Codemagic(移动端构建+桌面端部署)
  • Codemagic分组:
    app_env
    (直接变量)
    desktop_secrets
    (在
    OMI_DESKTOP_APP_ENV
    base64包内部)
  • Keychain服务名:
    gemini-api-key
  • GCP项目:
    based-hardware
    (生产)、
    based-hardware-dev
    (开发)
  • Cloud Run:
    desktop-backend
    (us-central1,直接环境变量——不通过K8s密钥)
  • K8s部署:
    prod-omi-backend-listen
    (通过ExternalSecrets)
  • Helm values通过
    secretKeyRef
    引用(Helm chart中无硬编码值)
  • 重要提示: 还需要更新Codemagic中的
    OMI_DESKTOP_APP_ENV
    (第8步)——base64编码的
    .env.app
    中包含该密钥
  • 警告: 永远不要在任何被跟踪的文件中硬编码该密钥,之前已经因为提交Helm values泄露过两次。

OPENAI_API_KEY

OPENAI_API_KEY

  • Used by: backend (Python), Codemagic (mobile builds)
  • Codemagic group:
    app_env
  • 使用方:后端(Python)、Codemagic(移动端构建)
  • Codemagic分组:
    app_env

GOOGLE_CLIENT_SECRET

GOOGLE_CLIENT_SECRET

  • Used by: backend OAuth, Codemagic
  • Codemagic group:
    app_env
  • 使用方:后端OAuth、Codemagic
  • Codemagic分组:
    app_env