# Secrets CLI — AGENTS.md 跨设备密钥与配置管理 CLI 工具,将 refining / ricnsmart 两个项目的服务器信息、服务凭据存储到 PostgreSQL 18,供 AI 工具读取上下文。 ## 项目结构 ``` secrets/ src/ main.rs # CLI 入口,clap 命令定义,auto-migrate db.rs # PgPool 创建 + 建表/索引(幂等) models.rs # Secret 结构体(sqlx::FromRow + serde) commands/ add.rs # add 命令:upsert,支持 --meta key=value / --secret key=@file search.rs # search 命令:多条件动态查询 delete.rs # delete 命令 scripts/ seed-data.sh # 从 refining/ricnsmart config.toml 导入全量数据 .gitea/workflows/ secrets.yml # CI:fmt + clippy + musl 构建 + Release 上传 + 飞书通知 .vscode/tasks.json # 本地测试任务(build / search / add+delete roundtrip 等) .env # DATABASE_URL(gitignore,不提交) ``` ## 数据库 - **Host**: `47.117.131.22:5432`(阿里云上海 ECS,PostgreSQL 18 with io_uring) - **Database**: `secrets` - **连接串**: `postgres://postgres:@47.117.131.22:5432/secrets` - **表**: 单张 `secrets` 表,首次连接自动建表(auto-migrate) ### 表结构 ```sql secrets ( id UUID PRIMARY KEY DEFAULT uuidv7(), -- PG18 时间有序 UUID namespace VARCHAR(64) NOT NULL, -- 一级隔离: "refining" | "ricnsmart" kind VARCHAR(64) NOT NULL, -- 类型: "server" | "service"(可扩展) name VARCHAR(256) NOT NULL, -- 人类可读标识 tags TEXT[] NOT NULL DEFAULT '{}', -- 灵活标签: ["aliyun","hongkong"] metadata JSONB NOT NULL DEFAULT '{}', -- 明文描述: ip, desc, domains, location... encrypted JSONB NOT NULL DEFAULT '{}', -- 敏感数据: ssh_key, password, token... created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(namespace, kind, name) ) ``` ### 字段职责划分 | 字段 | 存什么 | 示例 | |------|--------|------| | `namespace` | 项目/团队隔离 | `refining`, `ricnsmart` | | `kind` | 记录类型 | `server`, `service` | | `name` | 唯一标识名 | `i-uf63f2uookgs5uxmrdyc`, `gitea` | | `tags` | 多维分类标签 | `["aliyun","hongkong","ricn"]` | | `metadata` | 明文非敏感信息 | `{"ip":"47.243.154.187","desc":"Grafana","domains":["..."]}` | | `encrypted` | 敏感凭据(MVP 阶段明文存储,后续对 value 加密) | `{"ssh_key":"-----BEGIN...","password":"..."}` | ## CLI 命令 ```bash # 查看版本 secrets -V / --version # 查看帮助 secrets -h / --help secrets help # 子命令详细帮助,如 secrets help add # 添加或更新记录(upsert) secrets add -n --kind --name \ [--tag ]... # 可重复 [-m key=value]... # --meta 明文字段,-m 是短标志 [-s key=value]... # --secret 敏感字段,value 以 @ 开头表示从文件读取 # 搜索(默认隐藏 encrypted 内容) secrets search [-n ] [--kind ] [--tag ] [-q ] [--show-secrets] # -q 匹配范围:name、namespace、kind、metadata 全文内容、tags # 增量更新已有记录(合并语义,记录不存在则报错) secrets update -n --kind --name \ [--add-tag ]... # 添加标签(不影响已有标签) [--remove-tag ]... # 移除标签 [-m key=value]... # 新增或覆盖 metadata 字段(不影响其他字段) [--remove-meta ]... # 删除 metadata 字段 [-s key=value]... # 新增或覆盖 encrypted 字段(不影响其他字段) [--remove-secret ]... # 删除 encrypted 字段 # 删除 secrets delete -n --kind --name ``` ### 示例 ```bash # 添加服务器 secrets add -n refining --kind server --name i-uf63f2uookgs5uxmrdyc \ --tag aliyun --tag shanghai \ -m ip=47.117.131.22 -m desc="Aliyun Shanghai ECS" \ -s username=root -s ssh_key=@./keys/voson_shanghai_e.pem # 添加服务凭据 secrets add -n refining --kind service --name gitea \ --tag gitea \ -m url=https://gitea.refining.dev \ -s token= # 搜索含 mqtt 的所有记录 secrets search -q mqtt # 查看 refining 的全部服务配置(显示 secrets) secrets search -n refining --kind service --show-secrets # 按 tag 筛选 secrets search --tag hongkong # 只更新一个 IP(不影响其他 metadata/secrets/tags) secrets update -n refining --kind server --name i-uf63f2uookgs5uxmrdyc \ -m ip=10.0.0.1 # 给一条记录新增 tag 并轮换密码 secrets update -n refining --kind service --name gitea \ --add-tag production \ -s token= # 移除一个废弃的 metadata 字段 secrets update -n refining --kind service --name mqtt \ --remove-meta old_port ``` ## 代码规范 - 错误处理:统一使用 `anyhow::Result`,不用 `unwrap()` - 异步:全程 `tokio`,数据库操作 `sqlx` async - SQL:使用 `sqlx::query` / `sqlx::query_as` 绑定参数,禁止字符串拼接(搜索的动态 WHERE 子句除外,需使用参数绑定 `$1/$2`) - 新增 `kind` 类型时:只需在 `add` 调用时传入,无需改代码 - 字段命名:CLI 短标志 `-n`=namespace,`-m`=meta,`-s`=secret,`-q`=query ## 提交前检查(必须全部通过) 每次提交代码前,请在本地依次执行以下检查,**全部通过后再 push**: ### 1. 版本号(按需) 若本次改动需要发版,请先确认 `Cargo.toml` 中的 `version` 已提升,避免 CI 打出的 Tag 与已有版本重复。可通过 git tag 判断: ```bash # 查看当前 Cargo.toml 版本 grep '^version' Cargo.toml # 查看是否已存在该版本对应的 tag(CI 使用格式 secrets-) git tag -l 'secrets-*' ``` 若当前版本已被 tag(例如已有 `secrets-0.1.0` 且 `Cargo.toml` 仍为 `0.1.0`),则应在 `Cargo.toml` 中 bump 版本号后再提交,以便 CI 自动打新 Tag 并发布 Release。 ### 2. 格式、Lint、测试 ```bash cargo fmt -- --check # 格式检查(不通过则运行 cargo fmt 修复) cargo clippy -- -D warnings # Lint 检查(消除所有 warning) cargo test # 单元/集成测试 ``` 或一次性执行: ```bash cargo fmt -- --check && cargo clippy -- -D warnings && cargo test ``` ## CI/CD - Gitea Actions(runner: debian) - 触发:`src/**`、`Cargo.toml`、`Cargo.lock` 变更推送到 main - 构建目标:`x86_64-unknown-linux-musl`(静态链接,无 glibc 依赖) - 新版本自动打 Tag(格式 `secrets-`)并上传二进制到 Gitea Release - 通知:飞书 Webhook(`vars.WEBHOOK_URL`) - 所需 secrets/vars:`RELEASE_TOKEN`(Release 上传,Gitea PAT)、`vars.WEBHOOK_URL`(通知,可选) ## 环境变量 | 变量 | 说明 | |------|------| | `DATABASE_URL` | PostgreSQL 连接串,优先级高于 `--db-url` 参数 |