ops0-ai/ops0-cli
GitHub: ops0-ai/ops0-cli
ops0-cli 是一款面向 AI 编程 Agent 的 IaC 安全护栏,在 Agent 写入或执行基础设施代码时实时进行策略检查、安全扫描、成本估算和破坏性命令拦截。
Stars: 64 | Forks: 3
# ops0 CLI
**面向 AI 编程 Agent 的策略、代码检查、漏洞和成本护栏。**
位于 Claude Code、Codex 和 Gemini CLI 的前端。Agent 进行的每一次 IaC 编辑
都会在服务端经历验证、代码检查、策略检查、安全扫描以及
成本估算,失败结果会以失败的工具调用形式反馈给模型。破坏性命令在运行前会被拦截。
[](https://github.com/ops0-ai/ops0-cli/releases/latest)
[](LICENSE)
[](https://goreportcard.com/report/github.com/ops0-ai/ops0-cli)
```
curl -fsSL https://raw.githubusercontent.com/ops0-ai/ops0-cli/main/install.sh | sh
```
[快速开始](#quick-start)
· [每次编辑时运行的操作](#what-runs-on-every-edit)
· [Agent 如何触发它](#how-agents-trigger-it)
· [ops0-scan.md](#the-ops0-scanmd-report)
· [常见问题](#faq)
## 为什么会有这个项目
当人类编写基础设施时,策略关卡会在 PR 阶段触发。CI 会运行
扫描器,有人会收到告警,PR 在审查中会停滞数小时。
当 **Agent** 编写基础设施时,这个闭环就被打破了。Agent 会
乐于生成公开的 S3 存储桶、开放的安全组,或者
规模过大的 EC2 集群。等到 CI 发现问题时,Agent 早就继续下一步了。
`ops0` 位于 Agent 的前端。它会在 Agent 认为编辑完成之前运行
你所在组织的检查,在破坏性命令执行前将其拦截,告知 Agent 其 IaC
每月的成本是多少,并根据项目的预算对变更进行把关。
## 功能特性
| | |
|---|---|
| **每次编辑后验证 IaC** | `PostToolUse` 钩子会对 Agent 写入的每个 `.tf` / `.tofu` / `.hcl` / `.tfvars` 文件运行 `ops0 validate`。服务端流水线会在一次调用中捕获所有问题。 |
| **拦截销毁命令** | `PreToolUse` 钩子会拦截 `terraform destroy`、`tofu destroy`、`oxid destroy` 以及 `-destroy` 变体命令。可通过 `OPS0_ALLOW_DESTROY=1` 覆盖。 |
| **强制执行项目预算** | 如果成本估算超过了 ops0 控制台中设置的项目预算,关卡将判定为失败。Agent 会收到优化的指示。 |
| **写入全新的报告文件** | 每次运行后,仓库根目录下的 `ops0-scan.md` 会被重写,因此 Agent 只需读取一个文件即可了解所有阶段的当前状态。 |
| **支持 MCP** | `ops0 mcp serve` 会向任何兼容 MCP 的 Agent 暴露 `list_policies` 和 `check_compliance` 能力。在运行 `ops0 init` 时会自动注册到 Claude Code。 |
| **支持多项目感知** | 从被编辑的文件向上查找,定位最近的 `.ops0/config.json`。一个包含十个子项目的仓库可以分别映射到各自独立的 ops0 项目。 |
| **审计日志** | 每一次失败的 lint 检查、策略违规、漏洞、被拦截的销毁操作以及预算超支,都会根据你的 API 密钥记录在 `Settings → API Keys → Activity` 中。 |
| **处处生效** | 钩子会同时安装在项目级别和用户级别,因此无论 Claude Code 在哪个目录打开,它们都会触发。 |
## 快速开始
```
# 1. Install
curl -fsSL https://raw.githubusercontent.com/ops0-ai/ops0-cli/main/install.sh | sh
# 2. Auth: 请在 https://brew.ops0.ai/settings?tab=api-keys 获取密钥
ops0 login --api-base https://brew.ops0.ai
# 3. 将 repo(或任何子目录)绑定到 ops0 项目
cd ~/work/my-terraform-repo
ops0 init --project=
# 4. 在 repo 中打开 Claude Code(或 Codex/Gemini)并编写一些 Terraform。
# 每次 .tf 编辑都会触发完整的 pipeline。尝试运行 `terraform destroy`
# 并观察其如何被阻止。
```
验证连接状态:
```
$ ops0 policies list
NAME CATEGORY SEVERITY DESCRIPTION
no-public-s3 security high S3 buckets must not be public
require-encryption-at-rest security medium All storage must use customer-managed keys
tag-required-cost-center tagging low Every resource must carry a cost-center tag
$ ops0 validate .
ops0 validate . (4 files, 8.2s)
✓ Configuration is valid
tflint: 0 error(s), 2 warning(s), 0 notice(s)
[WARNING] terraform_required_providers: Missing version constraint for provider "aws"
scan: 14 passed, 8 failed (0 parsing errors). Severity: 1C / 1H / 6M / 0L
[CRITICAL] no-public-s3: S3 bucket has public read access
[HIGH] require-encryption: S3 bucket is missing default encryption
...
cost: $284.50 / month across 6 resource(s)
$148.92 aws_db_instance.app (aws_db_instance)
$89.71 aws_instance.api (aws_instance)
...
budget: ✓ $284.50/mo within project limit of $500.00/mo.
```
## 每次编辑时运行的操作
一次单独的 `ops0 validate` 调用会按顺序分发到 **五个** 服务端阶段。CLI 会返回合并后的结果;失败将拦截该钩子。
| # | 阶段 | 捕获内容 | 关卡失败条件... |
|---|---|---|---|
| 1 | **语法验证** | 解析错误、未定义的变量、错误的属性类型 | `terraform validate` 返回 invalid |
| 2 | **Lint** (感知 Provider) | 错误的实例类型、已弃用的参数、缺失的版本约束 | 出现 lint 错误(警告/通知仅作报告) |
| 3 | **策略 + 漏洞** | 你所在组织的合规规则、安全发现(公开的存储桶、开放的安全组、IMDSv1、未加密的卷、缺失的标签等) | 任何达到或高于 `--scan-fail-on` 级别(默认为 `high`)的发现 |
| 4 | **成本估算** | 所有已定价资源的月度成本 | 仅供参考,除非触发了第 5 步 |
| 5 | **项目预算** | 来自 ops0 控制台的按项目月度限制 | `enabled` 且 `exceeded` 且开启了 `Block Deployments on Exceed` |
这五个阶段都在一次 HTTPS 调用中完成。在服务端 Provider 缓存预热的情况下,
根据仓库大小,每次编辑的往返时间约为 5-12 秒。
## Agent 如何触发它
`ops0 init` 会在项目级别和用户级别的 `.claude/settings.json` 中写入钩子,
因此无论 Claude Code 在哪个目录打开,关卡都会触发。
```
┌──────────────────────────────┐
│ Claude Code / Codex / │
│ Gemini CLI writes a .tf │
└──────────────┬───────────────┘
│
│ PostToolUse hook (.claude/settings.json)
▼
┌─────────────────────────────────┐
│ ops0 validate "$file" │
│ (walks up to find │
│ .ops0/config.json) │
└─────────────┬───────────────────┘
│ HTTPS (API key)
▼
┌─────────────────────────────────┐
│ ops0 platform │
│ - syntax validate │
│ - lint │
│ - policies + vulnerabilities │
│ - cost │
│ - project budget │
└─────────────┬───────────────────┘
│
▼
┌─────────────────────────────────┐
│ exit 0 → agent moves on │
│ exit ≠ 0 → hook fails → │
│ agent gets stderr, retries │
│ │
│ Either way: ops0-scan.md │
│ is rewritten with the latest │
│ state. │
└─────────────────────────────────┘
```
用户永远不需要问“你运行扫描了吗?”。关卡是自动执行的。
## 破坏性命令拦截
当 Agent 尝试通过 Bash 运行 `terraform destroy` / `tofu destroy` / `oxid destroy`
(或任何 `-destroy` 变体)时,`PreToolUse` 钩子会在命令运行 **之前** 触发:
```
agent calls Bash with `terraform destroy -auto-approve`
│
▼ PreToolUse hook reads the command
▼ matches the destroy pattern
▼ POSTs an audit row to ops0
▼ prints the block message to stderr
▼ exit 2 → Claude Code aborts the Bash call
```
如果需要故意销毁资源(沙盒、开发环境),请在命令前添加覆盖前缀:
```
OPS0_ALLOW_DESTROY=1 terraform destroy
```
该覆盖操作仍会被记录到审计日志中。
## ops0-scan.md 报告
在每次 `ops0 validate` 运行后,CLI 会在绑定的仓库根目录重写一个 Markdown 文件:
```
/ops0-scan.md
```
其中包含:
- 生成时间戳 + CLI 版本
- 汇总表(validate / lint / policies / cost / budget — 各占一行)
- terraform validate 错误(如果有)
- Lint 检查结果表
- 失败的策略 + 漏洞检查结果表,按严重程度排序
- 成本明细,按月度成本排名前 20 的资源
- 预算判定(在限额内 / 超出 $X / 已拦截)
该文件在每次运行时都会被覆盖,因此它始终反映当前的最新事实。
可在多个轮次中读取,无需重新运行 validate。不要手动编辑它
(下一次工具调用会丢弃你的更改)。
使用 `--no-report` 可禁用,或使用 `--report path/to/file.md` 更改其位置。
## 多项目单体仓库 (Monorepo)
一个仓库,十个子项目,每个子项目都有自己的策略和预算?这完全可行:
```
my-monorepo/
├── prod/
│ ├── .ops0/config.json ← projectId: prod
│ └── main.tf
├── staging/
│ ├── .ops0/config.json ← projectId: staging
│ └── main.tf
└── shared/
├── .ops0/config.json ← projectId: shared
└── main.tf
```
当钩子在 `prod/main.tf` 上触发时,CLI 会从该文件所在的目录向上查找 `prod/.ops0/config.json`,并针对 `prod` 项目运行 `ops0 validate`。在 `staging/main.tf` 中的相同编辑会解析为 `staging`。
每个子项目的策略、漏洞检查和预算都是独立应用的。
## 集成
### Claude Code
`ops0 init` 会为你完成配置:
```
ops0 init --project=
```
这一条命令会:
- 写入 `/.ops0/config.json` 以将目录绑定到项目。
- 在 `/.claude/settings.json` 中安装 `PostToolUse`(验证 IaC)、`PreToolUse`(拦截销毁操作)和 `Stop`(轮次结束时的重新检查)钩子。
- 在 `~/.claude/settings.json` 中安装相同的钩子,以便无论你在哪个目录打开 Claude Code 都能触发。用户级钩子受 `.ops0/config.json` 向上查找的制约,因此不会验证无关的仓库。
- 在 `CLAUDE.md` 中追加一个围栏式的治理部分,以便 Agent 在生成 IaC 之前阅读这些规则,并知道要读取 `ops0-scan.md` 以了解当前状态。
- 运行 `claude mcp add ops0 ops0 mcp serve`,以便 Agent 可以原生调用 `list_policies` 和 `check_compliance`。
升级 CLI 后,重新运行 `ops0 init --force` 以刷新钩子脚本。
### Codex / Gemini CLI / 任意 MCP 客户端
```
ops0 mcp serve
```
将其作为 stdio MCP 服务器运行。暴露的工具包括:`list_policies`、
`check_compliance`、`whoami`。通过你客户端的 MCP 配置进行接入。
## 命令
| 命令 | 作用 |
|----------------------------------|-----------------------------------------------------------------------|
| `ops0 login` | 使用 ops0 设置 UI 中的 API 密钥进行认证 |
| `ops0 init` | 将当前目录绑定到项目,安装钩子,注册 MCP |
| `ops0 policies list` | 列出当前目录项目范围内的策略 |
| `ops0 policies check [path]` | 轻量级扫描(仅含策略 + 漏洞,无 init/lint/cost) |
| `ops0 validate [path]` | 完整流水线:语法 + lint + 策略 + 漏洞 + 成本 |
| `ops0 mcp serve` | 通过 stdio 运行 MCP 服务器 |
| `ops0 telemetry blocked-command` | 记录被 PreToolUse 钩子拦截的销毁尝试 |
| `ops0 version` | 打印版本信息 |
### `ops0 validate` 标志
| 标志 | 默认值 | 用途 |
|---|---|---|
| `--format pretty\|json` | `pretty` | 输出格式。JSON 用于通过管道传递给其他工具。 |
| `--iac-type terraform\|opentofu\|oxid` | `terraform` | 要分发到的 IaC 类型。 |
| `--cloud aws\|gcp\|azure\|oracle` | (自动) | Lint 插件的提示。 |
| `--scan-fail-on critical\|high\|medium\|low` | `high` | 策略/漏洞关卡的严重性阈值。 |
| `--fail-on-warning` | `false` | 出现 lint 警告时同样以非零状态退出。 |
| `--report ` | `/ops0-scan.md` | 报告的写入位置。 |
| `--no-report` | `false` | 跳过写入报告文件。 |
## 配置文件
| 路径 | 作用域 | 用途 |
|---|---|---|
| `~/.ops0/config.yaml` | 用户范围 | 凭据和默认值 (`chmod 0600`) |
| `/.ops0/config.json` | 按目录 | 项目绑定。将其提交到 git。 |
| `/.claude/settings.json` | 按目录 | 项目级 Claude Code 钩子 |
| `~/.claude/settings.json` | 用户范围 | 用户级 Claude Code 钩子(在任何工作区中触发) |
| `/ops0-scan.md` | 按目录 | 自动生成的扫描报告。只读;不要编辑它。 |
## 常见问题
**它会将我的 Terraform 发送到云端吗?**
是的,通过 HTTPS 发送,并限定在你的 API 密钥范围内。文件在扫描期间
存在于 ops0 平台的临时目录 (tempdir) 中,且不会被持久化存储。
**如果我的 CI 运行了 `terraform apply` 会怎样?**
我们故意没有拦截 `apply`。拦截每一个 `apply` 是不可行的。对 `apply` 的正确防御应该基于 plan 的感知,这已在我们的路线图中。目前的重点是防止 Agent 在一开始就写出糟糕的 IaC,并防止它拆除已有的内容。
**我该如何让 `terraform destroy` 通过以进行计划中的拆除操作?**
在命令前添加前缀 `OPS0_ALLOW_DESTROY=1`。此拦截操作仍然会被
记录到审计日志中。
**如果我的组织没有设置项目预算怎么办?**
预算强制执行是选择加入的。如果没有设置,成本仍然会被计算并
在 validate 输出和 `ops0-scan.md` 中报告,但绝不会造成拦截。
**我删除了 `.ops0/config.json`。会发生什么?**
用户级钩子向上查找但什么也没找到,因此它会以状态码 0 退出。该
目录解除绑定。不会发生任何异常。
**我在一个仓库中有十个项目。钩子会知道是哪一个吗?**
是的。CLI 会从被编辑的文件向上查找最近的
`.ops0/config.json`,并使用该项目的策略 + 预算。
**它支持 Terraform 以外的工具吗?**
目前支持:Terraform、OpenTofu 和 Oxid,通过 `.tf`、`.tofu`、`.hcl、
`.tf.json`、`.tfvars` 和 `.tfvars.json` 扩展名识别。Kubernetes
清单是下一步计划。
**Agent 能否跳过这个关卡?**
不能。钩子在每次编辑/写入/多重编辑 (Edit/Write/MultiEdit) IaC 文件时都会机械地触发。Agent 无法选择退出——在 Claude Code 看来,钩子的非零退出会导致工具调用失败。这正是关键所在。
## 从源码构建
```
git clone https://github.com/ops0-ai/ops0-cli && cd ops0-cli
go build -o ops0 ./cmd/ops0
sudo install -m 0755 ops0 /usr/local/bin/ops0
```
需要 Go 1.22 或更高版本。
## 贡献
欢迎提交 Issue 和 PR。注意事项:
- 推送前运行 `go vet ./...` 和 `go test ./...`。
- 钩子脚本必须兼容 macOS bash 3.2 和 Linux bash 4+。
- 新的遥测字段需要在 `config-master` 仓库中进行配套迁移。
- 遥测调用尽最大努力执行。绝不能因为报告失败而阻塞 CLI。
## Star 历史
如果这个项目对你有用,请给它点个 Star。这是帮助其他团队发现它的最简单方式。
## 许可证
Apache 2.0。详见 [LICENSE](./LICENSE)。标签:AI安全, Chat Copilot, CISA项目, Claude Code, Codex, DevSecOps, EC2, ECS, EVTX分析, Gemini CLI, Golang, HCL, IaC, Lerna, OpenTofu, Terraform, 上游代理, 云防护, 人工智能代理, 代码验证, 安全编程, 成本估算, 日志审计, 策略执行, 防御破坏命令