Files
secrets/AGENTS.md

12 KiB
Raw Blame History

Secrets MCP — AGENTS.md

本仓库为 MCP SaaSsecrets-core(业务与持久化)+ secrets-mcpStreamable HTTP MCP、Web、OAuth、API Key。对外入口见 crates/secrets-mcp

版本控制

本仓库使用 Jujutsu (jj) 作为版本控制系统(纯 jj 模式,无 .git 目录)。

常用 jj 命令对照

操作 jj 命令
查看历史 jj log / jj log 'all()'
查看状态 jj status
新建提交 jj commit
创建新变更 jj new
变基 jj rebase
合并提交 jj squash
撤销操作 jj undo
查看标签 jj tag list
查看分支 jj bookmark list
推送远端 jj git push
拉取远端 jj git fetch

注意事项

  • 本仓库为纯 jj 模式,无 .git 目录;本地不要使用 git 命令
  • CI/CDGitea Actions仍通过 Git 协议拉取代码Runner 侧自动使用 git,无需修改
  • 检查标签是否存在时使用 jj log --no-graph --revisions "tag(${tag})" 而非 git rev-parse

提交 / 推送硬规则(优先于下文)

每次提交和推送前必须执行以下检查,无论是否明确「发版」:

  1. 涉及 crates/**、根目录 Cargo.toml/Cargo.locksecrets-mcp 行为变更的提交,默认视为需要发版,除非明确说明「本次不发版」。
  2. 提交前检查 crates/secrets-mcp/Cargo.tomlversion,再查 tagjj tag list。若当前版本对应 tag 已存在且有代码变更,必须 bump 版本号cargo build 同步 Cargo.lock
  3. 提交前运行 ./scripts/release-check.sh(版本/tag + fmt + clippy --locked + test --locked)。若脚本不存在或不可用,至少运行 cargo fmt -- --check && cargo clippy --locked -- -D warnings && cargo 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)、secretsentries_historysecrets_historyaudit_logusersoauth_accounts,首次连接 auto-migratesecrets-coremigrate)。
  • Web 会话:与上项 同一数据库 URLsecrets-mcp 启动时对 tower-sessions 的 PostgreSQL 存储 auto-migrate(会话表与业务表共存于该实例,无需第二套连接串)。

表结构(摘录)

entries (
  id          UUID PRIMARY KEY DEFAULT uuidv7(),
  user_id     UUID,                                -- 多租户NULL=遗留行;非空=归属用户
  folder      VARCHAR(128)  NOT NULL DEFAULT '',
  type        VARCHAR(64)   NOT NULL DEFAULT '',
  name        VARCHAR(256)  NOT NULL,
  notes       TEXT          NOT NULL DEFAULT '',
  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()
)
-- 唯一UNIQUE(user_id, folder, name) WHERE user_id IS NOT NULL
--       UNIQUE(folder, name) WHERE user_id IS NULL单租户遗留
secrets (
  id          UUID PRIMARY KEY DEFAULT uuidv7(),
  user_id     UUID,
  name        VARCHAR(256) NOT NULL,
  type        VARCHAR(64)  NOT NULL DEFAULT 'text',
  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(user_id, name) WHERE user_id IS NOT NULL
entry_secrets (
  entry_id    UUID NOT NULL REFERENCES entries(id) ON DELETE CASCADE,
  secret_id   UUID NOT NULL REFERENCES secrets(id) ON DELETE CASCADE,
  sort_order  INT  NOT NULL DEFAULT 0,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  PRIMARY KEY(entry_id, secret_id)
)

users / oauth_accounts

users (
  id           UUID PRIMARY KEY DEFAULT uuidv7(),
  email        VARCHAR(256),
  name         VARCHAR(256) NOT NULL DEFAULT '',
  avatar_url   TEXT,
  key_salt     BYTEA,         -- PBKDF2 salt32B首次设置密码短语时写入
  key_check    BYTEA,         -- 派生密钥加密已知常量,用于验证密码短语
  key_params   JSONB,         -- 算法参数,如 {"alg":"pbkdf2-sha256","iterations":600000}
  api_key      TEXT UNIQUE,   -- MCP Bearer token当前实现为明文存储
  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,
  email        VARCHAR(256),
  name         VARCHAR(256),
  avatar_url   TEXT,
  created_at   TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE(provider, provider_id)
)
-- 另有唯一索引 UNIQUE(user_id, provider)(迁移中 idx_oauth_accounts_user_provider同一用户每种 provider 至多一条关联。

audit_log / history

与迁移脚本一致:audit_logentries_historysecrets_history 用于审计与时间旅行恢复;字段定义见 crates/secrets-core/src/db.rsmigrate SQL。audit_log 含可选 user_id(多租户下标识操作者;可空以兼容遗留数据)。audit_log 中普通业务事件使用 folder / type / name 对应 entry 坐标;登录类事件固定使用 folder='auth',此时 type/name 表示认证目标而非 entry 身份。

MCP 消歧AI 调用)

name 定位条目的工具(secrets_update / secrets_history / secrets_rollback / secrets_delete 单条模式):若该用户下仅一条匹配则直接执行;若多条(同 name、不同 folder)则返回错误并提示补全 folder。也可直接传 idUUID跳过消歧。

注意:secrets_get 只接受 UUID id(来自 secrets_find 结果),不支持按 name 定位。

字段职责

字段 含义 示例
folder 隔离空间(参与唯一键) refining
type 软分类(不参与唯一键,用户自定义) server, service, account, person, document
name 标识名 gitea, aliyun
notes 非敏感说明 自由文本
tags 标签 ["aliyun","prod"]
metadata 明文描述 ipurlsubtype
secrets.name 密钥名称(调用方提供) token, ssh_key, password
secrets.type 密钥类型(调用方提供,默认 text text, password, key
secrets.encrypted 密文 AES-GCM

共享密钥N:N 关联)

多个 entry 可共享同一 secret 字段,通过 entry_secrets 中间表关联。 添加条目时通过 link_secret_names 参数指定要关联的已有 secret name(user_id, name) 精确匹配)。 删除 entry 时仅解除关联secret 本身若仍被引用则保留;不再被任何 entry 引用时自动清理。

代码规范

  • 错误:业务层 anyhow::Result,避免生产路径 unwrap()
  • 异步:tokio + sqlx async。
  • SQLsqlx::query / query_as 参数绑定;动态 WHERE 仍须用占位符绑定。
  • 日志:运维用 tracing;面向用户的 Web 响应走 axum handler。tracing 字段风格:变量名即字段名时用简写(%var?varvar),否则用显式形式(field = %expr)。
  • 审计:写操作成功后尽量 audit::log_tx;失败可 warn,不掩盖主错误。
  • 加密:密钥由用户密码短语通过 PBKDF2-SHA256600k 次) 在客户端派生,服务端只存 key_salt/key_check/key_params不持有原始密钥。Web 客户端在浏览器本地完成加解密MCP 客户端通过 X-Encryption-Key 请求头传递密钥,服务端临时解密后返回明文。
  • MCPtools 参数与 JSON Schemaschemars)保持同步,鉴权以请求扩展中的用户上下文为准。

生产 CORS

生产环境 CORS 使用显式请求头白名单(build_cors_layer),而非 allow_headers(Any) 因为 tower-http 禁止 allow_credentials(true)allow_headers(Any) 同时使用。

维护约束:若 MCP 协议或客户端新增自定义请求头,必须同步更新 production_allowed_headers()。 当前允许的请求头:AuthorizationContent-TypeX-Encryption-Keymcp-session-idx-mcp-session

提交前检查

./scripts/release-check.sh

或手动:

cargo fmt -- --check
cargo clippy --locked -- -D warnings
cargo test --locked

发版前确认未重复 tag

grep '^version' crates/secrets-mcp/Cargo.toml
jj tag list

CI/CD

  • 触发:任意分支 push,且路径含 crates/**deploy/**、根目录 Cargo.tomlCargo.lock.gitea/workflows/**(见 .gitea/workflows/secrets.yml)。
  • 版本与 tag:从 crates/secrets-mcp/Cargo.toml 读版本;构建成功后打 secrets-mcp-<version>:若远端已存在同名 tagCI 会先删后于当前提交重建并推送(覆盖式发版)。
  • 质量与构建fmt / clippy --locked / test --lockedx86_64-unknown-linux-musl 发布构建 secrets-mcp
  • Release可选secrets.RELEASE_TOKENGitea PAT用于通过 API 创建或更新该 tag 的 Release非 draft、上传 tar.gz + .sha256;未配置则跳过 API Release仅 tag + 构建。
  • 部署(可选):仅 mainfeat/mcpmcp 分支在构建成功时跑 deploy-mcp;需 vars.DEPLOY_HOSTvars.DEPLOY_USERsecrets.DEPLOY_SSH_KEY。勿把 OAuth/DB 等写进 workflowdeploy/.env.example 在目标机配置。
  • Secrets 写法Actions secrets 须为原始值PEM、PAT 明文), base64否则 SSH/Release 会失败。在 CI 中保存 GOOGLE_CLIENT_SECRET、DB 密码。
  • 通知vars.WEBHOOK_URL(可选,飞书)。

环境变量secrets-mcp

变量 说明
SECRETS_DATABASE_URL 必填。PostgreSQL URL。
SECRETS_DATABASE_SSL_MODE 可选但强烈建议生产必填。推荐 verify-full(至少 verify-ca)。
SECRETS_DATABASE_SSL_ROOT_CERT 可选。私有 CA 或自签链路时指定 CA 根证书路径。
SECRETS_DATABASE_POOL_SIZE 可选。连接池最大连接数,默认 10
SECRETS_DATABASE_ACQUIRE_TIMEOUT 可选。获取连接超时秒数,默认 5
SECRETS_ENV 可选。设为 prod / production 时会拒绝弱 PostgreSQL TLS 模式。
BASE_URL 对外基址OAuth 回调 ${BASE_URL}/auth/google/callback
SECRETS_MCP_BIND 监听地址,默认 127.0.0.1:9315(容器/远程直接暴露时需改为 0.0.0.0:9315)。
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET 可选;仅运行时配置。
RUST_LOG secrets_mcp=debug
RATE_LIMIT_GLOBAL_PER_SECOND 可选。全局限流速率,默认 100 req/s。
RATE_LIMIT_GLOBAL_BURST 可选。全局限流突发量,默认 200
RATE_LIMIT_IP_PER_SECOND 可选。单 IP 限流速率,默认 20 req/s。
RATE_LIMIT_IP_BURST 可选。单 IP 限流突发量,默认 40
TRUST_PROXY 可选。设为 1/true/yes 时从 X-Forwarded-For / X-Real-IP 提取客户端 IP。

SERVER_MASTER_KEY 已不再需要。新架构下密钥由用户密码短语在客户端派生,服务端不持有。