news 2026/9/12 17:52:18

如何调用 JumpServer Runtime Store API 提交 Kael journal 记录并处理 revision 冲突?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何调用 JumpServer Runtime Store API 提交 Kael journal 记录并处理 revision 冲突?

如何调用 JumpServer Runtime Store API 提交 Kael journal 记录并处理 revision 冲突?

【免费下载链接】jumpserverJumpServer is an open-source Privileged Access Management (PAM) platform that provides DevOps and IT teams with on-demand and secure access to SSH, RDP, Kubernetes, Database and RemoteApp endpoints through a web browser.项目地址: https://gitcode.com/GitHub_Trending/ju/jumpserver

当你在为 JumpServer 的 Chat AI 运行时(Go 服务 Kael)做二次开发或联调时,需要把 Kael 产生的对话状态落到 JumpServer Core 的持久化存储里。这一步走的是 Core 的内部 Runtime Store API:Kael 通过POST /api/v1/chat-ai/runtime-store/追加一条 journal record,Core 在事务内做 revision 比对(CAS)并落库;当多个调用方并发写入、或你上一次写入因为网络抖动没有确认结果时,就会收到 revision 冲突,需要按返回的current_revision重算后重提。本文给出从准备凭据、构造 record 与签名、提交写入,到正确处理 409 冲突的完整路径。适用前提是:你已经有一个绑定到type=kael的 Terminal 的服务账号,并且能拿到该服务账号的 Access Key;接口只在 Core 的/api/v1/chat-ai/路由下可访问,不单独暴露端口。

一、前置条件:服务账号与 Access Key

Runtime Store 是"内部接口",认证和授权都有硬性约束,先核对清楚再动手:

  • 认证方式为Access Key HTTP SignatureSignatureAuthentication)。请求需携带Signature形式的Authorization头,包含keyidalgorithmsignature三个字段,且签名至少覆盖(request-target)date两个头。keyid对应你的 Access Key。
  • 授权上要求认证用户必须是服务账号,并且该账号绑定的 Terminal 类型必须是type=kael。普通用户、其他组件的服务账号、以及用户委托票据(X-JMS-AI-Delegation)都不能访问这个接口。
  • 接口实现位于 RuntimeStoreView,路由定义在 api/urls.py。

确认要点:你的 Access Key 属于一个服务账号、该账号挂了kael类型的 Terminal、Key 处于有效状态(过期或被 IP 组限制会直接鉴权失败)。这一步不满足时,后续任何请求都会在鉴权层失败,与本文后续的 record/签名无关。

二、先读一次:拿到当前 revision 起点

写入前需要先知道 journal 当前的 revision。读接口:

GET /api/v1/chat-ai/runtime-store/?nonce=<uuid>&after=0&limit=1000
  • nonce:本次请求生成的 UUID,必填。
  • after:默认0,表示调用方已持久化到的 revision;limit:默认1000,范围 1 到 1000。
  • after早于最新 snapshot,响应会从该 snapshot 开始。
  • 单页 record 总量达到 64 MiB 时,即使未达limit也会截断并返回has_more=true

响应结构(数值为文档示例,仅作格式参考):

{ "nonce": "<uuid>", "revision": 12, "results": [ { "revision": 12, "commit_id": "<uuid>", "snapshot": false, "record": "<jsonl-record>" } ], "has_more": false, "receipt": "<hmac-sha256>" }

revision字段即当前 head。把它作为下一次写入的expected_revision起点。响应带Cache-Control: no-storehas_more=true时用本页最后一条 record 的revision继续翻页。receipt用当前请求 Access Key 的 secret 对 canonical 内容做 HMAC-SHA256,调用方应校验它,以确认响应未被篡改。

三、构造写入请求:record 结构与 integrity 签名

写入请求体只有五个字段,多一个都会被拒绝(Unknown field(s): ...):

{ "commit_id": "<uuid>", "expected_revision": 11, "snapshot": false, "record": "<jsonl-record>", "integrity": "<hmac-sha256>" }

各字段含义与约束:

  • commit_id:本次提交的 UUID,用于幂等(见第四节)。
  • expected_revision:你预期当前 head 的值(非负整数),是 CAS 的比对基准。
  • snapshot:布尔。写入 snapshot 时,Core 会在同一事务中保存新 snapshot,并删除它之前的 journal records。
  • record必须恰好一行的 JSON 字符串,且只能包含versioncreated_atpayloadchecksum四个字段。当前version1payload是 Base64 字符串,checksum是 payload 解码后字节的 SHA-256。Core 会把record当不透明字符串保存,但会验证 JSONL envelope、payload checksum、提交完整性和 revision。单条 record 上限 64 MiB。
  • integrity:用当前请求 Access Key 的 secret 对如下 canonical 内容(各部分以换行拼接、无末尾换行)执行 HMAC-SHA256:
kael-runtime-store-commit-v1 default <commit_id> <expected_revision> <0|1 snapshot> <sha256(record)>

其中<0|1 snapshot>是 snapshot 的位表示(false0true1),<sha256(record)>record字符串的 SHA-256。下面这段 Python 仅演示"如何按文档规则算出payloadchecksum以及integrity",其中的 UUID、时间戳、payload 都是占位值,需要你替换成真实数据:

import base64, hashlib, hmac, json secret = b"<你的 Access Key secret>" # 替换:请求所用 Access Key 的 secret commit_id = "<uuid>" # 替换:本次提交的 UUID expected_revision = 11 # 替换:预期的当前 head snapshot = False payload_bytes = b"<你的 payload 原始字节>" # 替换:要持久化的业务数据 payload_b64 = base64.b64encode(payload_bytes).decode() record = json.dumps({ "version": 1, "created_at": "<ISO8601 时间字符串>", # 替换:如 2026-09-11T20:00:00Z "payload": payload_b64, "checksum": hashlib.sha256(payload_bytes).hexdigest(), }, separators=(',', ':'), ensure_ascii=False) def sign(parts): msg = "\n".join(str(p) for p in parts).encode("utf-8") return hmac.new(secret, msg, hashlib.sha256).hexdigest() record_hash = hashlib.sha256(record.encode("utf-8")).hexdigest() integrity = sign([ "kael-runtime-store-commit-v1", "default", commit_id, expected_revision, "1" if snapshot else "0", record_hash, ])

注意:record内层只允许那四个字段,外层请求体也严格只允许那五个字段,任何多余键都会触发校验失败。

四、提交写入并处理 revision 冲突

提交:

POST /api/v1/chat-ai/runtime-store/

Core 在事务内锁定 revision 并执行 CAS,两种结果:

成功(HTTP 201),返回:

{ "revision": 12, "commit_id": "<uuid>", "receipt": "<hmac-sha256>" }

成功receipt的 canonical 内容同样用 Access Key secret 做 HMAC-SHA256:

kael-runtime-store-receipt-v1 default <commit_id> <expected_revision> <revision> <0|1 snapshot> <sha256(record)>

调用方应校验该 receipt。

revision 冲突(HTTP 409),返回:

{ "code": "runtime_store_revision_conflict", "detail": "The runtime store revision has changed.", "current_revision": 13 }

冲突意味着在你读到expected_revision之后、真正落库之前,head 已经被别的写入推进了。处理路径:

  1. 读取返回体里的current_revision,这是服务端给出的最新 head,不要自己猜。
  2. 基于这个新 head 重新计算你的业务状态与 record(Kael 负责对话状态的恢复、压缩和演进,Core 不解析 payload 语义),得到新的record与对应的sha256(record)
  3. 用新的expected_revision = current_revision、并重新生成commit_idintegrity后重新POST

关于幂等重试(这是冲突处理里最容易踩坑的一点):commit_id支持网络失败后的重试,但只有当 revision、snapshot、record 均相同,且该提交仍是当前 head 时,才会返回与首次相同的结果。也就是说,"网络超时但实际已写入"的场景,用同一个commit_id重发能拿回原结果;一旦你在冲突后换成了新的 record 或 revision,就必须用新的commit_id。判断是否可以直接重试的关键,就是这三项(revision/snapshot/record)是否与上次完全一致。

五、边界与限制

以下限制直接影响调用能否成功,联调前确认一遍:

  • 反向代理的请求体限制必须高于 64 MiB,以容纳 record 外层 JSON 和字符串转义开销;否则会先被代理拦下,到不了 Core。
  • Runtime Store API 只能在 Core 的/api/v1/chat-ai/路由下访问,不应单独暴露服务端口。
  • Core 只把record作为不透明字符串保存,不解析 Kael payload 的业务语义;恢复、压缩和对话状态演进由 Kael 负责。
  • Access Key 轮换不会重写历史 record;读取和写入的 receipt 始终使用当前请求的 Access Key。
  • 落库表为chat_ai_runtime_store(head)和chat_ai_runtime_store_record(record),见 models.py。

完成一次成功的POST后,用GET重新读取确认新 revision 与 record 已可见,即视为这条 journal 记录已正确落入 Runtime Store;遇到 409 时按第四节的current_revision重算-重提流程收敛。更多接口细节可参考 Chat AI:Kael 与 JumpServer Core 桥接。

【免费下载链接】jumpserverJumpServer is an open-source Privileged Access Management (PAM) platform that provides DevOps and IT teams with on-demand and secure access to SSH, RDP, Kubernetes, Database and RemoteApp endpoints through a web browser.项目地址: https://gitcode.com/GitHub_Trending/ju/jumpserver

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 17:49:18

PyTorch猫狗公鸡图像分类实战:从数据管道到模型部署

简介&#xff1a;面向有一定深度学习基础、希望上手PyTorch与CNN图像分类的初学者&#xff0c;这份猫狗公鸡三分类实战资源&#xff0c;完整覆盖图像数据预处理、CNN网络搭建、损失函数与优化器选择、训练验证、模型保存加载以及结果可视化等关键步骤&#xff0c;帮助读者建立从…

作者头像 李华
网站建设 2026/9/12 17:45:55

三步抓到第一包:用 ProxyPin 跑通跨平台网络调试

三步抓到第一包&#xff1a;用 ProxyPin 跑通跨平台网络调试 【免费下载链接】network_proxy_flutter Open source free capture HTTP(S) traffic software ProxyPin, supporting full platform systems 项目地址: https://gitcode.com/GitHub_Trending/ne/network_proxy_flu…

作者头像 李华
网站建设 2026/9/12 17:43:30

风光互补制氢合成氨系统设计与Python优化实践

1. 项目背景与核心价值风光互补制氢合成氨系统是当前新能源领域的前沿研究方向之一。这个项目标题中提到的"并/离网"系统设计&#xff0c;实际上解决了一个行业痛点&#xff1a;如何平衡可再生能源发电的间歇性与工业生产的连续性需求。我在参与某风电制氢项目时&…

作者头像 李华