- Split library (db/crypto/service) and MCP/Web/OAuth binary - Add deploy examples and CI/docs updates Made-with: Cursor
6.8 KiB
6.8 KiB
Secrets MCP — AGENTS.md
本仓库为 MCP SaaS:secrets-core(业务与持久化)+ secrets-mcp(Streamable HTTP MCP、Web、OAuth、API Key)。对外入口见 crates/secrets-mcp。
提交 / 发版硬规则(优先于下文)
- 涉及
crates/**、根目录Cargo.toml/Cargo.lock、secrets-mcp行为变更的提交,默认视为需要发版,除非明确说明「本次不发版」。 - 发版前检查
crates/secrets-mcp/Cargo.toml的version,再查 tag:git tag -l 'secrets-mcp-*'。 - 若当前版本对应 tag 已存在,须先 bump
version,再cargo build同步Cargo.lock后提交。 - 提交前优先运行
./scripts/release-check.sh(版本/tag +fmt+clippy --locked+test --locked)。
项目结构
secrets/
Cargo.toml
crates/
secrets-core/ # db / crypto / models / audit / service
secrets-mcp/ # rmcp tools、axum、OAuth、Dashboard
scripts/
release-check.sh
setup-gitea-actions.sh
.gitea/workflows/secrets.yml
.vscode/tasks.json
数据库
- 建议库名:
secrets-mcp(专用实例,与历史库名区分)。 - 连接:环境变量
SECRETS_DATABASE_URL(本分支无本地配置文件路径)。 - 表:
entries(含user_id)、secrets、entries_history、secrets_history、audit_log、users、oauth_accounts、api_keys,首次连接 auto-migrate。
表结构(摘录)
entries (
id UUID PRIMARY KEY DEFAULT uuidv7(),
user_id UUID, -- 多租户:NULL=遗留行;非空=归属用户
namespace VARCHAR(64) NOT NULL,
kind VARCHAR(64) NOT NULL,
name VARCHAR(256) NOT NULL,
tags TEXT[] NOT NULL DEFAULT '{}',
metadata JSONB NOT NULL DEFAULT '{}',
version BIGINT NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
secrets (
id UUID PRIMARY KEY DEFAULT uuidv7(),
entry_id UUID NOT NULL REFERENCES entries(id) ON DELETE CASCADE,
field_name VARCHAR(256) NOT NULL,
encrypted BYTEA NOT NULL DEFAULT '\x',
version BIGINT NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(entry_id, field_name)
)
users / oauth_accounts / api_keys
users (
id UUID PRIMARY KEY DEFAULT uuidv7(),
email VARCHAR(256),
name VARCHAR(256) NOT NULL DEFAULT '',
avatar_url TEXT,
key_salt BYTEA, -- PBKDF2 salt(32B),首次设置密码短语时写入
key_check BYTEA, -- 派生密钥加密已知常量,用于验证密码短语
key_params JSONB, -- 算法参数,如 {"alg":"pbkdf2-sha256","iterations":600000}
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
oauth_accounts (
id UUID PRIMARY KEY DEFAULT uuidv7(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
provider VARCHAR(32) NOT NULL,
provider_id VARCHAR(256) NOT NULL,
...
UNIQUE(provider, provider_id)
)
api_keys (
id UUID PRIMARY KEY DEFAULT uuidv7(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(256) NOT NULL,
key_hash VARCHAR(64) NOT NULL UNIQUE,
key_prefix VARCHAR(12) NOT NULL,
last_used_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
audit_log / history
与迁移脚本一致:audit_log、entries_history、secrets_history 用于审计与时间旅行恢复;字段定义见 crates/secrets-core/src/db.rs 内 migrate SQL。
字段职责
| 字段 | 含义 | 示例 |
|---|---|---|
namespace |
隔离空间 | refining |
kind |
记录类型 | server, service, key |
name |
标识名 | gitea, i-example0… |
tags |
标签 | ["aliyun","prod"] |
metadata |
明文描述 | ip、url、key_ref |
secrets.field_name |
加密字段名(明文) | token, ssh_key |
secrets.encrypted |
密文 | AES-GCM |
PEM 共享(key_ref)
将共享 PEM 存为 kind=key 的 entry;其它记录在 metadata.key_ref 指向该 key 的 name。更新 key 记录后,引用方通过服务层解析合并逻辑即可使用新密钥(实现见 secrets_core::service)。
代码规范
- 错误:业务层
anyhow::Result,避免生产路径unwrap()。 - 异步:
tokio+sqlxasync。 - SQL:
sqlx::query/query_as参数绑定;动态 WHERE 仍须用占位符绑定。 - 日志:运维用
tracing;面向用户的 Web 响应走 axum handler。 - 审计:写操作成功后尽量
audit::log_tx;失败可warn,不掩盖主错误。 - 加密:密钥由用户密码短语通过 PBKDF2-SHA256(600k 次) 在客户端派生,服务端只存
key_salt/key_check/key_params,不持有原始密钥。Web 客户端在浏览器本地完成加解密;MCP 客户端通过X-Encryption-Key请求头传递密钥,服务端临时解密后返回明文。 - MCP:tools 参数与 JSON Schema(
schemars)保持同步,鉴权以请求扩展中的用户上下文为准。
提交前检查
./scripts/release-check.sh
或手动:
cargo fmt -- --check
cargo clippy --locked -- -D warnings
cargo test --locked
发版前确认未重复 tag:
grep '^version' crates/secrets-mcp/Cargo.toml
git tag -l 'secrets-mcp-*'
CI/CD
- 触发:
main/feat/mcp(以仓库 workflow 为准);路径含crates/**、deploy/**、Cargo.toml、Cargo.lock。 - 构建:
x86_64-unknown-linux-musl→secrets-mcp。 - Release:tag
secrets-mcp-<version>,上传 tar.gz +.sha256。 - 部署:可选在仓库 Actions 中配置
vars.DEPLOY_HOST、vars.DEPLOY_USER与secrets.DEPLOY_SSH_KEY(勿写进 workflow);可用scripts/setup-gitea-actions.sh调 Gitea API 写入。Actions secrets 须为原始值(如 PEM 全文、PAT 明文),不要先 base64 再写入,否则工作流内无法识别(例如 SSH 私钥无效)。勿在 CI 中保存GOOGLE_CLIENT_SECRET、DB 密码。 - 通知:
vars.WEBHOOK_URL(可选)。
环境变量(secrets-mcp)
| 变量 | 说明 |
|---|---|
SECRETS_DATABASE_URL |
必填。PostgreSQL URL。 |
BASE_URL |
对外基址;OAuth 回调 ${BASE_URL}/auth/google/callback。 |
SECRETS_MCP_BIND |
监听地址,默认 0.0.0.0:9315。 |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET |
可选;仅运行时配置。 |
RUST_LOG |
如 secrets_mcp=debug。 |
USER |
若写入审计 actor,由运行环境提供。 |
SERVER_MASTER_KEY已不再需要。新架构下密钥由用户密码短语在客户端派生,服务端不持有。