关于 rekor
Rekor 是面向 AI Agent 的数据层,通过 `rekor` CLI 设计和管理数据模型,并支持预览环境中的模式变更。生产 Agent 可通过 MCP 工具读写记录,外部系统则可通过 REST API 集成。
主要功能
- 01通过 CLI 管理数据模型
- 02在预览环境配置模式
- 03管理记录、关系与文件
- 04配置外部数据源和触发器
- 05定义面向 Agent 的操作与工具集
- 06通过预览和人工操作发布模式
适用场景
- 01为 Agent 应用设计记录类型
- 02在预览环境验证模式变更
- 03让生产 Agent 读写业务记录
- 04将外部系统接入记录数据流
MCP 相关
Rekor 可将一组 Action 配置为 MCP 服务工具集,供 MCP 客户端使用。生产 Agent 使用 MCP 工具读写记录;CLI 用于设置和管理数据模型。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wayai-pro/ rekor-skill --skill rekor安装后在 Claude Code 中输入/rekor确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/executors.mdexternal-sources.mdmcp-factory.mdproviders.mdquerying.md详细介绍
Rekor — AI Agent 的数据层
你可以使用 rekor CLI——Rekor 的构建接口。使用它来设置架构、配置 record_types、在预览环境中测试,并发布到生产环境。生产环境中的 Agent 使用 MCP 工具读写记录;外部系统通过 REST API 集成。CLI 是用于设计和管理数据模型的工具。
本文档自包含地介绍相关概念:Rekor 对象模型定义了每种基本元素,任务 → 功能映射可将任意请求路由到实现该功能的部分,而更多详情说明了何时需要打开随附的 references/ 文件,以查看完整的选项语法。
Agent 指南
- 有 Shell 访问权限时,通过
rekorCLI 操作 Rekor。如果工具集中也有mcp__rekor__*工具,请优先使用 CLI 进行设置和配置;MCP 工具仅用于生产环境的读写操作。 - 仅提供此技能、工具说明或参考文档中的信息。不要编造 URL、路径、命令或标志。
- 配置某项功能前,先阅读匹配的
references/*.md文件。 本文档有意只介绍概念和常见命令形式;查询与搜索调优、外部数据源、MCP Factory Actions/工具集、执行器和提供方适配器的完整选项语法,见随附的参考文件(索引见更多详情)。 - 架构工作(record_types、入站 Webhook、触发器、MCP Factory 工具集)只能在预览基础中进行。更改架构前,务必创建或使用预览基础。
- 标识符是永久的。 基础、record_type、关系类型、Action 和 MCP Factory 工具集的
id就是其身份标识,且不可变——创建时选定,此后不得重命名。显示名称和描述可以编辑,但id永远不能更改(关系类型没有单独的名称——其id也就是标签)。没有重命名功能:若要“重命名”其中任何一项,需创建新的实体并迁移其数据(旧实体保持不变)。因此,请预先选择清晰、稳定的小写 slug 形式的id(例如patients、treated_by)。 - 只能由人来发布。 架构就绪后,给出准确的
rekor bases promote命令,并等待用户运行。 - Token 只会在创建时显示一次。务必提醒用户先复制并妥善保存,再进行其他操作。
- 切勿自动提交。显示
git diff(处理架构文件时),并等待用户批准。
Rekor 对象模型
请先阅读本节——这里完整列出了相关概念。每项设置任务都是这些部分的组合;下方的完整命令参考介绍命令和规则,随附的 references/ 文件则提供详细语法。
Organization ← account boundary; API tokens, vault secrets
└── Base (production | preview) ← top-level data container: one per app, domain, or tenant
├── CONFIG — edit in PREVIEW, ship with PROMOTE (human-run):
│ record types (+ their external sources) · relationship types · file types
│ · triggers · inbound webhooks · Actions · toolsets · seed fixture definitions
└── DATA — read/write on ANY base, production included:
records · relationships · files · attachments · batch writes
(seed apply/reset/clear and seed lease acquire/release run only on previews)
适用于所有命令的一条规则: 生产基础会拒绝配置写入——请在预览基础中(rekor bases create-preview)进行配置,然后让用户运行 rekor bases promote。常规数据写入可在任何环境中进行;种子夹具和种子租约数据操作仅限预览环境。如果写入意外因预览/403 错误而失败,请检查是否在生产环境中进行配置或评估状态操作。
每种基本元素,各用一行说明
两组架构→实例配对是模型的基础:记录类型 → 记录和关系类型 → 关系。文件也遵循此模式(文件类型 → 文件)。
| 基础概念 | 含义 | 命令 / 章节 |
|---|---|---|
| 组织 | 拥有 bases、令牌和密钥的账户边界;rec_ 密钥仅属于一个组织,无法指向其他组织(--org / REKOR_ORG 不起作用) | — |
| Base | 顶层数据容器。production(其配置实体只能通过 promote 更改;名称、标签、时区等元数据仍可编辑)或 preview(<prod>--<slug>,可编辑配置的克隆,可 promote 回其源) | rekor bases |
| 记录类型 | 定义记录类型的 JSON Schema——在运行时创建,无需迁移。包含 Schema 提示(外键 x-fk、搜索调优 x-search、归档模式 x-archival、x-timezone、显示 x-ui),还可以声明外部数据源 | rekor record-types |
| 记录 | 符合其记录类型的 JSON 行。系统生成 id,另有你的 external_id / external_source,用于幂等 upsert(再次 upsert 会原位更新)。可以取消(一等状态)或归档(终止 / 非活动状态) | rekor records |
| 关系类型 | 定义 rel_type 的配置(其 ID 即为类型),可选包含元数据 Schema 及源 / 目标记录类型允许列表。创建此类型的任何关系前,必须先创建该关系类型 | rekor relationship-types |
| 关系 | 两条记录之间带类型的有向链接(端点也可以是文件),元数据经过验证;可双向遍历 | rekor relationships、rekor query-relationships |
| 筛选 DSL | 唯一的条件语言——{field, op, value} 加上 and / or 分组——用于 records query、触发器 --filter 和 Action precondition | Records → Filtering & search |
| 文件类型 | 文件的存储桶配置:最大大小、允许的内容类型、可选的元数据 Schema | rekor file-types |
| 文件 | 有版本、通过路径寻址的内容(PDF、图像、文档)。支持比较并设置写入、按版本查看历史和差异,也可挂载为 S3 | rekor files |
| 附件 | 简单的记录级上传——Files 是功能更丰富、带版本的演进方案 | rekor attachments |
| 外部数据源 | 在记录类型上声明:通过单个双向 field_mapping 契约,使用外部 API 执行其 CRUD 操作 | External Sources |
| 触发器 | 在记录 / 关系 / 文件事件发生时触发。其操作可以是发出的已签名Webhook、internal_write(对相关记录进行受保护的局部更新)或 external_write(通过数据源将更改推送到上游) | rekor triggers |
| 入站 Webhook | 外部系统向其推送数据的已签名接收 URL——负载以原始形式存储、通过映射转换,或进行数据补全(收到精简的“ID 已更改”通知 → Rekor 通过绑定的数据源获取完整记录) | rekor inbound-webhooks |
| 执行器 | 由你部署的小型 HTTP 服务,用于处理 Rekor 无法直接调用的操作(自定义逻辑、mTLS、SOAP、二进制凭证);接收已签名的调度请求,并通过 @wayai/executor-sdk 验证 | Executors |
| Signing v1 | 触发器投递、绑定到执行器的数据源调用(选择启用 signing)以及入站 Webhook 验证(默认方案)使用的 HMAC 请求签名——双向使用同一种线格式;由 @wayai/executor-sdk 实现,切勿自行实现 | Executors、Inbound Webhooks |
| Action | 单个记录类型加一项操作,并经过塑形和保护(filterable_fields、writable_fields、precondition、binding);也可以是由多个原子步骤组成的复合操作。其 ID 即面向 Agent 的工具名称 | rekor actions |
| Toolset | 精选的 MCP 服务,由 Action 引用(以及关系 / 批量 / SQL 工具)组成,服务地址为 mcp.rekor.pro/t/<slug>/mcp | rekor toolsets |
| 种子夹具 / 租约 | 为隔离的 Agent 评估提供命名且可逆的记录与关系基线。定义属于配置;apply / reset / clear 是仅限 preview 的数据操作。绑定到执行者的租约会独占重置、拥有并清除某次评估运行的夹具状态 | rekor seed、rekor seed lease |
| API 令牌 | rec_… 凭证:按授权范围授予权限(bases × record_types × environments × permissions),或绑定到 toolset(其授权即为某个 toolset 的工具范围) | rekor tokens |
| 密钥 | 组织级加密凭证(字符串或文件 Blob),在数据源 / 触发器配置中通过 vault:<name> 引用;执行器可在调度时拉取 | rekor secrets |
标识规则。 配置 slug 是永久 ID(参见 Agent Guidelines)。对于具有自然键的记录,务必设置 external_id,这样重试和重新导入时会执行更新或插入,而不是重复创建。每次记录写入都会根据其记录类型架构进行验证(还会检查 x-fk 引用并规范化日期时间)。 |
集成边。 一个 source 上的 field_mapping 契约会被每条边复用:代理读写、向外写入 external_write,以及入站 Webhook 的映射和数据补全。应先规范化实体建模,再按操作选择边:Integration Modeling 一节提供决策指南和模式目录。
一致性与限制。 单条记录的 get,以及符合条件的活跃记录 records query / query-relationships 列表会立即反映写入;sql、search,以及少数从最近同步的数据读取的查询形式(针对内置 created_at/updated_at 时间戳且不带时区偏移的筛选器)可能会在写入后短暂延迟;而在 local 层级的 base 上(评估克隆的默认层级;参见 Bases),这些相同的查询形式会直接被拒绝,而不是出现延迟。记录/关系 JSON 的上限约为 1 MiB,大型或二进制内容应放在 Files 中。
任务 → 功能对照表
将请求路由到合适的功能,然后跳转到命令参考中的相应章节。
| 你需要…… | 使用 | 章节 |
|---|---|---|
| 存储并验证结构化业务数据 | 记录类型架构 + 记录 | RecordTypes、Records |
| 修改一个字段,而无需重新发送整条记录 | 部分更新(REST PATCH / MCP patch) | Records |
| 在重试和重新导入时防止重复 | 按 --external-id 执行 upsert | Records |
| 关联实体;遍历图 | 关系类型 + 关系 | Relationship Types、Relationships |
| 让引用解析为真实记录 | 在架构字段上设置 x-fk | Records → Referential integrity |
| 按近似名称/文本查找 | 过滤 DSL search 运算符 | Records → Filtering & search |
| 报表、聚合、联接 | rekor sql | SQL Query |
| 存储 PDF / 图片 / 有版本的文档 | 文件类型 + 文件;通过 S3 挂载 | Files |
| 执行多项写入,并确保全部成功或全部回滚 | rekor batch | Batch |
| 批量加载历史数据(回填) | rekor import run(NDJSON;支持流式处理和断点续传) | Bulk Import |
| 数据变更时通知外部系统 | trigger → webhook | Triggers |
| “A 变更时,更新相关的 B” | trigger → internal_write(异步或事务性) | Triggers |
| 将原生记录镜像到上游 API | trigger → external_write,复用一个 source | Triggers、External Sources |
| 接收外部推送的完整负载 | 入站 webhook(可选字段映射) | Inbound Webhooks |
| 将“记录 X 已变更”的通知转换为完整记录 | 充实型入站 webhook(使用 merge 保留你的字段) | Inbound Webhooks |
| 像读写原生数据一样读写外部 API | 在记录类型上配置外部 source | External Sources |
| 集成 Rekor 无法直接调用的服务 | executor + @wayai/executor-sdk | Executors |
| 为大模型 Agent 提供安全且以领域命名的工具 | Actions + toolset + 绑定到 toolset 的令牌 | MCP Factory |
| 确保依赖状态的写入不会发生竞争 | Action precondition(比较并设置) | MCP Factory |
| 一个 Agent 工具执行多项原子写入 | 组合 Action | MCP Factory |
| 遵循最小权限原则的机器/CI 访问 | 限定范围的令牌(Agent 使用 create-for-toolset) | API Tokens |
| 存储集成凭据 | 密钥保管库(vault:<name> 引用) | Secrets |
| 在 git 中管理配置 / 通过 PR 审查 | rekor pull / rekor push | Config as Code |
| 执行确定性的 Agent 评估,不连接实时上游 | 使用 --integrations disabled 预览(用于临时数据时加上 --analytics local)+ seed fixtures | Bases、Seed Fixtures |
| 导入/导出提供方工具定义 | 提供方适配器 | Provider Adapters |
| 查看谁在何时更改了什么 | history 子命令(仅限管理员) | Records |
| 决定采用原生、代理还是镜像模式 | 模式目录 | Integration Modeling |
| 设计架构/工具,让 Agent 顺利完成任务 | 原则 + 配方 | Modeling Principles |
| 设计面向 Agent 的工具接口 | 工具设计原则(意图型工具、必填参数、封闭集合) | Modeling Principles |
查找更多详细信息
references/ 中打包的文件包含完整的选项语法——配置某项功能之前,请先阅读对应文件。
| 参考文件 | 何时阅读 | 内容 |
|---|---|---|
references/querying.md | 编写较复杂的 SQL、调整 search、设置时区时 | SQL 示例集(数组、ARRAY JOIN、CTE、--param、模糊排序)、x-search 模式/阈值、日期时间和时区配置 |
references/external-sources.md | 配置任何外部数据源时 | 完整的数据源语法:field_mapping 规则(值映射、转换、拆分/解构/计算、按操作覆盖)、按操作配置的端点、命名写入绑定、id_path 变体、forward_filters、local_filters、请求体整形、{{current.*}}/{{prior.*}}、缓存/签名/超时/熔断器、SSRF 规则,以及一个完整的旧版上游示例 |
references/mcp-factory.md | 编写超出默认配置的 Actions 或工具集时 | 完整的 Action 和工具集语法:filterable_fields(包括 optional)、expose_*/default_*、writable_fields、base_filter、precondition、binding、复合 Actions、工具命名、哪个 base 为工具集 slug 提供服务 |
references/executors.md | 构建或部署执行器时 | @wayai/executor-sdk 合约(验证、去重、错误封装)、签名回写、托管位置、保管库证书拉取模式、本地开发、重试 |
references/providers.md | 导入/导出提供方工具定义时 | 各提供方(OpenAI/Anthropic/Google/MCP)对应的精确 rekor providers 命令形式 |
运行时还可以查阅:
rekor <command> --help(以及rekor <command> <subcommand> --help)——任何命令的权威标志列表。rekor status——身份验证、连接状态、当前组织和工作树绑定;rekor whoami——已通过身份验证的身份。
如果某个命令、标志或 URL 未出现在这些来源中,就假设它不存在——切勿猜测。
首次设置流程
用户从零开始时,请引导他们完成以下步骤。已完成的步骤可以跳过——请先运行 rekor bases list 检查,再做判断。
1. 安装 CLI
npm install -g rekor-cli
运行 rekor --version 验证。CLI 以 rekor-cli 的名称发布到 npm;二进制文件名为 rekor。
2. 身份验证
使用 API 密钥进行身份验证:
rekor login --token rec_xxx
此 API 只接受 API 密钥。 浏览器登录(不带 --token 的 rekor login)已停用:后端不再验证任何浏览器或 OAuth 凭据,因此通过这种方式获得的会话会在后续每条命令中被拒绝。请向账户管理者获取密钥——管理你所在组织的平台会签发密钥,并且每个密钥都已限定到唯一一个组织。
rekor init 也已停用。它原本用于将仓库绑定到你的某个组织,因此需要用户登录来枚举组织;而 rec_ 密钥本身已包含所属组织,无需再选择。
密钥决定组织,且不会被任何设置覆盖。 已提交的 .rekor.yaml、--org 标志和 REKOR_ORG 都会传入一个服务器不再读取的请求标头,因此不会改变任何内容——即便通过 --org 指定的组织并非签发该密钥的组织,操作仍会作用于密钥所属组织,且不会报错。要在其他组织中工作,请使用在该组织签发的密钥(REKOR_TOKEN,或 rekor login --token)。
如果用户还没有组织,应向管理其账户的平台申请——此处已停用自助注册。免费套餐每月包含 5,000 次操作。
3. 创建基础
基础是顶层数据容器。每个应用、域或租户创建一个。
rekor bases create <id> --name "<Display Name>" [--tags <comma-separated>]
在用户的 shell 中设置 REKOR_BASE=<id>,即可避免在每条命令中传递 --base。
**生产基础需要付费套餐。**使用免费套餐时,此命令会失败并提示升级;请改为创建预览基础,并在其中完成所有工作:
rekor bases create <id> --name "<Display Name>" --environment preview
其他所有内容(record_types、records、relationships、triggers、toolsets)的行为都相同——这是一个完整的开发环境。
以这种方式创建的预览没有生产来源,之后也无法附加来源(API 会拒绝此尝试,而不是默默忽略),因此无法直接将其提升。升级后,在它旁边创建生产基础,并将配置复制过去:
rekor pull <preview-id> # config → rekor-ws/bases/<preview-id>/
rekor unbind # release this checkout's binding to it
rekor bases create <prod-id> --name "<Display Name>" # allowed once on a paid plan
rekor bases create-preview <prod-id> --name dev # a preview linked to production
rekor pull <prod-id>--dev # scaffolds rekor-ws/bases/<prod-id>--dev/
# copy ALL seven entity folders — record-types/, relationship-types/, inbound-webhooks/,
# triggers/, toolsets/, actions/, seeds/ — from rekor-ws/bases/<preview-id>/
# into rekor-ws/bases/<prod-id>--dev/ (push is additive, so anything you skip is
# silently absent rather than reported as a removal)
rekor push <prod-id>--dev
rekor bases promote <prod-id> --from <prod-id>--dev
有两点可以确保这是文件复制,而不是 ID 编辑:push <selector> 会查找名称为 <selector> 的文件夹(仅编辑 base.yaml 不会改变目标);pull/push 会将检出内容绑定到它们操作的基础——因此切换前要运行 rekor unbind。记录和关系是数据,不是配置:它们会留在原预览中,不会随 pull/push 一起移动。
rekor bases promote 也需要付费套餐。生产基础的读取和写入不会停止——只有向其部署新配置会停止;无论如何,--dry-run 和 rekor bases rollback 始终可用。
4. 创建预览环境
在生产基础中,架构变更(record_types、入站 Webhook、triggers、MCP Factory toolsets)会被阻止。创建预览以进行架构工作:
rekor bases create-preview <id> --name "<preview-slug>"
生成的预览基础 ID 为 <id>--<preview-slug>。将其用作所有架构命令的 --base。
来源可以是生产基础**,也可以是另一个预览**——克隆预览会生成 <preview-id>--<preview-slug>,并关联到被克隆的预览。
从另一个预览克隆的预览会创建在 local 分析层级上,在该层级中,不允许对记录运行 rekor sql、查看 history 和遍历关系——请参阅 Bases 下的分析层级。如果要克隆预览来源以进行架构工作,并打算使用 rekor sql 查询(第 6 步),请传递 --analytics standard;之后无法更改层级。克隆生产来源不受影响。
如果你在第 3 步中将 <id> 创建为预览(免费套餐),此步骤仍然可用,但意义不大:从预览克隆的预览会通过其来源进行提升,无法直接提升到生产;它会创建在上文所述的 local 层级;无论如何,第 6 步的 promote 仍要求付费套餐。最简单的做法是跳过此步骤,直接在 <id> 中工作——只要在任何写有 --base <id>--<preview-slug> 的地方改为传递 --base <id>,第 5 步和第 6 步就无需更改,第 6 步的测试命令也包括在内。在第 6 步中,跳过 promote:没有可供提升到的生产基础。
5. 定义第一个 record_type
record_type 是一种用于定义记录类型的 JSON Schema。在预览基础中定义它:
rekor record-types upsert <slug> --base <id>--<preview-slug> \
--name "<Display Name>" \
--schema '{"type":"object","properties":{...},"required":[...]}'
对于超过一行的架构,请将其写入文件,并传递 --schema @path/to/schema.json。
6. 在预览中测试,然后请用户提升
向预览写入一些测试记录,查询它们,并不断迭代,直到架构正确:
rekor records upsert <slug> --base <id>--<preview-slug> --data '{...}'
rekor sql "SELECT * FROM records WHERE org_id = {org_id:String} AND base_id = {base_id:String} AND record_type = '<slug>' AND deleted = false" --base <id>--<preview-slug>
准备就绪后,向用户展示要运行的提升命令(你无法自行执行提升):
rekor bases promote <id> --from <id>--<preview-slug>
建议先使用 --dry-run 预览差异。
7. 连接生产环境中的 Agent
让生产环境中的 Agent 访问有两种方式:
限定范围的 API 令牌 — 用于直接通过 REST/CLI 访问:
rekor tokens create --name "<agent-name>" \
--grants '[{"scope":{"bases":["<id>"],"environments":["production"]},"permissions":["read:records","write:records"]}]'
原始令牌(rec_...)只会显示一次。请立即复制。
MCP Factory 工具集 — 为每个 Agent 提供专用的 MCP 工具(推荐用于大模型 Agent):
请参阅下方的 MCP Factory 部分。
除了第一种记录类型之外,关系、文件、触发器、集成、工具集、评测等功能,请使用上方的 Task → Feature Map 找到合适的功能。当配置涉及多个实体时,优先使用 Config as Code(rekor pull/push),而不是一次性命令,这样变更就会以文件形式保存,便于审查。
快速入门
核心的 schema→实例配对示例。--base my-ws 是占位符——对于下面的配置写入(record-types upsert、relationship-types upsert),它必须指定一个预览库。
1. 创建一个 record_type
rekor record-types upsert invoices --base my-ws \
--name "Invoices" \
--schema '{"type":"object","properties":{"customer":{"type":"string"},"amount":{"type":"number"},"status":{"type":"string","enum":["draft","issued","paid"]}},"required":["customer","amount"]}'
2. 创建一条记录
rekor records upsert invoices --base my-ws \
--data '{"customer":"Acme Corp","amount":5000,"status":"draft"}'
使用外部 ID 执行幂等 upsert:
rekor records upsert invoices --base my-ws \
--data '{"customer":"Acme Corp","amount":5000}' \
--external-id inv_123 --external-source billing
3. 查询记录
rekor sql "SELECT data.invoice_number.:String as num, data.status.:String as status, arraySum(CAST(data.line_items[].amount, 'Array(Float64)')) as total FROM records WHERE org_id = {org_id:String} AND base_id = {base_id:String} AND record_type = 'invoices' AND deleted = false ORDER BY total DESC" --base my-ws
4. 声明关系类型,然后关联记录
必须先创建关系类型,才能关联记录。先声明一次(这是配置写入——请在预览环境中执行,然后再晋升):
rekor relationship-types upsert belongs_to --base my-ws \
--description "Invoice belongs to a customer" \
--source-record_types '["invoices"]' --target-record_types '["customers"]'
然后关联:
rekor relationships upsert --base my-ws \
--source invoices/rec_abc --target customers/rec_xyz \
--type belongs_to
省略 --schema 即可允许使用任意元数据;传入 --schema 则会根据 JSON Schema 验证关系的 --data。
每个端点也可以使用 external_id(而非内部记录 ID)进行指定——如果你刚通过 external_id 执行了 upsert,且没有保留返回的 ID,这种方式会很方便:
rekor relationships upsert --base my-ws \
--source-external invoices/inv-2026-001 --target-external customers/cust-42 \
--type belongs_to
每个端点的 --source/--source-external 必须且只能指定一个(目标端点同理);如果 external_id 属于某个源系统,请添加 --source-external-source <name>。两种模式对缺失记录的处理方式不同:内部 ID 会按给定值存储(允许在记录创建前建立关联),而使用外部 ID 要求记录已存在——否则写入会失败,并返回清晰的错误信息。API 中对应的字段为 source_external_id/target_external_id(以及可选的 *_external_source),用于替代 source_id/target_id。
5. 遍历关系
rekor query-relationships invoices rec_abc --base my-ws \
--type belongs_to --direction outgoing
完整命令参考
在交互式终端中,具有破坏性的
delete命令会提示确认。传入-y/--yes可跳过提示(在非交互式/CI 环境中也会自动跳过)。
库
rekor bases list [--tag <tag>]
rekor bases get <id>
rekor bases create <id> --name <name> [--description <desc>] [--tags <comma-separated>] [--environment <production|preview>] [--analytics <standard|local>] # production requires a paid plan
rekor bases rename <id> --name <new-name> # display name only — the id/slug is immutable
rekor bases tag <id> --tags <comma-separated>
rekor bases delete <id> [--purge] # --purge is preview-only: also reclaims storage
rekor bases create-preview <origin-id> --name <preview-slug> [--description <desc>] [--integrations <enabled|disabled>] [--analytics <standard|local>] [--create-only]
rekor bases update <preview-id> --integrations <enabled|disabled> # preview-only eval toggle
rekor bases list-previews <origin-id>
rekor bases promote <prod-id> --from <preview-id> [--dry-run] [--record_types <ids>] [--triggers <ids>] [--inbound-webhooks <ids>]
rekor bases promotions <prod-id>
rekor bases rollback <prod-id> --promotion <promotion-id>
标签可用于对库进行分组(例如 client:acme,billing)。使用 --tag 进行筛选。晋升操作仅限人工执行(请参阅环境)。使用 --record_types/--triggers/--inbound-webhooks 选择性晋升(省略则晋升所有内容)。promotions 会列出之前的晋升记录;rollback 会按 ID 撤销其中一项。
create-preview 可从生产库或另一个预览库克隆;新 ID 为 <origin-id>--<preview-slug>,并会关联到其克隆来源,因此,预览库的预览库会经由该来源晋升,而不是直接晋升到生产环境。重新运行该命令会将配置重新应用到现有预览库,但不会更改其中的数据——传入 --create-only 可改为在预览库已存在时失败。对于一次性运行的临时库,你应该使用该参数,以免落到其他人的库中,导致双方的运行都受到影响。
删除与清除。 rekor bases delete <id> 是软删除:该库不再显示在列表中,但其记录、关系、文件元数据和配置都会保留,重新创建该 ID 即可恢复。--purge(仅限预览库)会永久销毁这些内容。清除后的 ID 将被废弃——无法重新创建,因此下次运行时请生成新的 ID。使用 purge 拆除临时库;普通删除会让所有内容永久保留。
purge 不会移除两类内容:已上传的文件内容(只会移除用于索引这些文件的元数据),以及该库在分析存储中的行。ID 废弃后,这两类内容都无法再访问,但仍会占用存储空间。
评估模式(--integrations disabled)。 预览环境可以在禁用外部集成的情况下运行(默认为 enabled)。禁用后,所有外部连接都会停用——record_type 数据源、出站 external_write 触发器和入站 Webhook 数据填充均不例外;预览环境会使用自己的预置数据,并提供完全相同的架构、工具和字段映射,不会调用外部系统。因此,预览环境可作为确定性且适用于生产环境的Agent 评估目标:针对相同的标准接口测试 Agent 的策略,同时避免污染仅用于生产环境的上游系统,也不会触发实时速率限制或接触个人身份信息(PII)。在禁用状态下写入记录即可为预览环境填充数据(写入由数据源支持的 record_type 时,数据会保存在本地);切换为 enabled 可测试真实集成;从生产环境重新克隆可避免数据偏离。生产环境的基础始终为 enabled——此开关仅适用于预览环境。
分析层级(--analytics)。 预览环境可以完全不将数据存入分析存储。在 local 模式下,记录和关系仅保存在基础自身中,不会进行镜像;这适合一次性评估基础,因为运行结束后其中的数据便不再有用。
从另一个预览环境克隆的预览环境默认使用 local——这种配置就是每个会话的评估克隆。从生产环境基础克隆的预览环境,以及通过 rekor bases create 创建的所有基础,默认使用 standard。在任一路径中传入 --analytics 都可以显式选择,该选项优先于默认值。
有两个后果,其中第一项是数据丢失,而非读取受限:
- 完全不会记录变更历史。 历史会被丢弃,而非转移——此类基础上的任何写入都不会在任何位置生成审计行。
history子命令和审计端点会返回错误,而非空列表;但错误背后没有任何可供恢复的数据。对于可能需要查询历史的基础,切勿选择local。 - 原本需要从分析存储读取的数据无法读取,系统会拒绝请求,而非返回空结果: 对记录表执行的原始 SQL,以及关系遍历。单条记录读取和普通的筛选 DSL 列表会直接从基础自身提供数据,照常工作;但基础无法独立处理的查询也会被拒绝。这包括
search运算符、引用archived的筛选条件、将created_at/updated_at与未携带时区偏移量的日期时间字面值进行比较,以及作用范围内实时行数超过基础内上限(10,000)的任何列表。记录的上限按 record_type 计算,关系的上限则是整个基础范围内的总数;上限按分页之前的整个作用范围计算——调小--limit无法突破上限,因此预置测试数据时应确保数量低于上限。
规划评估断言时,请使用针对 data.* 字段的精确筛选条件,并确保所有 created_at/updated_at 范围条件都带有明确的偏移量(2026-08-01T00:00:00Z)。
速览——local 基础不会提供哪些功能,以及可以改用什么:
不适用于 local 基础版的功能 | 替代方案 |
|---|---|
对 records、relationships 或 audit_log 执行 rekor sql(以及工具集中的 sql_query 工具) | 使用 Filter DSL 执行 records query。其他所有表——record_types、relationship_types、bases、organization、operations_log——仍可查询,不过此层级的 operations_log 仅包含计费行 |
对任何实体执行 history,以及审计端点 | 无——这些基础版永远不会记录历史,因此没有替代方案 |
关系遍历(rekor query-relationships、工具集中的关系列表工具) | 关系列表接口(GET /v1/{base}/relationships?filter=…),通过端点 ID 查询 |
使用 search 的筛选条件,或引用 archived 的条件 | 对 data.* 使用精确匹配或 like 条件。此处的已归档行不属于单独的层级 |
内置的 created_at/updated_at 与不含时区偏移量的值进行比较 | 为边界值提供时区偏移量或 Z。你自己的 data.* 日期时间字段不受影响 |
| 范围超过 10,000 条未归档行的列表 | 将测试数据控制在上限内,或使用 standard 基础版 |
此层级在创建基础版时即固定,且仅用于预览。无法双向切换——更改后不会协调存储,因此在错误层级上创建的基础版只能替换(使用 --purge,并采用新 ID,因为已清除的 ID 会被弃用)。创建时请谨慎设置:
rekor bases create-preview <origin> --name eval-run-42 --integrations disabled --analytics local --create-only
如果你之后会用到历史记录或 SQL,请改用 --analytics standard——包括克隆预览源的情况,因为这类克隆默认使用 local。MCP manage_base 的 create_preview 接受相同的 analytics 参数。如果新基础版使用 local,同时包含提供 sql_query 或关系列表工具的工具集,create-preview(以及从预览 origin_base_id 自动创建基础版时的 rekor push)会发出警告——这些工具在此层级下会永久拒绝请求。
配置即代码(拉取 / 推送)
将基础版的整套配置——record_types、关系类型、入站 Webhook、触发器、MCP 工具集、种子测试数据——作为受版本控制的 YAML 文件进行管理,而不是使用一次性命令。文件位于 rekor-ws/bases/<db>/ 下,每个实体对应一个文件,方便在拉取请求中审查更改。
rekor pull <preview> # write the preview's config + a read-only mirror of linked production
rekor push <preview> [--dry-run] # apply your local files to the preview (--dry-run shows the diff only)
rekor push <preview> --prune # also delete entities that exist on the server but not in your files
rekor use <preview> # bind THIS git worktree to a base (push/pull refuse a different one)
rekor unbind # clear this worktree's base binding
- 只能编辑预览环境。
push只会写入预览基础(生产环境的配置绝不会直接编辑)。pull预览环境时,关联的生产配置也会作为只读参考文件夹写入其旁边(rekor-ws/bases/<prod>/),这样你可以在迭代时查看线上生产配置——该文件夹会标记为只读(push会拒绝写入,裸pull/push的自动选择也会忽略它)。直接pull <production>只会写入该只读参考文件夹。要发布,请照常运行rekor bases promote。 - 自动创建预览环境。 使用
origin_base_id: <prod-id>(且不含base_id)创建rekor-ws/bases/<name>/base.yaml脚手架;rekor push会从该生产基础创建预览环境,将新 ID 写回文件,然后应用你的文件。 base.yaml用于描述基础,不会重新配置基础。 除base_id/origin_base_id外,pull还会在此记录基础的environment、integrations和analytics,以便文件夹说明其目标。对于已有base_id的文件夹,这些内容纯属描述性信息——编辑它们不会改变任何内容,而且只能在创建时设置的analytics层级绝不能通过这种方式更改;如果值出乎意料,应将其视为重新检查基础的信号,而不是编辑文件。**唯一例外是自动创建分支中的analytics:**携带origin_base_id且不含base_id的新文件夹会将声明的层级传递给push创建的基础,这是设置该值的唯一机会。如果来源是预览环境(其克隆默认设为local),而你需要 SQL、历史记录或遍历功能,请在首次push前声明analytics: standard。- 工作树绑定(路由保护)。 每个 Git 检出目录(主工作树或关联工作树)只能绑定到一个基础。一旦绑定,
pull/push就会拒绝针对其他基础运行——以此捕获常见错误:提示词落在错误的终端/工作树中,覆盖了另一个预览环境的配置。绑定会在未绑定的检出目录首次成功执行pull/push时自动设置(创建新预览环境时也会设置),每个工作树各自独立,且绝不会提交。rekor status会显示当前绑定。如果pull/push因绑定不匹配而报错,请停止操作并先询问用户——这通常表示该提示词原本是为另一个工作树准备的。除非当前会话中用户明确指示,否则不要运行rekor unbind/rekor use;更改绑定属于路由决策,就像切换用户认为你正在操作的基础一样。 - 绝不会将密钥写入文件。
pull时会剥离入站 webhook/触发器和外部源密钥。push时,新添加的入站 webhook/触发器会生成一个新的密钥(只显示一次,请保存);现有密钥则保持不变。使用rekor inbound-webhooks/rekor triggers/rekor secrets管理密钥值。 - 删除操作需显式选择。 由于删除
record_type也会删除其记录,push默认采用增量方式:文件中缺少的实体会被报告,但仍会保留。添加--prune可将其删除。 - 在
AGENTS.md中记录基础上下文。 执行pull/push时,Rekor 会在每个基础文件夹(rekor-ws/bases/<db>/)中创建AGENTS.md和一行内容的CLAUDE.md@AGENTS.md转接文件(仅在文件不存在时创建,绝不覆盖你的编辑)。使用基础文件夹中的AGENTS.md记录配置本身无法体现的内容:基础的用途、关键决策及原因、业务规则、术语和集成注意事项。随着基础演进及时更新——这是下一个 Agent 在处理此数据层前会读取的持久记忆。如果文件缺失(例如,较早版本的pull未创建),请自行按相同格式创建。 - 重命名
record_type应重新播种,而非原地重命名。record_type的id就是其身份标识,必须与其文件名(record-types/<id>.yaml)一致,因此重命名时必须同时更新文件名和内部的id:字段(只改其中一项会报错;也可以删除内部的id:,使其默认采用文件名)。无论哪种方式,这都是创建新项并遗留旧项,而非原地重命名:差异会显示+ <new>/- <old>,push会创建一个新的空record_type,而旧项(及其所有记录)会保持不变——记录不会迁移。如要重命名并保留数据:(1) 执行push创建新的空record_type,(2) 重新播种其记录(例如使用rekor records upsert或批量操作),然后 (3) 执行push --prune删除孤立的旧record_type,该操作会级联删除其记录。关系类型和其他配置实体也适用相同的文件名必须等于id的规则。
RecordTypes
rekor record-types list --base <ws>
rekor record-types get <id> --base <ws>
rekor record-types upsert <id> --base <ws> --name <name> --schema <json|@file> [--description <desc>] [--icon <name>] [--color <hex>] [--sources <json|@file>]
rekor record-types delete <id> --base <ws>
rekor record-types history <id> --base <ws> [--limit <n>] [--offset <n>] [--diff]
--icon/--color 是检查界面的显示提示。--sources 用于声明外部来源——请参阅下文的“外部来源”部分。
删除 record_type 也会移除其所有记录(以及所有指向这些记录的关系);使用相同的 id 重新创建 record_type 时,它将为空。
Records
rekor records upsert <record_type> --base <ws> --data <json|@file> [--id <uuid>] [--external-id <id>] [--external-source <src>]
rekor records get <record_type> <id> --base <ws>
rekor records query <record_type> --base <ws> [--filter <json|@file>] [--sort <json>] [--fields <list>] [--limit <n>] [--offset <n>] [--external-source <src>]
rekor records delete <record_type> <id> --base <ws>
rekor records history <id> --base <ws> [--limit <n>] [--offset <n>] [--diff]
history 会返回实体的完整变更历史——每个版本的快照、修改者、操作以及修改时间。仅限管理员使用:平台管理员或获得 read:audit 权限的令牌可以使用;普通 Agent 令牌会被拒绝。(relationships、record_types 和 relationship-types 也有同名的 history 子命令。)local 层级的评估克隆不会保留变更历史——请参阅基础 → Analytics 层级。
筛选与搜索。 query(REST GET /records/<record_type>、MCP query_records)接受 Filter DSL 表达式——条件 {field, op, value},或由这些条件组成的 and/or 组。运算符包括:eq、neq、gt、gte、lt、lte、in、not_in、like、ilike、is_null、is_not_null、has 和 search。仅接受此 DSL 格式——不支持 Mongo 风格的简写({"data.field":"value"}、{"field":{"$in":[...]}})。
列出和筛选活动记录(及关系)时,写后读一致——刚写入的记录会立即出现在下一次列表结果中,不会有延迟。少数查询形式会改为读取近期同步的数据(因此刚写入的记录可能需要片刻才会出现):search 查询(见下文),以及将内置的 created_at/updated_at 时间戳与未指定时区偏移的值进行比较的筛选条件。请求投影字段子集本身不会改变这一点——投影不会导致查询转为读取近期同步数据的路径。local 层级的基础——从另一个预览克隆的预览会创建为此层级,请参阅基础 → Analytics 层级——完全没有近期同步副本,因此这两种查询形式都会报错拒绝,而不是出现延迟。
search 会按近似值匹配字段——当你知道一个接近正确但并非存储值完全一致的字符串时,可以使用它(例如模型名称、地点、计划名称或可能有拼写错误的 SKU)。结果会按接近程度排序,每条结果都包含 _search_score(0–1,数值越高表示越接近):
// "find vehicles whose model is close to 'honda civic', only active ones"
{ "and": [
{ "field": "data.status", "op": "eq", "value": "active" },
{ "field": "data.car_model", "op": "search", "value": "honda civic" }
] }
默认情况下,所有字段都可搜索——search 无需设置。精确筛选条件和 search 可以组合在同一个查询中:将精确条件与 search 条件并列,以便精确条件缩小结果集,再由 search 对结果排序。要调整字段的匹配方式——x-search 模式(name/text/fuzzy)、threshold 和 searchable: false——请参阅 references/querying.md。搜索基于最新同步的数据,因此刚刚写入的记录可能需要片刻才会出现。要通过工具集向 Agent 提供 search(及其调整选项),请在 list Action 的 filterable_fields 中声明 match: search——只有此匹配方式会使用该运算符。
排序。 --sort 接受一个JSON 数组,其中每个项的格式为 {"field":"data.<field>","direction":"asc"|"desc"}——例如 --sort '[{"field":"data.created_at","direction":"desc"}]'。多个项按字典顺序排序(第一项为主要排序条件)。省略 direction 时默认为 asc。不接受冒号简写 data.name:asc——请传入 JSON 数组格式。使用 search 时请省略 --sort,以保留相关性排序。
日期时间。 在 record_type 架构中,将日期时间字段声明为 "format": "date-time"(仅日期则使用 "format": "date")。Rekor 会将它们存储为统一的规范形式:不带时区偏移的值会附加 record_type 的时区,因此,2026-06-11T13:00:00 会存储并返回为 2026-06-11T13:00:00-03:00——无论通过 query 还是单条记录读取,形式都相同,你无需进行任何 UTC 换算。时区按 record_type x-timezone → 基础 settings.timezone → UTC 的顺序确定。筛选会感知时刻: eq/neq/in/not_in 和范围运算符会根据日期时间所表示的时间点进行匹配,因此你传入的具体表示形式(T 还是空格、是否带偏移量)不会改变结果。要精确匹配日期时间,请使用 eq,而不是 like(普通子字符串匹配)。原始 rekor sql 不会进行这样的改写——日期时间条件请优先使用 Filter DSL query。时区设置(CLI --timezone、REST、settings 的合并与替换语义)详见 references/querying.md。
部分更新与完整更新。 完整 upsert(manage_record upsert、REST PUT)会写入整条记录——请发送每个字段。若只需更改一个字段(切换状态、设置标志),而不想重新发送其余字段,请使用部分更新:manage_record patch、生成的 update_<record_type> 工具,或 REST PATCH .../records/<record_type>/<id>。部分更新会将提供的字段合并到已存储的记录中(顶层合并;嵌套对象会整体替换),通过 id 或 external_id 定位现有记录,且合并后的结果仍必须符合 record_type 架构。对于由外部来源支持的 record_type,提供的字段会转发给上游系统,由上游系统决定如何应用更新——合并并保留其余字段的保证仅适用于存储在 Rekor 中的记录。
取消与归档。 默认情况下,记录可以更新——对同一个 external_id 重新 upsert 会原地更新现有记录(幂等 upsert),你也可以自由推进状态(例如,scheduled → confirmed)。record_type 可以通过其架构中的 x-archival.mode 选择更严格的归档策略:immutable(只写一次——任何更新都会被拒绝)或 on_attribute(在达到配置的终止值之前可以更新)。记录也可以通过 MCP(manage_record cancel)或 REST(POST .../records/<record_type>/<id>/cancel)取消——这是一种与删除不同的一等状态;CLI 没有 cancel 子命令。已取消的记录无法再更新。记录也可能会自动归档(达到终止/不可变状态时,或长期未活动后)。已归档的记录仍可读取,也仍可取消,但不能删除;使用同一个 external_id 重新 upsert 会创建一条新记录,而不是更新已归档的记录。列表查询默认返回活动记录——rekor records list / rekor relationships list、查询工具和记录遍历都会排除已归档记录。若要通过列表查询已归档记录,请添加针对 archived 的筛选条件(例如,{"field":"archived","op":"eq","value":true})——之后由你的条件决定会看到哪一类记录。记录遍历没有筛选覆盖选项——只有通过原始 SQL 才能访问已归档的链接。原始 rekor sql 不应用任何默认条件:若只要活动记录,请自行添加 archived = false(已取消的行会带有 cancelled = true)。
引用完整性(外键)。 Record 类型架构根目录 properties 的直接子项可以添加 x-fk 提示,声明为指向另一个 record_type 的外键。引用方 Record 类型必须是原生类型(sources: [])。此后,每次原生写入都会检查该值是否指向真实记录——如果引用不存在,系统会拒绝写入并提供可操作的错误信息,而不是让错误数据悄然写入。嵌套或组合声明(例如 insurance.plan_id)、基于数据源的引用方 Record 类型上的声明,以及 Relationship 类型 data_schema 中的声明,都会在配置写入时被拒绝,并显示精确的架构指针。将嵌套引用提升为根属性、移除 x-fk,或将引用方 Record 类型设为原生类型。现有生产环境中的旧版配置会在关联预览环境中进行修正期间继续提供服务;提升预览环境的试运行会报告最终配置中的所有阻塞项,在修复这些问题之前,实际提升操作仍会被阻止。
"patient_id": { "type": "string", "x-fk": { "record_type": "patients" } }, // matches patients by external_id (default)
"professional_id": { "type": "string", "x-fk": { "record_type": "professionals", "key": "code" } }, // matches a named field on the target
"note_patient_id": { "type": "string", "x-fk": { "record_type": "patients", "key": "id" } } // matches the target's system id
key是目标记录中必须与该值匹配的字段——默认是external_id,也可以是目标的系统id或任意顶层字段(例如code)。- 如果目标是没有 external_id 的原生记录,请使用
key: "id"。系统 ID 会在记录创建时分配,因此请先创建目标记录,再读取其id并进行引用——这无法通过一次批量操作或种子数据文件完成(它们通过external_id解析),因此在这些场景中请使用external_id/数据键。 - 每次写入时都会检查(创建、完整 upsert、部分更新、批量写入)。空值/缺失值会被跳过——如果该字段必须存在,请将其标记为
required。 - 限制针对的是引用方类型。目标本身可以使用外部数据源;由于这类目标的记录存储在上游,Rekor 会跳过本地存在性检查。
- 在批量写入中,请先写入目标,再写入引用它的记录(批量操作具有原子性——错误引用会导致整个批量操作被拒绝)。此顺序仅适用于
external_id/数据键——使用id键时,必须先在之前的一次写入中创建目标(见上文)。 - 外键适用于参考数据(患者、计划、产品),不适用于高吞吐量的事件日志——被引用的 record_type 会保持随时可用,以确保检查速度。
附件
将大型或二进制内容(PDF、图片、文件)作为记录的附件存储,并在记录数据中引用它们——记录/关系 JSON 用于结构化数据,大小上限约为 1 MiB,因此不允许内联 Base64 数据块。文件名可以包含路径以构建文件夹结构(例如 docs/guide.md)。如需版本控制、元数据架构、比较并设置或挂载 S3,请优先使用 Files(见下文)——它是功能更丰富的附件演进方案。
rekor attachments upload <record_type> <id> --base <ws> --filename <name> [--content-type <mime>] [--file <path>]
rekor attachments url <record_type> <id> --base <ws> --filename <name>
rekor attachments list <record_type> <id> --base <ws> [--prefix <path>]
rekor attachments delete <record_type> <id> <attachment-id> --base <ws>
使用 upload 和 --file 可直接上传本地文件;不使用 --file 时,upload 会返回一个预签名 URL,你可以通过 PUT 将字节上传到该 URL。url 会返回用于获取现有附件的下载 URL。--prefix 可按路径前缀筛选 list(例如 docs/)。
文件
一等公民、带版本控制、按路径寻址的文件——附件的增强版。将 PDF、图片、生成的报告或任何大型/二进制内容存储为文件类型中的文件(一种配置存储策略的「存储桶」,包括最大大小、允许的内容类型,以及可选的元数据架构)。文件通过可变的 path 寻址;重命名只会移动地址,不会改变字节内容。文件支持比较并设置(通过 --if-match,仅当当前内容哈希匹配时才上传)、结构化元数据,以及与记录关联。
配置文件类型(先在预览版基础中配置,再进行发布):
rekor file-types upsert <id> --base <ws> --name <name> [--max-size <bytes>] [--allowed-content-types <json|@file>] [--metadata-schema <json|@file>]
rekor file-types list --base <ws>
rekor file-types get <id> --base <ws>
rekor file-types delete <id> --base <ws>
管理文件:
rekor files put <file_type> <path> --base <ws> --file <local> [--content-type <mime>] [--metadata <json|@file>] [--label <tag>] [--if-match <sha256>]
rekor files get <file_type> <path> --base <ws> [--output <local>] [--meta] [--version <n>]
rekor files list <file_type> --base <ws> [--prefix <path>] [--depth <n>]
rekor files mv <file_type> <from> <to> --base <ws>
rekor files rm <file_type> <path> --base <ws>
rekor files history <file_type> <path> --base <ws> [--limit <n>] [--offset <n>]
rekor files diff <file_type> <path> --base <ws> --from <n> --to <n>
rekor files mount <file_type> --base <ws> [--mode <read_only|read_write>] [--path <prefix>] [--expires-in <seconds>]
文件内容版本。 文件内容每次发生变更,都会保留为不可变版本。在执行 put 时使用 --label 为版本添加标签(例如 --label "proposal 1"),并使用 rekor files get --version <n> 读取任意早期版本(默认读取最新版本)。重命名或重新标记文件——仅变更元数据,不改变字节内容——不会创建新版本。使用 rekor files history <file_type> <path> 列出文件的版本,使用 rekor files diff <file_type> <path> --from <n> --to <n> 比较两个版本——结果包含元数据差异,以及文本文件逐行的内容差异。
将文件类型挂载为文件系统。 rekor files mount <file_type> 会生成兼容 S3 的凭据,以便任何支持 S3 的执行环境或工具(如 s3fs、云存储挂载 SDK)都能将存储桶挂载为本地文件夹,并通过常规文件系统调用读取文件,无需为每种工具单独集成。命令会输出端点、存储桶名称和访问密钥(密钥仅显示一次);使用路径风格寻址,将 S3 客户端指向这些信息。使用 --path <prefix>(可重复指定)将凭据限制在子树中,并使用 --mode read_only(默认值)或 --mode read_write 设置其访问权限;生成写入凭据需要对文件具有写入权限。read_write 挂载还可以写回文件——写入可防止冲突,并带有版本控制:每次变更都会与开始操作时的版本进行核对(创建文件要求该路径不存在;更新文件要求自读取后文件未发生变更),因此会拒绝过时的覆盖操作,而不是静默覆盖并发变更。每次写入都会创建新版本,并保留完整历史记录。(read_only 挂载只能读取;如需在其他位置写入,请使用 rekor files put 或 API。)
将文件关联到记录。 关系可以指向文件(其端点类型为 file)。使用 GET /v1/{base}/records/{record_type}/{id}/related-files 列出记录关联的文件。若要一步创建文件并将其关联到记录,请使用 POST /v1/{base}/records/{record_type}/{id}/attach(请求体为文件字节;file_type、rel_type、path 为查询参数)。如果关系类型声明为 cascade,删除关联(或所属关系)时会移除文件;非级联关联只是普通引用,因此共享给多个记录的文件仍会保留。生产环境中的 Agent 使用 manage_file MCP 工具管理文件(创建文本内容/获取元数据/列出/移动/删除/查看历史/比较差异),并使用 manage_file_type 管理文件类型。
SQL 查询
直接针对基础数据执行只读 SQL 查询。支持筛选、聚合、JOIN、CTE 和数组操作。
rekor sql "<query>" --base <ws> [--param key=value ...] [--file query.sql]
表:records、relationships、record_types、relationship_types、bases、operations_log、organization、audit_log
organization 表提供组织级元数据。其 plan 和 status 列不再维护,也不代表任何计费信息——你的套餐归管理你账户的平台所有,与此处无关。请勿将这些列视为权益信息;根据组织的创建时间,它们可能为空字符串,也可能是过时的默认值。
三个表没有 base_id 列,因此应将 {base_id:String} 绑定到实际标识 base 的列:
| 表 | Base 范围限定条件 |
|---|---|
bases | id = {base_id:String} |
operations_log | data.base_id.:String = {base_id:String} |
organization | 无——该表为组织级数据,不按 base 分行 |
因此,此 Base 范围的端点无法查询 organization;请通过组织级 SQL 接口(POST /v1/orgs/{org_id}/sql)读取,该接口仅按 {org_id:String} 限定范围。在这三种情况下,都要保留 org_id = {org_id:String}。
audit_log 表记录了 rekor records history 背后的变更历史——包含每个实体的所有版本及操作者。任何引用该表的查询(包括通过 JOIN 引用)都受到相同的管理员权限限制:平台管理员,或获得 read:audit 授权的令牌。该表和 operations_log 都是仅追加表,因此两者都不需要 deleted = false 条件。
重要:始终同时包含 org_id = {org_id:String} 和 base_id = {base_id:String}(根据上表绑定到标识 base 的列),以及 deleted = false——但仅追加表 audit_log 和 operations_log 除外,它们没有 deleted 列,因此不接受该条件。两个范围限定条件都不可缺少——base id 在同一 org 内唯一,并非全局唯一,因此必须提供 org_id 才能隔离数据。这两个占位符会由服务器根据经过身份验证的 org 和 base 绑定。records 表还包含 archived 和 cancelled 布尔列——添加 archived = false 可仅查询活动记录。查询始终读取每一行的最新版本——服务器会处理去重。
评估克隆:从另一个预览克隆的预览会运行在 local 分析层级,在该层级上,针对 records、relationships 和 audit_log 的 SQL 会被拒绝;其他表仍可正常查询——请参阅 Bases → Analytics tier。
访问 JSON 字段:使用 data.field.:Type 子列语法指定原生 JSON 类型。需要进行类型安全转换时,使用 CAST(data.field, 'Type')(例如,将以 Int64 而非 Float64 存储的整数转换为相应类型)。
示例:
# Simple query
rekor sql "SELECT data.invoice_number.:String as num, data.status.:String as status FROM records WHERE org_id = {org_id:String} AND base_id = {base_id:String} AND record_type = 'invoices' AND deleted = false" --base my-ws
# Aggregation
rekor sql "SELECT data.status.:String as status, count() as cnt FROM records WHERE org_id = {org_id:String} AND base_id = {base_id:String} AND record_type = 'invoices' AND deleted = false GROUP BY status" --base my-ws
如需查看完整示例集——数组聚合、用于展开嵌入式数组的 ARRAY JOIN、将记录与关系连接的 CTE、--param 绑定,以及模糊文本匹配/按 jaroWinklerSimilarity 排序——请参阅 references/querying.md。
关系类型
关系类型定义 rel_type,并且必须先创建关系类型,才能创建该类型的任何关系(这是一个配置操作——先预览,再提升)。--schema(可选)用于验证关系元数据;--source-record_types/--target-record_types(可选)用于限制其可连接的 record_types。
rekor relationship-types upsert <rel_type> --base <ws> [--description <text>] [--schema <json>] [--source-record_types <json>] [--target-record_types <json>]
rekor relationship-types list --base <ws>
rekor relationship-types get <rel_type> --base <ws>
rekor relationship-types delete <rel_type> --base <ws>
rekor relationship-types history <rel_type> --base <ws> [--limit <n>] [--offset <n>] [--diff]
删除关系类型也会删除该类型的所有关系。
Relationships
rekor relationships upsert --base <ws> --type <type> [--source <col/id> | --source-external <col/external_id> [--source-external-source <name>]] [--target <col/id> | --target-external <col/external_id> [--target-external-source <name>]] [--id <id>] [--external-id <key> [--external-source <name>]] [--data <json>]
rekor relationships get <id> --base <ws> [--rel-type <type> [--external-source <name>]]
rekor relationships delete <id> --base <ws> [--rel-type <type> [--external-source <name>]]
rekor relationships history <id> --base <ws> [--limit <n>] [--offset <n>] [--diff]
rekor query-relationships <record_type> <id> --base <ws> [--type <type>] [--direction outgoing|incoming|both] [--limit <n>] [--offset <n>]
每个端点必须且只能指定一个 --source/--source-external(目标端点同理)。外部地址解析的仅是处于活动状态的记录,且要求该记录存在;内部 ID 则按给定值存储。
关系也可以有自己的外部键:执行 upsert 时使用 --external-id 可确保创建操作具备幂等性(使用相同键重试 upsert 时,会更新首行而不是重复创建)。如果 --rel-type 限定了关系类型,get/delete 也可使用该键代替 ID。在 API 中,这些键对应顶层的 external_id/external_source 字段(批量 upsert_relationship 操作和生成的链接工具中也有这些字段),并可通过 GET/DELETE /relationships/<external_id>?rel_type=<type> 指定目标。
--type 必须是已声明的关系类型。--data 必须符合该类型的架构。
集成建模(规范模型优先)
以下集成边——入站 Webhook、触发器和外部数据源——用于连接 record_type 与外部系统。它们能帮助你决定使用哪种边,以及如何构建其背后的实体。本节与靠近文末的 面向 Agent 使用的建模原则 相辅相成:后者用于塑造面向 Agent 的工具界面;本节则说明数据如何作为支撑。
唯一原则:先以规范模型构建实体,使其与任何集成都无关;然后针对每项操作,决定如何为其提供数据支撑。 record_type 就是实体:使用业务命名属性、合理的枚举和默认值,构建清晰的架构——绝不能照搬上游载荷的结构。集成应体现在声明式边上,绝不能渗入架构或工具界面。使用者(Agent 或查询)不应能分辨某个字段是 rekor 原生字段,还是从旧系统镜像而来。
- 先建模规范结构。 根据领域和 Agent 的实际工作流程推导模式和工具表面,而不是根据任何后端的载荷推导。将每条业务规则放在最可靠且适合的层:模式(枚举、
required、默认值)> 触发器(反应式不变量)> 工具约束(writable_fields/precondition)> 提示词;绝不要将可强制执行的数据不变量编码为提示词规则。把握恰当粒度:只建模工作流程涉及的内容,并以增量方式扩展(推测性模式比起添加更难移除)。 - 进行替换测试。 如果为实体更换后端(旧系统 → 原生 → 其他系统)会迫使你修改其模式或工具的语义表面——工具名称、
data字段、筛选参数——就说明模型泄漏了集成细节;请修正模型。只有与集成无关的模型才可替换:在一个 Base 中,同一规范契约可以由原生方式提供服务;在另一个 Base 中,则可以通过只替换来源的field_mapping来代理到旧版 API。更换后端确实会改变封套的寻址参数(external_id/external_source),这些参数由来源接管——请参阅 MCP Factory 参考中的 外部寻址由来源拓扑发布。这是移至服务端的机制,不是泄漏;但如果提示词或 harness 在创建时传入external_id,就会与后端选择耦合,因此不要将其写入 Agent 的指令。对于旧系统缺少的目录条目,可在同一个 Base 中用原生 record_type 补齐,并通过同一工具集公开。 - 离线评估是额外收益——从另一侧验证替换测试。 仅预览的
--integrations disabled评估开关(见上文 Bases)之所以恰好可用,是因为模型与集成无关:替换测试是在不触及实体表面的情况下更换后端,而评估开关是在不触及实体表面的情况下取消后端绑定——禁用来源后,同一规范模式、工具和字段映射仍可提供服务,只是改为使用已播种的本地数据。因此,优先建立规范模型,就能免费获得确定性且适用于生产环境的 Agent 评估:这是一条原则,而非独立功能。 - 原生还是代理是逐个 record_type 的选择;来源的操作块声明上游支持哪些操作。 没有来源的 record_type 是原生的(所有 CRUD 操作都在本地执行);有来源的 record_type 由代理提供支持——所有写入都发往上游,读取则由上游提供(本地不存储任何内容)。来源的
list/get/create/update/delete块决定上游支持哪些操作(只读get/list,或完整读写);来源未声明的操作是不支持,而不是默认改为原生执行。不要在同一个 record_type 的不同操作之间拆分原生和代理;应在不同 record_type 之间混用:一个工具集可以自由组合由原生支持和由代理支持的工具(例如,代理支持的invoices,搭配你补齐的原生plans/activities)。 - 不要按工具或字段路由原生与代理。 原生还是代理是逐个 record_type 的选择,绝不是逐个工具的选择;
writable_fields只限制工具写入哪些字段,绝不决定写入何处。(同一操作上的两个代理工具可以选择来源的不同命名写入绑定——见 External Sources——但这只决定写入命中上游的哪个端点;依然全部由代理处理,绝不会让一个工具变为原生而另一个变为代理。)在一个 record_type 上混合“原生读取 + 代理写入”会造成读取一致性漏洞(在同步之前,原生读取不会反映代理写入)——而一旦添加这种同步,原生写入 +external_write触发器就明显更好。两种一致的形态是全部由代理处理,或全部原生 + 同步;按工具混用是前后不一致的中间形态。 - 优先使用原生镜像;只有迫不得已时才选择代理。 保持同步的原生 record_type 可提供本地查询/联接/筛选、速度、事务性写入、审计,以及与上游解耦的可用性。只有在无法接受数据陈旧、必须由上游记录系统同步确认写入,或数据集过大或过冷而不适合镜像时,才选择读取时代理。
- 混合实体(镜像字段 + rekor 拥有的覆盖字段)应放在同一个 record_type 中。 上游不存在的字段(工作流
status、标签、分数)应与镜像字段放在同一条记录中——集成查询/筛选需要这样做(如果status位于另一个 record_type,就无法执行list where status=X)。拆分实体以区分所有权,会牺牲真实的查询能力来换取记账便利;不要这样做。例外情况:覆盖部分本身是有更丰富生命周期/历史的实体 → 使用相关 record_type + 关系。标量属性绝不是拆分实体的理由。 field_mapping是一份双向契约,可在每个边界重复使用。 同一来源契约支撑代理读取、写入透传、入站 Webhook 水合(source_binding)和external_write输出。只定义一次转换;各个边界都重复使用它。- 参考型 Webhook:通过来源的
get进行水合,绝不要跟随载荷中的回调。 大多数成熟的 Webhook 都采用参考型模式(“记录 X 已更改,请获取该记录”)——载荷小、没有过时快照,且顺序可自我纠正。解析时提取 ID,并通过绑定的来源执行get来获取记录。载荷提供的回调 URL 可能导致 SSRF,因此应忽略。 模式目录 — 根据上游提供的能力和你的需求选择边:
| 模式 | 来源/边形态 | 读取 | 写入 | 适用场景 |
|---|---|---|---|---|
| 原生 | sources: [] | 本地 | 本地 | rekor 拥有数据 |
| 代理读取 | 来源 list/get | 实时读取上游 | — | 对新鲜度要求高,或参考数据太大而不适合镜像 |
| 直写 | 来源 create/update(读取时使用 get/list) | 实时读取上游 | → 上游(返回 id) | 上游是权威数据源;你需要同步确认,不需要本地镜像 |
| 推送同步镜像 | 接收并写入数据的入站 webhook + 绑定来源 get | 本地 | 本地 | 上游推送变更通知;你需要查询新鲜的本地镜像 |
| 双向镜像 | 入站 hydration(merge)+ 出站 external_write 触发器(watched_fields) | 本地 | 本地(→ 通过触发器写入上游) | 双向同步的混合实体,工具全部采用原生方式 |
双向镜像是混合实体的惯用模式:使用一个原生 record_type、全部采用原生工具,并将同步声明为边。使用 hydration merge,这样重新同步时只会刷新由映射管理的字段,同时保留你的叠加字段;使用 external_write watched_fields: "mapped",这样只更改叠加字段时不会向上游推送无操作请求。hydration 的写入目标必须是原生的(没有来源)——出站边是 external_write 触发器,而不是同一 record_type 上的代理写入;skip_inbound_webhook_writes 可避免重新同步时再次触发该触发器(防止循环)。
**标识键。**目录/参考实体(计划、活动、地点)→ 使用稳定的 slug 作为键,并将其用作 external_id,以便解析跨 record_type 的 x-fk 引用。人员/关系(成员、预订、注册)→ 使用 rekor 的内部 uuid 作为键。
入站 Webhook(数据流入)
外部系统通过入站 webhook 将数据推送到 Rekor。每个 webhook 都会提供一个唯一的接收 URL。
rekor inbound-webhooks create --base <ws> --name <name> --secret <hmac-secret> [--id <id>] [--record_type-scope <comma-separated>] [--field-mapping <json>] [--source-binding <json>] [--ingest-auth <json>]
rekor inbound-webhooks list --base <ws>
rekor inbound-webhooks get <id> --base <ws>
rekor inbound-webhooks delete <id> --base <ws>
--secret 是共享密钥(必填)。默认情况下,发送方会对接收请求进行签名,Rekor 会验证 HMAC:可以使用 Signing v1 — X-Data-Signature: v1,<hex>,对 id、timestamp、method、path 和 body 进行签名,并通过 X-Data-Timestamp 让 Rekor 检查请求是否足够新鲜(触发器和代理调用也使用相同方案,因此同一个签名器可用于两个方向)——也可以使用供旧版发送方使用的仅针对 body 的旧版 HMAC。v1 投递具有幂等性:重复投递(重试或携带相同 X-Data-Id 的重放请求)会重放首次响应,而不会再次写入。对于使用每个账户的静态请求头而非对每个请求单独签名来进行身份验证的发送方,--ingest-auth '{"type":"static_header","header":"X-Account-Key"}' 会将该请求头的值与 --secret 进行比对(以恒定时间方式);Rekor 比对的是配置的请求头,而不是签名。--record_type-scope 会限制入站 webhook 可写入的 record_types(省略则不作限制)。入站 webhook 只能在预览环境中创建或删除。准备就绪后,将其提升到生产环境。
写入前转换收到的负载。默认情况下,入站 webhook 会原样存储负载。若要直接从外部系统的原始结构存储规范化记录,可附加映射——使用与外部来源相同的 field_mapping 契约(重命名、值映射、日期格式转换、计算/组合),并应用于入站方向:
--source-binding '{"record_type":"<id>","source":"<name>"}'会重用现有来源的field_mapping,因此,为该 record_type 的读取/写入提供代理服务的同一转换也会规范化入站投递——两个方向使用同一契约。--field-mapping '{"to_external":{"status":"state"}}'是适用于没有来源的纯原生 record_type 的内联映射。to_external中的重命名会在读取时自动反转(上游state→ Rekorstatus);也可以显式提供to_record/computed。 两者互斥。创建 webhook 时以及晋升时都会验证此映射(source_binding必须仍能解析到具有field_mapping的 source)。这样就能完成无需执行器的 原生镜像同步:将入站 webhook 的to_record与external_write触发器的to_external配对,即可让原生 record_type 与纯 HTTP 上游实现双向同步,并复用同一份 source 契约。
数据补全型 webhook(通知 + 获取)。 许多系统发送的是引用型 webhook——“记录 X 已更改,这是它的 ID”——而不是完整记录(Stripe 建议按 ID 重新获取;Shopify、Salesforce、HubSpot 和旧版 CRM 也采用相同做法)。将此类 webhook 指向 record_type 的读取 source,Rekor 就会在每次投递时自行获取完整记录,然后存储规范化记录:
rekor inbound-webhooks create --base <ws> --name orders-ref --secret <token> \
--record_type-scope orders \
--source-binding '{"record_type":"orders","source":"shop_api"}' \
--ingest-auth '{"type":"static_header","header":"X-Account-Key"}' \
--hydration '{"id_path":"record_id","event_path":"event","event_map":{"ResourceDeleted":"delete"}}'
收到投递后,Rekor 会从精简的请求正文中读取记录 ID(id_path),调用绑定 source 的读取端点以获取完整记录,通过 source 的 field_mapping 将其规范化,然后按 external_id 执行 upsert——这是代理读取的入站对应操作。事件字段(event_path + event_map)会路由到 upsert 或 delete(delete 会跳过获取并移除镜像记录);其他情况默认执行 upsert。获取和写入会异步进行,并自动重试和退避,因此发送方会立即收到接受响应;使用 rekor inbound-webhooks deliveries --base <ws> 查看进度。此功能需要 --source-binding(用于提供读取端点)以及单个原生 --record_type-scope。安全性: Rekor 只会获取由 source 配置的 URL,并使用提取出的 ID;请求正文中的回调 URL 会被忽略,绝不会跟随访问。引用型发送方通常会搭配 --ingest-auth(静态标头)使用,因为它们不会对每次投递进行签名。
重新同步时保留你自己的字段——merge。 默认情况下,每次重新同步都会替换整个记录,因此你在本地添加的字段(工作流状态、标签、评分、分配信息、内部备注)都会被覆盖。在 --hydration 中添加 "merge":true 后,重新同步只会刷新 source 映射所管理的字段,并保留你添加到记录上的其他所有内容——这样,一个 record_type 就能同时保存上游镜像和本地补充信息,你仍可按自己的字段列出或筛选实体(例如 status=qualified)。首次同步仍会根据映射后的字段创建记录;上游之后清空的字段也会同步反映;合并后的记录仍会通过架构验证。示例:--hydration '{"id_path":"record_id","merge":true}'。
触发器(数据流出)
当记录或文件发生变化时,触发器会自动触发。触发器的操作类型包括:
- webhook — 向外部系统发送经 HMAC 签名的 HTTP POST(
--url/--secret)。 - internal_write — Rekor 会为你更新一个被引用的记录,无需外部服务。匹配的写入发生时,它会定位目标记录并应用受保护的补丁(可选的比较并设置
precondition,只有当目标仍处于预期状态时才会应用更新)。非常适合“当 A 发生变化时,更新相关的 B”这类场景——例如,预约时,仅当时段空闲时,才将其切换为忙碌状态。通过--action传入(见下文);仅适用于 record.created/updated。有两种模式:async会在触发写入后立即应用补丁(最终一致;如果precondition不匹配,会记录这一情况,但触发写入仍会生效);transactional会将补丁与触发写入原子地一同应用——如果precondition不匹配,则整个写入会被拒绝并回滚(不会创建任何内容)。如果两次写入必须同时成功或同时失败,请使用transactional(例如,只有时段空闲时才创建预约)。 - external_write — Rekor 复用外部源的写入路径,将发生变化的记录写出到外部系统——使用该源的字段转换、端点、请求体结构和身份验证,无需执行器。此操作适合将原生 record_type 镜像到旧版 API:在 Rekor 中原生存储记录,并按上游系统自身的数据结构将每次写入推送到上游。该操作会指定源:
{type:"external_write","record_type":"<source-owner>","source":"<name>","op":"create"}。record_type是定义该源的 record_type(原生镜像指向同级的、由源支持的 record_type;若省略,则默认为触发器的 record_type);op默认根据事件确定(created→create、updated→update、deleted→delete),也可覆盖。创建时,Rekor 会从响应中读取上游分配的 id,并将其关联回触发记录的external_id(使原生记录指向对应的上游记录,并避免重试时重复创建)——可通过link_external_id:false禁用此行为。只有已关联到该源的记录才会执行更新和删除操作。默认情况下,每次匹配的更新都会推送到上游;添加watched_fields,即可将 update 操作限定为实际更改了上游映射字段的写入——"mapped"表示精确监视该源映射到上游的字段(因此,只更改 Rekor 自有字段——即该源不会映射到外部的字段——会跳过冗余的无操作上游推送),也可以传入明确的数据字段名称数组。创建和删除始终会执行。投递为异步,采用签名验证、重试和死信处理,与 webhook 相同(使用rekor triggers deliveries检查);仅适用于 record.created/updated/deleted。
rekor triggers create --base <ws> --name <name> --events <comma-separated> --url <url> --secret <hmac-secret> [--id <id>] [--record_type-scope <comma-separated>] [--filter <json>]
# internal_write action (instead of --url/--secret):
rekor triggers create --base <ws> --name <name> --events record.created --record_type-scope appointments \
--action '{"type":"internal_write","target":"slots","match":{"source_field":"data.slot_id"},"patch":{"status":"busy"},"precondition":{"field":"data.status","op":"eq","value":"free"},"mode":"async"}'
# external_write action — mirror a native record_type's writes out through a source's write path:
rekor triggers create --base <ws> --name <name> --events record.created,record.updated --record_type-scope appointments \
--action '{"type":"external_write","record_type":"upstream_appointments","source":"legacy","op":"create"}'
rekor triggers list --base <ws>
rekor triggers get <id> --base <ws>
rekor triggers delete <id> --base <ws>
rekor triggers deliveries --base <ws> [--status <pending|delivered|failed|dead>] [--trigger-id <id>]
--events 是以逗号分隔的列表(不是 JSON 数组)。有效事件:record.created、record.updated、record.deleted、record.cancelled、relationship.created、relationship.updated、relationship.deleted、file.created、file.updated、file.deleted——例如:--events record.created,record.updated。文件事件会在文件写入时触发(创建/内容或元数据发生任何变化/删除);文件触发器的 --record_type-scope 用于指定文件类型,--filter 用于匹配文件的元数据(例如 data.status)。文件事件仅支持 webhook(internal_write/external_write 操作仅适用于记录)。(create/update 这类不带前缀的名称永远不会匹配,也不会提示。)对于 webhook,--secret 是接收方用于验证的 HMAC 签名密钥。--record_type-scope 用于将触发范围限制为指定的 record_type(省略则适用于全部)。只要触发器引用了 internal_write 的目标记录,该记录就会持续可用。对于 external_write,创建时会验证引用的源(指定的 record_type、源以及每种操作对应的写入端点都必须存在)——触发器引用某个源,绝不会将触发器的 record_type 变成代理;它仍是原生类型。
添加 --filter '<json>',仅在记录符合条件时触发——使用与查询相同的过滤 DSL,并针对正在写入的记录(或关系)进行评估,例如 --filter '{"field":"data.status","op":"eq","value":"paid"}' 仅在 status 为 paid 时触发。可使用 and/or 组组合条件。格式错误的过滤器会在创建时被拒绝。
触发器使用 HMAC 签名(对 id+timestamp+method+path+body 签名,格式为 X-Data-Signature: v1,<hex>;接收方通过 X-Data-Timestamp 检查请求的新鲜度——与代理请求使用相同的方案),并携带 X-Data-Id/X-Data-Delivery-Id 以便接收方去重。投递可靠——失败的尝试会通过退避重试,反复失败后进入死信队列;使用 rekor triggers deliveries 检查状态。默认情况下,入站 webhook 写入不会再次触发触发器(skip_inbound_webhook_writes: true)。触发器只能在预览环境中创建/删除。
执行器(对外部世界执行操作)
Rekor 负责记录、签名和分发——但实际的外部操作(调用第三方 API、提供客户端证书、运行 Rekor 无法执行的逻辑)由执行器完成:这是一个小型无状态 HTTP 服务,由你部署并指定给触发器或外部数据源。Rekor 会为每个分发的请求签名;执行器负责验证签名、执行操作,并将结果写回。
真的需要执行器吗? 对于普通的 REST/JSON API,只需使用外部数据源(无需执行器)——同一数据源的写入路径也可由 external_write 触发器复用于出站场景,因此,将原生 record_type 镜像到普通 HTTP 上游也无需执行器。只有在触发器需要运行自定义逻辑,或数据源调用无法通过直接 HTTP 请求完成(mTLS、SOAP、二进制凭据、繁重处理)时,才需要构建执行器。入站 webhook 的流程则相反——由你的执行器调用入站 webhook,将结果写回。
始终使用 @wayai/executor-sdk 包接收这些请求——切勿自行编写签名验证代码(一旦出错,任何人都可能伪造请求并发送给你的执行器)。该 SDK 会验证签名和时间戳,根据幂等键去重重试,并规范化错误——你只需编写一个处理器:
import { createExecutor, toFetchHandler } from '@wayai/executor-sdk'
const handler = toFetchHandler(createExecutor({
secret: process.env.REKOR_SIGNING_SECRET!,
handler: async (ctx) => { /* ctx.body is verified; do the work, return a result or nothing */ },
}))
// Hono: app.post('/rekor', (c) => handler(c.req.raw)) • Cloudflare: export default { fetch: handler }
运行位置:默认选择无服务器平台(Cloudflare Workers、Vercel、Deno Deploy、AWS Lambda——任选你已在使用的平台;该契约与平台无关)——大多数执行器只需调用经过身份验证的 API,而无服务器函数成本低、速度快,并且可以缩至零。只有当无服务器函数无法胜任时,才升级为长时间运行的容器或服务器(Fly.io、Railway、Render、VM):例如需要双向 TLS 客户端证书、长时间运行的任务、原始 TCP/SOAP 或原生依赖。
完整契约、框架变体、通过 vault 提供证书的模式以及本地开发说明,请参阅 references/executors.md(与此技能一同打包)。
外部源(通过外部 API 支持 record_type)
record_type 可以声明一个或多个外部源。当记录操作携带的 external_source 与已配置的源名称匹配时,Rekor 会将该读写操作代理到外部 API(或执行器),而不是使用自身存储。因此,Agent 可以通过相同的 rekor records/MCP 调用读取和写入由 stripe 支持的发票,无需单独编写集成代码。
在 record_type 架构中配置源(仅限预览,像任何架构变更一样晋级):
rekor record-types upsert invoices --base my-ws--preview --name "Invoices" \
--schema @schema.json --sources @sources.json
每个源都定义以下配置项(完整语法、每个选项的规则,以及一个使用旧版上游服务的完整示例,请参阅 references/external-sources.md):
name— 必须与记录的external_source相同。auth— 标头和值模板;密钥可内联填写,或使用vault:<name>。内联密钥仅限当前环境,晋级时绝不会复制,因此请直接在生产环境中设置(新晋级的源会保持未配置状态,在完成配置之前,请求会失败并返回明确错误)。vault:<name>引用会以引用形式传递。field_mapping— 可选(省略时按原样传递)。to_external/to_record在你的架构与上游字段名称之间进行映射——可以是简单重命名,也可以是包含transform、values(始终以 Rekor 为键)、default、date_format、array_mode、target的复杂规则。高级形式包括:将一个字段拆分为多个参数(targets)、解构复合值(separator+parts)、通过组合多个上游字段来生成 Rekor 字段(computed模板),以及按操作覆盖。get/list/create/update/delete— 各操作独立配置端点模板,每个模板都有自己的url(令牌{{external_id}}、{{query.*}}、{{data.*}}、{{current.*}}/{{prior.*}}、{{auth.*}})和method,因此 RPC 风格的动词路径和所有请求都使用 POST 的上游服务均可直接使用。response_path/total_path/success_path/message_path用于拆解响应封装。- 命名写入绑定(
create/update/delete)— 当一个规范实体的写入操作需要分发到多个后端端点时,将写入操作声明为由多个具名端点变体组成的映射;调用方通过名称选择其中一个(借助工具的bindings),仍只需一个 record_type。 id_path— 指定external_id在各原始上游记录中的位置(点分路径、复合模板或仅适用于创建操作/请求模板的变体),适用于主键不是id的上游服务。forward_filters(用于list)— 将 Agent 的eq过滤条件转发到上游(可允许指定fields,并选择query/body作为目标);也会以{{filter.<field>}}的形式提供给读取映射。local_filters(用于list)— 适用于不支持过滤参数的上游服务:声明集合范围有界,完整获取后由 Rekor 在内存中应用全部过滤条件和排序(支持除search以外的所有运算符;使用原生分页)。不能与forward_filters同时使用;max_rows可选(默认值为 500)。static_body/body_template/request_encoding— 用于塑造常量、模板化和表单编码的请求正文(例如,将 id 放入正文以兼容旧版动词)。injections/executor_secrets— 在请求的标头/正文中注入额外的按请求获取的保险库密钥,并提供具名凭据供执行器在分发时提取(例如,mTLS 证书)。cache_ttl+stale_if_error/signing/timeout_ms/breaker— 分别用于读取穿透式缓存、HMAC 签名(适用于执行器)、按请求超时以及按源配置的断路器。
URL 必须是绝对 https URL(或在设置 allow_insecure_http: true 时使用 http),令牌只能出现在路径/查询中(绝不能出现在主机名中),否则 SSRF 防护机制会拒绝该请求。
批量操作(原子性)
以原子方式执行最多 1,000 个操作 — 要么全部成功,要么全部失败:
rekor batch --base <ws> --operations '[
{"type":"upsert_record","record_type":"invoices","external_id":"inv-1","data":{"customer":"A","amount":100}},
{"type":"upsert_record","record_type":"customers","external_id":"cust-1","data":{"name":"A"}},
{"type":"upsert_relationship","rel_type":"belongs_to","source_record_type":"invoices","source_external_id":"inv-1","target_record_type":"customers","target_external_id":"cust-1"}
]'
操作类型:upsert_record、delete_record、upsert_relationship、delete_relationship、upsert_record_type、delete_record_type
关系操作通过内部 ID(source_id/target_id)或外部键(source_external_id + 可选的 source_external_source)指定每个端点 — 每个端点必须且只能使用其中一种。由于操作会在一个事务中按顺序运行,上面的示例会在一次原子调用中创建两条记录并将它们关联起来:关联操作会解析前面操作刚写入的 external_ids;如果解析失败,整个批量操作都会回滚。
读取结果:默认的(表格)输出会为每个操作打印一行摘要(索引、类型、生成的 ID)。添加 --output json 可获取每个操作的完整负载 — 每次写入的完整记录/关系。由于批量操作具有原子性,任何一个操作失败都会导致整个批量操作回滚,并使命令以非零状态退出,同时显示 Operation <N> failed: <reason>,指出出错的索引 — 不会写入任何部分结果。
批量导入
对于历史数据回填,导入是专门设计的路径 — 它的写入方式与普通 upsert 相同,但会跳过触发器级联(回填的行不会触发 Webhook,也不会触发内部/外部写入级联),因此除了 write:records 权限外,还需要专用的 write:imports 权限。
请使用 CLI。它会流式读取文件(因此大小不受内存限制)、分块处理,并为你处理下面所述的续传和月份边界情况:
rekor import run patients --file patients.ndjson --base prod # one JSON object per line
rekor import list [--limit <n>] [--offset <n>] --base prod # sessions, newest first
rekor import rollback <import_id> --base prod # undo (prompts only in a terminal)
run 会打印生成的 request_key。请妥善保存:如果运行中途失败,rekor import run … --request-key <key> --start-chunk <n> 会从该分块继续,而不是重新发送文件。请传入最初使用的同一个 --chunk-size — 分块编号由它决定,因此使用不同的大小续传会使索引 N 指向不同的行;使用 --start-chunk 时,CLI 会明确要求提供此标志。如果运行中途停止,它会告知你应从哪个分块索引继续 — 使用它打印的 --start-chunk/--chunk-size/--request-key 和所有其他标志保持不变,重新运行同一条命令。不要从头重新输入命令:过高的 --start-chunk 会静默跳过行;省略 --base 则会将续传导入完全不同的 base。(--org 不起作用 — 组织由该键决定。)--chunk-size 不得超过 1,000;--no-finalize 会让会话保持打开状态,以便之后续传。
如果你选择自行驱动它,而不是使用 CLI — 例如通过语言运行时或定时任务 — REST 接口如下:
POST /v1/{base_id}/import/begin {"request_key":"<your-run-id>","record_type":"<record_type>"}
POST /v1/{base_id}/import/chunk {"import_id":"<from begin>","chunk_index":0,"rows":[{"external_id":"…","data":{…}}, …]}
POST /v1/{base_id}/import/{import_id}/finalize
POST /v1/{base_id}/import/{import_id}/rollback — undo the import (deletes only rows it CREATED)
GET /v1/{base_id}/import/{import_id}/rollback-plan?counts=true — what an undo would reach, before doing it
GET /v1/{base_id}/import/sessions — list sessions (newest first) to find or recover a stuck run
契约要点:
begin按request_key具备幂等性(每次逻辑运行选择一个):响应丢失时可安全地重新发送,并会返回相同的会话。在会话保持打开期间,用同一个键指定不同的记录类型会返回 409——一次运行只能用一个键对应一种记录类型。会话完成后,该键即可再次使用(同一作业明天运行时可以复用该键,并会获得一个新会话)。如果begin返回 503,表示无法读取剩余额度,且未创建任何会话——使用相同的request_key重试。- 每个分块最多包含 1,000 行,按
chunk_index编号,并由服务端逐个记录回执:重新发送相同分块(索引和内容均相同)会重放已记录的结果,不会再次应用——因此超时或连接中断时可以安全重试(类似于精确重试种子租约,与其他所有数据写入不同)。重新发送索引相同但内容不同的分块会返回 409(IMPORT_CHUNK_HASH_MISMATCH);要从经过编辑的输入继续处理,必须创建新会话。 - 行按
external_id执行 upsert;如果行内容已匹配,则会跳过而不写入,因此重新运行完整刷新时只会重写实际发生变化的内容。有一种例外:如果匹配的行在约 45 天内没有被写入,则仍会重写(版本号递增,历史记录中增加一条更新记录),以确保定期重新导入的记录被计为活跃记录,而不是逐渐转入归档状态。因此,间隔超过该时长的刷新会重写每一行,rows_skipped不能作为长时间间隔下可靠的幂等性判据。 - 每项验证及归档/工作流门控都与常规写入完全相同——导入
immutable记录类型时只能创建。仅支持原生记录类型(由数据源支持的类型无法批量写入)。 - 会话空闲 24 小时后,其打开名额会被回收(在该基础上运行下一个
begin或finalize时才会发生,因此未触及的空闲会话可能会一直保留到那时);每个基础最多只能有少数几个打开的会话——完成后请调用finalize。会话也会在 UTC 月界线结束,因为其包含的行额度属于会话开始时所在的月份:在新月份发送的第一个分块会返回 409IMPORT_SESSION_CLOSED,且details.status: "expired"。若要恢复,请再次调用begin(相同的request_key会返回一个新会话),然后重新发送该分块——已经写入的行会被跳过,因此跨越月界线只需额外调用一次begin,而无需重新导入。details.status为closed表示该次运行是被主动完成的;不要自动重新启动。 撤销:rekor import rollback <import_id>(或 REST 路由)只会软删除此次导入创建的行;仅被覆盖的行会保留,并继续使用导入的值(不会还原到旧版本)。指向已删除行的链接也会一并删除,因此不会留下悬空链接。此操作会分批执行:每次调用都会返回swept和complete,因此请持续调用,直到complete为 true;CLI 会自动循环执行。在终端中,系统会先显示当前匹配数量并要求确认(--yes可跳过确认)。在其他任何环境中——脚本、CI 或 Agent(这些环境都不是 TTY)——系统不会提示,而是立即开始清理;这与其他破坏性命令的惯例一致。如果想先查看数量,可使用GET /v1/{base_id}/import/{import_id}/rollback-plan?counts=true自行检查。重试始终安全——已清理的批次会保持清理状态,因此调用因可重试的503(基础库繁忙)失败时,不会恢复已撤销的内容;只需再次调用。与导入操作本身一样,此操作不会触发任何触发器——删除记录和移除链接都不会发送通知,因此将删除操作同步到其他系统的触发器无法获知这些变更,另一个系统会继续保留这些记录。此操作需要write:imports、对应记录类型的write:records,以及write:relationships(清理操作可能会始终移除链接)。如果已撤销的记录附带文件,附件链接会被删除,但文件本身会保留——撤销操作只会移除导入创建的内容,而文件是在之后添加的;如果不需要该文件,请显式删除。还有两项限制值得注意:已移入归档存储的行无法触及,因此实际可撤销时限大致等于归档周期;对于immutable记录类型,重新导入也无法覆盖这些行,因此导入此类记录必须确保首次导入的数据正确。通常,使用修正后的数据重新导入是更好的修复方式:行会按external_id执行 upsert,因此重新运行时会原地覆盖。
你的套餐每月包含与记录上限相同数量的批量导入行;超出部分每 20 行计 1 op,每次请求向上取整,最低为 0.1 op(因此请分批发送合理数量的行——分块越小,费用不会更低)。只有实际写入的行会计费——跳过的(未更改)行和重放的分块均免费,因此重新运行只会按变更内容计费,唯一例外是上文提到的约 45 天归档重写:如果刷新间隔长于此时间,就会重写并对每一行计费。begin/finalize 免费,每次 rollback 调用计 1 op(因此撤销大型导入时每页计 1 op);rollback-plan 预检查在使用 ?counts=true 时计 1 op(会遍历导入的行并计数),不带该参数时计 0.1 op。交互式(非令牌)导入不计量,但套餐记录上限仍适用于导入的行。
种子夹具(隔离式评估数据)
夹具是一个命名且可逆的记录(及关系)集合,可应用于预览基础库,为 Agent 评估提供干净、可重复运行的起始状态。将其编写为配置即代码,并按名称运行——这样评估测试框架(包括使用作用域令牌的无头/云端框架)便可在每次运行前重置数据,避免基础库中的脏数据影响后续运行。
将夹具编写为文件,放在基础库的配置文件夹下,然后与基础库一起推送:
# rekor-ws/bases/<preview-base>/seeds/demo.yaml
id: demo
description: Overdue-bills demo baseline
records: # author FK targets BEFORE the records that reference them
- record_type: customers
external_id: cust-1
data: { name: Ada }
- record_type: bills
external_id: bill-1
data: { customer_id: cust-1, status: overdue, amount: 120 }
relationships: # endpoints reference records by external_id
- rel_type: owes
source: { record_type: customers, external_id: cust-1 }
target: { record_type: bills, external_id: bill-1 }
exclusive_record_types: [customers, bills] # optional: reset also removes non-declared records of these types
rekor push # store the fixture on the preview (pushes with the rest of the config)
rekor seed apply demo --base <preview> # write the fixture's records/relationships
rekor seed reset demo --base <preview> # restore the declared baseline (fixes records a run mutated)
rekor seed clear demo --base <preview> # delete every row the fixture owns (teardown)
rekor seed list --base <preview> # show the fixtures defined on the base
对于并发运行或结果未知的评估,请使用与操作者绑定的种子租约,而不要分别调用
reset 和 clear。生成一个新的 UUIDv7,并在释放租约前持久化保存此不可变元组;owner_ref 仅用于标记运行,不能授予任何权限。其长度必须为 1–200 个
字符,以字母或数字开头,其余字符只能使用字母、数字或 ._:@/+~=-
(不得包含空白字符)。
rekor seed lease acquire demo --base <preview> \
--lease-id <uuidv7> --owner-ref <safe-run-id>
rekor seed lease status --base <preview> --lease-id <uuidv7>
rekor seed lease release demo --base <preview> \
--lease-id <uuidv7> --owner-ref <safe-run-id>
Acquire 会以原子方式重置 fixture,并锁定整个预览环境中的 fixture 数据操作;release 会以原子方式清除由 fixture 拥有的数据并解除锁定。只有经过验证的同一用户或 Rekor token 才能重试、检查或释放该元组。完全相同的重试不会执行任何操作,已释放的 ID 永远不可复用,其他获授权的操作者只能得知该基础环境已锁定。如果 acquire 超时且结果未知,请重试同一元组。每个预览环境的永久账本最多容纳 1,000 个 ID(达到 800 和 950 个时开始警告);达到上限后,请更换一次性预览环境。
对应的 REST 接口为 POST /v1/{base_id}/seed-leases/acquire、POST /v1/{base_id}/seed-leases/release 和 GET /v1/{base_id}/seed-leases/status[?lease_id=…],均需要具备基础环境范围的组合 write:seeds 授权。首次 acquire/reset 和首次 release/clear 转换各消耗 1 次固定操作;status、冲突、完全相同的重试和重复 release 不消耗操作次数。
规则与保证:
- 按
external_id实现幂等。apply会按每条记录的external_id执行 upsert,因此重新应用会原位更新,绝不创建重复项——即使记录已存在,也不会出现重复记录。 reset会恢复基线。 它会原位重新应用已声明的记录(修复运行中被修改的记录,例如被触发器翻转的状态),并移除 fixture 不再声明的、由 fixture 拥有的行。在每次评估运行之间使用reset,确保干净的起始状态。exclusive_record_types让reset对整个记录类型具有权威性。 Agent 在运行期间创建的记录没有所有权标记,因此普通 reset 会将其保留,导致它们在多次运行中累积。列出评估所写入的记录类型后,reset还会移除这些类型中所有未由 fixture 声明的记录——无论由谁创建,包括其他 fixture 的行——以及指向已移除记录的任何关系。被 fixture 已声明关系引用的记录始终会保留,即使它不在records中。此功能需主动启用,且仅影响 reset(apply/clear不变);Agent 在两个保留的基线记录之间创建的关系仍会保留;若基础环境不存在所列记录类型,则 reset 时会静默跳过(promote 时会检查是否存在)。在共享预览环境中请谨慎选择排他类型——fixture 将成为这些类型的唯一事实来源。clear用于拆除。 它只会删除 fixture 拥有的行,不会删除其他内容——fixture 通过保留的所有权标记记录所有权(绝不使用external_source),因此拆除操作不会触及非种子数据。- 仅限预览环境。
apply/reset/clear只能在预览基础环境上运行(与架构更改一样);它们会拒绝生产环境。fixture 定义与基础环境的其他配置一样会一同 promote,但数据操作仅限预览环境。 - 不产生触发器副作用。 应用/重置 fixture 属于基线设置——不会触发触发器,因此恢复数据不会引发级联操作。
- 可通过 API 调用。 这三个动词都能通过 REST API 按 fixture 名称调用,因此持有一个针对单个预览基础环境、范围为
write:seeds的 token 的云端评估框架可以自行重置数据。 - 租约会全局锁定 fixture 数据操作。 只要有任何种子租约处于活动状态,针对所有 fixture 的普通
apply、reset和clear调用都会返回FIXTURE_TARGET_IN_USE;fixture 定义的配置 CRUD 仍可使用。
提供商适配器
将任意 LLM 提供商的工具定义导入为 record_types(rekor providers import <provider>),将 record_types 导出为工具定义(rekor providers export <provider>),或将提供商原生工具调用转换为记录(rekor providers import-call)。支持的提供商:openai、anthropic、google、mcp。命令格式和载荷结构见 references/providers.md。
MCP Factory(自定义工具集)
根据基础 record_types 创建专用 MCP 服务。每个工具集都提供特定领域的工具——Agent 看到的是 create_invoice、list_payments,而不是通用 Rekor 操作。
# 1. Author first-class Actions — one record_type + one operation each; the id is the tool name
rekor actions upsert get_invoice --base my-ws --record_type invoices --operation get
rekor actions upsert list_invoices --base my-ws --record_type invoices --operation list
rekor actions upsert create_payment --base my-ws --record_type payments --operation create
rekor actions upsert list_payments --base my-ws --record_type payments --operation list
# 2. Compose a curated toolset that references those Actions by id
rekor toolsets upsert invoicing-agent --base my-ws \
--name "Invoicing Agent" \
--action get_invoice --action list_invoices \
--action create_payment --action list_payments \
--relationship "invoice_payment:list" \
--batch "invoices:create,update" --batch "payments:create" --batch "invoice_payment:create" \
--sql-query
# Get the MCP connection URL (Streamable HTTP)
rekor toolsets url invoicing-agent
# → https://mcp.rekor.pro/t/invoicing-agent/mcp
# List all toolsets
rekor toolsets list --base my-ws
# Get toolset config (with resolved schemas)
rekor toolsets get invoicing-agent --base my-ws --resolved
# Delete a toolset
rekor toolsets delete invoicing-agent --base my-ws
Action 引用格式:--action <action_id>(或使用 <action_id>=<surface_name> 在此工具集中重命名)。请先使用 rekor actions upsert <id> --record_type X --operation Y 创建 Action——一个 record_type + 一个 operation,其 id 即为 Agent 侧使用的工具名称。Action 也可以是组合 Action——rekor actions upsert <id> --config '{ "steps": [...] }' 会将多个原生写入操作(record create/update/delete + relationship create/delete)打包为一个原子工具,并为每个步骤设置保护条件(参见 references/mcp-factory.md → Composite Actions)。
Relationship 规范格式:rel_type:op1,op2(操作:create、list、delete)
Batch 规范格式:record_type_or_rel:op1,op2
高级控制由工具集所引用的一等 Actions 提供。使用塑形配置创建 Action——rekor actions upsert <id> --record_type X --operation Y --config '{…}'(一个 record_type + 一个 op)——然后在工具集中引用它(使用 --action <id>,或在工具集 --config 中添加 actions: [{ action, action_name?, description_override? }] 条目)。每个 Action 的可调参数各占一行(完整语法和 JSON 示例见 references/mcp-factory.md):
- 工具名称 — Action 的
id是面向 Agent 的工具名称;工具集引用可以通过action_name按界面重命名,或通过description_override替换其描述。名称在整个工具集中必须唯一。(在工具集上内联声明的关系工具保留name/names映射。) filterable_fields— 将listAction 的选定字段公开为带类型的参数(原生参数,而非原始筛选条件)。每个字段可配置:param、match(exact/range/text/any_of/member/search)、enum、pattern、optional、description。声明的字段默认必填(这是工具要询问的内容)——设置optional: true可将其设为可选,而范围的两个边界始终可选。param是一个词干,并不总是最终参数名:text→<param>_contains,range→<param>_min/_max(日期使用_after/_before),search→<param>_search;exact/any_of/member则原样使用该值。match: search是唯一会调用排名式search运算符(以及字段的x-search调优设置)的匹配方式——参见references/mcp-factory.md。expose_*+default_*—listAction 的机制参数(filter/sort/limit/offset/fields)默认隐藏,因此工具只体现语义。设置expose_<param>: true可重新公开其中一个参数;default_*用于设置参数保持隐藏时应用的服务端值(隐藏的limit默认采用较大的页大小)。writable_fields—create/updateAction 写入端的对应配置:仅允许设置指定字段的白名单(遵循最小权限原则的意图工具),同时会根据 record_type 架构生成丰富的类型化data架构。base_filter— Filter DSL 谓词,由服务端以 AND 方式合并到listAction 的每次调用中,对 Agent 不可见。它能从宽泛的 record_type 中裁剪出范围狭窄的工具(list_active_practitioners= list Action + 内置的status = active),而不是公开一个模型必须知道如何填写的可选status参数。仅适用于list,且 record_type 必须是原生的,或由某个声明了local_filters的列表端点支持;它不是安全边界。对于date-time字段上的范围运算谓词,使用值"$now",即可让谓词保持实时有效(starts_at gt $now= 仅限未来),而非使用会过时的字面日期——这仅用于配置,绝不是筛选参数(参见references/mcp-factory.md)。precondition— 对create/updateAction 设置 Filter DSL 比较并设置条件,并根据记录的当前状态进行检查;若不匹配,则返回 409,且不会发生任何更改。对 Agent 不可见——它能将脆弱的先读后写操作变为一次无竞争条件的调用(例如,仅当时段仍为free时执行book_slot)。binding— 选择代理源的某个操作所对应的命名写入绑定,供写入 Action 分发使用(参见外部数据源),让一个规范的 record_type 写入可以分发到多个端点。
通过作用域严格限定为一个 base 的令牌,将 Agent 连接到工具集 URL。Agent 只能看到你配置的工具——完全符合特定领域,不涉及任何 Rekor 概念。为遵循最小权限原则,可通过一步操作创建绑定到工具集的令牌:rekor tokens create-for-toolset <slug> --base <db>。工具集绑定令牌的授权范围就是该工具集的工具界面——仅限这些 record_types 和操作、关系、批处理,以及仅在启用时提供的 SQL,除此之外别无其他——因此,即使令牌泄露,也无法访问你所公开工具之外的内容,并且你可以为每个 Agent 单独轮换或撤销令牌。交互式设置时,rekor toolsets upsert --mint-token 也会创建一个令牌,但其合并输出的工具集和令牌并非用于自动捕获凭据的流程。
对于自动化设置,请运行 rekor tokens create-for-toolset <slug> --base <db> --token-only;关于失败时默认拒绝的捕获方案和 JSON 响应格式,请参见下方的 API Tokens。
只能在预览基座中创建或修改工具集。准备就绪后再推广到生产环境;如果推广会破坏已发布的工具集(移除了其依赖的 record_type/关系类型,或其类型化筛选器/writable_fields/base_filter/precondition/bindings 所依赖的字段),则会被阻止——干运行会先列出冲突。mcp.rekor.pro/t/{slug}/mcp 会根据你的 令牌 所限定的基座解析工具集,因此,要连接已推广的工具集,请使用生产令牌;要在沙盒中测试尚未推广的工具集,请使用预览基座令牌(references/mcp-factory.md 介绍了解析规则)。
API 令牌
为 Agent、集成和 CI/CD 创建限定范围的令牌。令牌可限制为特定基座、记录类型、环境和权限。令牌在存储前会经过哈希处理——原始值仅在创建时显示一次。如果脚本需要密钥,请使用 --token-only;使用 --json 时,未封装的密钥字段是 .token,而不是 .data.token。
自动化场景中,优先使用 --token-only:它会验证令牌格式,并且只输出一次性密钥,不附带表格、JSON 或 MCP URL。捕获密钥时,不要打印它,也不要启用 shell 跟踪:
TOKEN="$(rekor tokens create-for-toolset <slug> --base <db> --token-only)"
case "$TOKEN" in rec_*) ;; *) echo "Token creation failed" >&2; exit 1 ;; esac
如果工作流必须使用 --json,Rekor CLI 响应已经解封装:提取顶层的 .token,绝不要使用 .data.token;如果值为 null,应失败关闭,而不是接受它:
TOKEN="$(rekor tokens create-for-toolset <slug> --base <db> --json \
| jq -er '.token | strings | select(startswith("rec_"))')"
使用 --ttl 创建探测、测试和诊断令牌,让它们自动过期——一次性令牌无需手动撤销或清理。使用 --description 记录令牌的用途(由哪个服务、凭据或流水线持有),这样将来撤销令牌时,你就知道会导致什么失效。
# Create a full-access token
rekor tokens create --name "my-key" --grants '[{"scope":{"bases":["*"]},"permissions":["*"]}]'
# Create a scoped token (read-only on one base)
rekor tokens create --name "client-a-reader" \
--grants '[{"scope":{"bases":["client-a"],"environments":["production"]},"permissions":["read:records","read:record_types"]}]'
# Capture the one-time secret for automation (validates rec_ and prints nothing else)
TOKEN="$(rekor tokens create --name "client-a-reader" --token-only \
--grants '[{"scope":{"bases":["client-a"]},"permissions":["read:records"]}]')"
# Disposable probe token — auto-expires, no cleanup needed (--ttl takes s/m/h/d)
rekor tokens create --name "probe" --ttl 10m \
--grants '[{"scope":{"bases":["*"]},"permissions":["*"]}]'
# Absolute expiry + a note about who holds the token
rekor tokens create --name "temp-key" \
--grants '[{"scope":{"bases":["*"]},"permissions":["*"]}]' \
--expires-at 2026-12-31T23:59:59Z \
--description "CI pipeline for repo X"
# List tokens (shows status, last_used_at, expires_at, description)
rekor tokens list
# Revoke a token (permanent)
rekor tokens revoke <token_id>
# Clean up stale tokens — selects by staleness ONLY (expired, or unused past the
# window), never by name. Previews first; revoking requires --yes.
rekor tokens prune # expired tokens only
rekor tokens prune --unused-since 30d # + tokens with no activity for 30 days
rekor tokens prune --unused-since 30d --yes
撤销防护措施:如果某个令牌在过去 7 天内使用过,或绑定到了工具集(即消费者令牌——MCP 连接可能持有该令牌),撤销操作会被拒绝并显示警告;传入 --force 可覆盖此限制。除非有人已确认没有任何依赖,否则不要使用 --force(也不要使用 --include-bound 清理)——撤销操作不可逆,并会立即中断实时流量。过期令牌可直接撤销,无需额外操作。
权限:read:records、write:records、read:record_types、write:record_types、read:relationships、write:relationships、read:relationship_types、write:relationship_types、read:attachments、write:attachments、read:files、write:files、read:file_types、write:file_types、read:inbound_webhooks、write:inbound_webhooks、read:triggers、write:triggers、read:toolsets、write:toolsets、read:actions、write:actions、read:seeds、write:seeds、read:bases、write:bases、read:audit(只读;授予变更历史访问权限,需要管理员授权,且不会由其他授权隐式授予)、write:imports(批量历史数据导入;与 write:records 分开,因为导入会提交数据但不会触发触发器,且不会由其他授权隐式授予),或使用 * 授予全部权限。
范围字段:bases(必填)、record_types(可选——省略则适用于全部)、environments(可选——production、preview,或省略以同时适用于两者)。
令牌会强制实施权限上限——你只能创建范围与自己相同或更窄的令牌。
密钥(保险库)
在组织级别存储凭据——API 密钥、签名密钥或凭据类文件(客户端证书、密钥库或服务账户 JSON)。密钥在静态存储时会加密,并且在响应中始终经过屏蔽。可在源配置/触发器配置中引用它们(例如执行器的签名密钥),也可以让执行器在运行时读取密钥。
# Store a string secret
rekor secrets create --name stripe-key --value sk_live_xxx --tags billing
# Store a credential file (base64-encoded) with its type, and an expiry — e.g. a client certificate
rekor secrets create --name partner-cert --file ./partner.p12 \
--content-type application/x-pkcs12 --expires-at 2027-01-01T00:00:00Z
rekor secrets list # List (values masked)
rekor secrets list --expiring [--days 30] # Only secrets expiring/expired within the window
rekor secrets get <id> # Metadata (value masked)
rekor secrets rotate <id> --value <new> [--expires-at <iso>] # Install a new value (never auto-generates)
rekor secrets delete <id>
对于有期限的凭据(如年度证书、限时令牌),请设置 --expires-at(ISO-8601),然后运行 rekor secrets list --expiring,即可查看需要续期的密钥,避免集成失效。到期时间仅作信息提示;Rekor 不会自动禁用密钥。
账户与诊断
# `rekor init` is retired — a rec_ key already carries its organization (see First-Time Setup)
rekor whoami # Show the authenticated identity
rekor status # Auth, connectivity, active org, worktree binding, CLI version
rekor update # Update the CLI to the latest published version
保持最新
确保 CLI 和此技能均为最新版本:
- 更新 CLI — 运行
rekor update(在 npm 上发布为rekor-cli;使用rekor --version验证)。 - 更新此技能 — 运行
mkdir -p .claude && npx skills add wayai-pro/rekor-skill -y,将最新发布的技能拉取到.claude/。
CLI 会在后台检查两者是否有更新的发布版本;若任一版本过时,会打印一行提示。运行上方对应的命令即可更新。使用 REKOR_NO_UPDATE_CHECK=1 可禁用这两项检查(设置了 NO_UPDATE_NOTIFIER 或 CI 时也会禁用)。
报告问题
向 Rekor 团队提交 Bug 或问题,并跟进直至修复。报告会去重:如果工作尚未完成时重复提交,会合并到现有报告中;原报告提交者重新提交已解决或未被最终驳回的人工报告时,会重新开启报告以便再次审查,除非其生命周期内的重新裁定次数已达上限。
如果出现更新提示(见保持最新),请先更新并重新检查,再提交报告——该 Bug 可能已在较新的 CLI 或技能版本中修复。
# File a report (deduplicated). --dedup-key collapses repeat occurrences of the same issue.
rekor report create --title "Upsert 500s on large docs" --description "..." \
[--severity high] [--steps "..."] [--error-message "..."] [--dedup-key "<stable-key>"] \
[--base-id <id>] [--record_type <name>] [--record-id <id>] # entity pointers aid investigation
# Track and verify your own reports
rekor report list [--status shipped] # your reports with titles, newest first (--status shipped = awaiting your check)
rekor report get <report_id> # title + description + status + the message thread
rekor report accept <report_id> # the shipped fix works → resolved
rekor report contest <report_id> --reason "still reproduces because ..." # the fix didn't work, or a dismissal is wrong → back to the team
缺陷验证循环。 ⚠️ 目前无法通过 CLI 使用。 report list/get/accept/contest 的作用范围限定于用户——它们会将调用者解析为报告提交者;而 API 密钥标识的是组织,而非个人,因此这四个命令都会被拒绝。提交报告(rekor report create)仍然可用。在面向用户的操作界面恢复之前,请通过账户管理员跟进已提交的报告。本段其余内容描述的是该循环的设计:
团队升级处理缺陷并发布修复后,你的报告会变为 shipped——你会收到一封电子邮件,且 rekor status 会提醒你一次(也可以使用 rekor report list --status shipped 按标题查找)。运行 rekor report get <id>,查看原始标题和描述、当前状态及团队备注;如果修复有效,就运行 accept,否则运行 contest --reason "..."。增强功能没有需要验证的缺陷修复,因此相关问题关闭时会直接变为 addressed。你也可以对认为属实的 dismissed 报告提出异议——报告会重新提交给团队处理。异议次数有限(有次数上限,且团队可以将驳回标记为最终结果);超过限制后,请联系支持团队。你只能列出、读取或处理由你本人提交的人工报告;由 Agent 提交的报告会保留在管理员分诊队列中。
输出格式
添加 --output json 可输出机器可读的 JSON 格式(默认值为 table)。
rekor records get invoices rec_abc --base my-ws --output json
数据输入
任何 --data、--schema、--tools 或 --operations 标志均接受:
- 内联 JSON:
'{"key":"value"}' - 文件引用:
@path/to/file.json
环境
规则位于 Rekor 对象模型中:配置只能在预览环境中写入,数据可在任何环境中写入,提升由人工执行。操作示例:
# 1. Create the preview
rekor bases create-preview my-base --name "add-invoices"
# 2. Make config writes against it
rekor record-types upsert invoices --base my-base--add-invoices --name "Invoices" \
--schema '{"type":"object","properties":{"amount":{"type":"number"}}}'
# 3. Ask the user to run the promotion (human-only — you cannot promote):
# rekor bases promote my-base --from my-base--add-invoices
如果 my-base 本身是一个预览环境(免费计划的配置方式——rekor bases create <id> --environment preview),第 1 步仍然可行——create-preview 也可以从预览环境克隆;但第 3 步不可行:提升操作受付费计划限制,而且从预览环境克隆出的预览环境会通过其源环境提升,而不是直接提升到生产环境。该克隆也会永久使用 local 分析层级(见 Bases → Analytics tier),因此除非传入 --analytics standard,否则无法用于 rekor sql、history 或遍历。请直接针对 my-base 执行配置写入,然后停止。
面向 Agent 使用的建模原则
上面的命令参考讲的是操作机制。以下说明如何组合这些机制,打造可靠的 Agent 驱动应用。操作机制几乎可以实现任意形态;这些原则则帮助你选出最适合让有缺陷的大模型 Agent 顺利完成任务的形态。它们与领域无关——无论你建模的是发票、预约还是工单,都适用。这些原则决定面向 Agent 的工具界面;有关如何支持实体(原生、代理、镜像)以及如何塑造集成边界,请参阅上文的 Integration Modeling (canonical-first)。
唯一的规则:要让正确的做法成为唯一容易表达的做法,并让错误的做法无法实现。 Agent 能否系统性地跳过步骤,还是能够顺畅地完成整个生命周期,通常取决于架构和工具的形态,而不是提示词或模型。
- 一个意图 = 一次原子写入。 一个需要 N 次独立写入的操作,就有 N 次机会漏掉其中一步,导致系统处于只更新了一部分的状态。将单个用户意图合并为一次写入;当一个意图确实涉及多条记录时,使用
rekor batch,确保它们全部提交或全部回滚。 - 状态保存在实体上——不要重复。 每项状态都应且仅应保存在一处,也就是它所描述的实体上。将状态镜像到第二条记录,会迫使 Agent 同步更新两处,从而重新引入多次写入的问题。应链接或查询事实来源,而不是复制其字段。
- 只暴露任务本身。 面向 Agent 的工具应只表达任务本身。生成的工具已经隐藏了底层机制(排序、限制、偏移量、字段选择、原始筛选器 DSL)——关于这对你编写 Actions 有何影响,请参阅下文的 Tool design principles。Agent 通过读取时的类型化
filterable_fields和写入时的writable_fields填入含义(工具只能看到其任务所设置的字段),而不是底层管道配置。 - 约束应放在配置中,而不是参数中。 在服务端强制执行不变量,让 Agent 无法忘记,而不是将其作为必须遵守的指令。
precondition可让依赖状态的写入无竞争,并且对 Agent 不可见;Schema 的required和枚举则会拒绝格式错误的输入。拒绝消息是错误恢复的途径——应确保其清晰易懂且可执行,让 Agent 能自行纠正。 - 缩小输入范围。 每一个自由度都可能导致出错。使用枚举(从封闭集合中选择)、类型化
filterable_fields(使用原生参数,而不是自由格式的筛选字符串)、稳定的external_id键(幂等 Upsert,不会凭空编造 ID),以及写入时的x-fk(引用必须解析到真实记录),以此限制输入。 - 针对有缺陷的 Agent 建模,而不是理想的 Agent。 Agent 会跳过步骤、为未填写的字段发送空字符串,还会编造 ID。
required会拒绝缺失字段,枚举会拒绝伪造的值,x-fk会拒绝编造的引用,precondition会拒绝顺序错误的写入——每一项都能将悄然发生的数据损坏转化为清晰且可纠正的错误。 - 将运维界面与 Agent 界面分开。 将编写配置的界面与调用配置的界面分开。供人员/CI 使用的广泛权限令牌负责设计 Schema 和工具集;绑定到工具集的令牌(
rekor tokens create-for-toolset)只能调用经过筛选的工具,无法访问其范围之外的内容。按操作和数量来命名工具(book_slot、list_invoices、assign_payment),让名称本身就能告诉 Agent 工具的用途。
此外: 保持数据规范且自描述——将日期时间字段声明为 format: date-time,并设置 record_type 的 x-timezone,确保每个值都带有时区偏移;优先使用易读的枚举,而不是不透明的代码,这样无需外部上下文也能理解记录。还要有意识地优化常见路径——将最常见的操作设计为一次命名清晰的工具调用,即使这会增加罕见操作的复杂度。
工具设计原则
如何塑造 Agent 实际调用的工具。Rekor 的默认设置已经实现了这些原则——失效模式是把工具重新扩展成宽泛的形式,因此下面每条规则都在说明不要添加什么。宽松接口的代价由将来持有令牌的最弱模型承担:可选参数是它可能会填入虚构占位值的参数(null、none、sem_filtro、.*),这些值会被编译成一个真实筛选条件,但匹配不到任何内容;系统会显示“无结果”,而不是报错,进而触发重试循环。
- 工具代表意图,而非端点。 根据任务命名(
find_patient_by_phone、book_appointment),并让它只回答一个问题。查询同一信息的两种方式 = 两个 Action,而不是一个带有两个可选筛选条件的工具。 - 隐藏内部机制。 默认不提供
filter、sort、limit、offset、fields;工具的 inputSchema 只包含语义。只有在 Agent 确实需要控制这些参数时,才用expose_<param>: true重新开放其中一个,并改用default_limit/default_fields,让服务端做决定。 - 已声明的参数均为必填;可选与否需要有充分理由。
filterable_fields条目就是工具要回答的问题,因此默认必填。只有确实需要缩小范围的筛选条件,才添加optional: true——如果一个工具累积了多个这样的参数,就说明应按意图拆分它。(范围字段始终为可选:它们的两个边界值不能都设为必填。) - 每个离散字符串参数都要有封闭的取值范围。 使用
enum(从一组值中选择——最好在 record_type schema 上声明,这样写入时也会验证)或pattern(一种有效格式,例如^\+[1-9][0-9]{6,14}$)。无约束的可选字符串筛选条件最容易出现虚构值。 - 不应由 Agent 决定的内容应由服务端注入。
base_filter限定读取可见的内容;precondition在不可见的情况下守护依赖状态的写入;writable_fields限定写入可触及的字段;external_source/bindings负责路由。这些都不是面向 Agent 的参数。 - 拒绝请求时应指明下一步操作。 错误消息是恢复渠道(MCP 客户端会丢弃
error.details),因此必须指出参数及修正方法。这就是为什么错误值必须明确报错,而不能被编译成筛选条件并返回零行。
反向规则: 按意图拆分,绝不按组合数量拆分。三个命名清晰的工具胜过一个有五个参数的工具;十五个几乎相同的变体则比两者都糟,因为工具选择也会因此退化。
配方:原则 → 实现该原则的功能
上述原则适用于 Agent 层,且与后端无关;以下是实现这些原则的具体工具集配置项——从意图到实现的映射。每个配置项都在上面的工具集部分中详细说明。
- 一个工具 = 一个意图;精心设计工具集表面 — 为每项操作指定不同的
names(names: { "list": "search_invoices" }),即使两个工具针对同一个 record_type 也如此;使用精心筛选的filterable_fields列表。 - 机制(filter / sort / limit / offset / fields) — 默认隐藏;设置
expose_<param>: true可重新启用其中一项,设置default_*可指定在隐藏期间应用的服务端值。 - 将筛选条件设为可选 —
filterable_fields[].optional: true(声明的字段默认必填;range字段的边界值本身就是可选的,而且设置optional: false会被拒绝)。 - 约束一组固定值 — 使用
filterable_fields[].enum,或在 record_type 架构字段上设置enum(优先采用后者——写入时也会根据它进行验证,而且筛选参数会从中派生)。 - 约束结构化 ID 或代码 — 使用
filterable_fields[].pattern(例如^pat-[0-9]+$);该规则会发布到工具架构中,并在运行时强制执行。 - 要求必须提供值 — 自动处理:除非标记为
optional: true,否则声明的filterable_fields条目均为必填(range字段的两个边界值始终可选);必填参数会拒绝空白或空选择输入(模型无法用""或[]假装满足要求)。 - 明确报错并提供可操作信息,绝不静默失败 — 在写入字段上使用
x-fk;读取参数上的空值、enum和pattern校验失败也会被拒绝(读写一致)。 - 将工具限定在某个子集内,但不向 Agent 展示筛选条件 — 在
listAction 上使用base_filter(不可见,始终应用)。 - 检查工具集表面是否符合这些原则 —
rekor push会输出工具设计警告(每个筛选条件都必填、单个工具上有多个可选筛选条件、未加约束的字符串筛选条件,以及与类型化参数并存且未关闭的原始filter逃生口)。仅提供建议,不会阻止推送。 - 让依赖状态的写入操作具备原子性并避免竞态 — 使用带有
precondition的create/update工具(比较并设置;对 Agent 不可见;发生冲突时返回 409)。 - 引导 Agent 选择正确的值 — 使用
filterable_fields[].description:说明字段类型、来源以及使用时机;使用占位符格式,切勿使用真实数据值。
一个容易忽略的问题: enum 会引导 Agent 选择 一个值——对于应始终填写的必填参数,这是合适的;但对于通常应跳过的 optional: true 参数,则不合适(固定选项会促使 Agent 选择,而不是省略)。对于可选筛选条件,优先使用 pattern(或普通自由输入参数);更好的做法是,如果该字段适合使用枚举,就先确认它是否真的是该工具的意图,是否应该设为必填。
最佳实践
- 使用外部 ID 进行幂等 upsert,确保重试安全且不会产生重复项。
- 每个上下文使用一个基础,以隔离数据(按测试运行、Agent 或环境隔离)。
- 架构变更先预览,在预览基础中修改 record_types、触发器和入站 Webhook。请人工审核并批准。
- 使用批处理确保原子性,适用于多个写入必须同时成功或失败的情况。
- 优先使用关系,而非嵌套数据,通过关联记录而非嵌入数据,支持遍历和灵活查询。
- 大体积或二进制内容使用文件,记录数据和关系元数据用于结构化 JSON,且上限约为 1 MiB;内联 Base64 数据块会被拒绝。将 PDF、图片及其他二进制内容存储为 文件(
rekor files put <file_type> <path> --file <local>)并关联到记录,或存储为简单的记录级附件(rekor attachments upload …)。 - 先定义架构,再写入记录。验证可及早发现错误数据。
- 写入后查询可能有延迟,单记录读取(
records get、relationships get)会立即反映写入;sql、records query、query-relationships和search可能需要片刻才能同步。如果需要在写入后立即查询,请稍等片刻或使用直接读取。 - 设置令牌过期时间,对短期令牌使用
--expires-at。及时撤销未使用的令牌。 - 立即保存令牌,原始令牌仅在创建时显示一次。关闭终端前,请将其安全存储。