one secrets
one secrets 通过 Infisical 管理 monorepo 多子项目的环境变量。从 v0.3.0 起替换了原先的 SOPS+age 实现。详细流程 + 安全契约见 Secrets 指南;本页是命令面 + 字段速查。
one secrets init --project-id <id> [--site-url <url>] [--envs dev,staging,prod] [--default-env dev] [--root-path /] [--skip-verify]one secrets set <KEY> [VALUE] [--env <e>] [--path <p>] [--yes]one secrets get <KEY> [--env <e>] [--path <p>]one secrets list [--env <e>] [--path <p>] [--recursive]one secrets pull [--env <e>] [--path <p>] [--force] [--dry-run]通用 flag:-d / --dir(工作区目录,默认当前目录)、--json(强制 JSON 输出)。
把 Infisical 配置写入 one.manifest.json#env。默认在线验证 Universal Auth 凭据 + projectId 可达;带 --skip-verify 跳过。
环境变量:
INFISICAL_UNIVERSAL_AUTH_CLIENT_ID— 必填(除非--skip-verify)INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET— 必填(同上)
输出 schema:one-cli/secrets-init/v1
{ "schema": "one-cli/secrets-init/v1", "project_id": "<id>", "site_url": "https://app.infisical.com", "environments": ["dev", "staging", "prod"], "default_env": "dev", "root_path": "/", "auth_status": "verified", "written_to": "/abs/path/to/one.manifest.json"}写入单个 key。智能选择 create / update:不存在就 create,已存在且不同需要 --yes,相同则返回 unchanged。
--path 默认从 cwd 推导:在子项目目录里执行 → /<relativeDir>;在 workspace 根 → rootPath(默认 /)。
输出 schema:one-cli/secrets-set/v1
{ "schema": "one-cli/secrets-set/v1", "env": "dev", "path": "/services/user-api", "key": "DATABASE_URL", "action": "created" // created | updated | unchanged}读取单个 key 的值。JSON 模式 stdout 包含值(agent | jq -r .value | xargs ... 友好)。
输出 schema:one-cli/secrets-get/v1
{ "schema": "one-cli/secrets-get/v1", "env": "dev", "path": "/services/user-api", "key": "DATABASE_URL", "value": "postgres://..."}列出某个 path 下的所有 key 名(不显示值)。--recursive 可包括子 folder。
输出 schema:one-cli/secrets-list/v1
{ "schema": "one-cli/secrets-list/v1", "env": "dev", "path": "/services/user-api", "keys": ["DATABASE_URL", "JWT_SECRET"], "total": 2}核心命令:从 Infisical 拉取所有子项目的密钥,按各自 .env.example 过滤后写入 .env。
默认行为(不传 --path):
- 遍历每个子项目(
apps/*、services/*、packages/*) - 计算其 Infisical path(从 manifest 推导或该 subproject
one.manifest.json条目里的env.path覆盖) - 沿继承链拉 key(root → ancestors → self,后者覆盖前者)
- 用该子项目的
.env.example过滤 - 写入
.env(0600权限)
带 --path 时只处理映射到该 Infisical path 的子项目(或 workspace 根)。
带 --dry-run 时只汇报”会写哪些 key”不实际落盘。
输出 schema:one-cli/secrets-pull/v1
{ "schema": "one-cli/secrets-pull/v1", "env": "dev", "dry_run": false, "written_count": 2, "skipped_count": 1, "per_subproject": [ { "name": "user-api", "relative_dir": "services/user-api", "infisical_path": "/services/user-api", "env_file_path": "/abs/.../services/user-api/.env", "status": "written", "keys_written": ["DATABASE_URL", "JWT_SECRET"] }, { "name": "docs-site", "relative_dir": "apps/docs-site", "infisical_path": "/apps/docs-site", "env_file_path": "/abs/.../apps/docs-site/.env", "status": "skipped", "reason": "no .env.example declared" } ]}双层安全契约
Section titled “双层安全契约”每次 pull 都经过两层防护:
- Infisical folder 隔离:dashboard 子项目(path=
/apps/dashboard)拿不到/services/user-api下的 key——根本不在它的继承链上 .env.example过滤:即使共享 folder 含多余 key,子项目.env只接收.env.example里声明过的
每个想消费 secrets 的子项目必须列出自己的 .env.example,否则 pull 跳过它(reason 字段写明)。
Workspace 级(必须)
Section titled “Workspace 级(必须)”one.manifest.json#env:
{ "version": 2, "env": { "provider": "infisical", "projectId": "<id>", "siteUrl": "https://app.infisical.com", "environments": ["dev", "staging", "prod"], "defaultEnv": "dev", "rootPath": "/" }}子项目级(可选)
Section titled “子项目级(可选)”one.manifest.json#subprojects[].env:
{ "subprojects": [ { "name": "charge", "relativeDir": "services/charge", "templateId": "api-nest", "toolchain": "node", "env": { "path": "/teams/payments/charge", "inherits": true } } ]}path:覆盖默认推导(/<relativeDir>)inherits:默认true;设false关闭父 folder 继承"disabled": true:完全跳过此子项目
| 错误码 | 处理 |
|---|---|
INFISICAL_NOT_CONFIGURED | one secrets init --project-id <id> |
INFISICAL_AUTH_MISSING | export INFISICAL_UNIVERSAL_AUTH_CLIENT_ID 和 _CLIENT_SECRET |
INFISICAL_AUTH_FAILED | Infisical Web 重新生成 client secret |
INFISICAL_PROJECT_NOT_FOUND | 检查 projectId;确认机器身份在该 project 有访问权限 |
INFISICAL_NETWORK_ERROR | 检查网络 + siteUrl |
SECRETS_PULL_CONFLICT | 已有 .env 跟 Infisical 不一致;--force 覆盖 |
SECRETS_KEY_NOT_FOUND | 该 env+path 下没有此 KEY;核对 --path 和拼写 |
SECRETS_INVALID_KEY | KEY 必须匹配 ^[A-Za-z_][A-Za-z0-9_]*$ |
SECRETS_INVALID_ENV_NAME | env 必须匹配 ^[a-zA-Z0-9][a-zA-Z0-9-_]*$ |
SECRETS_SET_OVERWRITE_REQUIRED | 已存在不同值;加 --yes 确认覆盖 |
完整错误码表见 error-codes。
<workspace>/├── one.manifest.json # env 配置块 + subprojects[].env 覆盖├── package.json # 普通 pnpm 根(不含 one 字段)├── apps/│ └── web/│ ├── .env.example # 提交(声明该子项目消费的 key)│ └── .env # 不要提交(pull 的产物,0600)└── services/ └── api/ ├── .env.example └── .env不再有 .sops.yaml / .secrets/ 目录 / *.enc 文件——这些都是 SOPS 时代的东西,迁移到 Infisical 后删除即可。
我们只支持环境变量传 Universal Auth 凭据。本地推荐 direnv 或 1Password CLI 这类不写盘的方案;CI 直接走 secret 注入。
不要把 INFISICAL_UNIVERSAL_AUTH_CLIENT_* 写到任何 git-tracked 文件——这会绕过整个安全模型。