TencentDB Agent Memory 非回环绑定:文档要求配 key,代码只 warn
翻 TencentDB Agent Memory 这个仓库时,最容易被绊一下的不是路由表,而是一个词:required。
README 的环境变量表里,TDAI_GATEWAY_API_KEY 那一行的说明写着 HTTP Bearer authentication; required for non-loopback binding。中文读过去就是「绑非回环地址时这个 key 是必填的」。但我们把 MemoryCore/ 下的 Gateway 源码顺着读下来,在这条路径上只找到一条 logger.warn——没有找到「未设 key 且绑了非回环地址就拒绝启动」的实现。
先把限定条件说在前面:本文对应的是 TencentCloud/TencentDB-Agent-Memory 在 2026-08-16 的快照,分支是 feat/server_team(这个仓库的默认分支就是它,不是 main 也不是 master,贴路径时容易写错)。MemoryCore/package.json 里的 version 是 2.0.0-beta.1,仍处于 beta 阶段,配置项与接口随版本变动,下面每一处行号都只对这一份快照负责。
差异到底在哪两处
按写「不一致」的纪律,只陈述位置与原文,不推断原因、不评价。
A 处(文档层):MemoryCore/README.md:204,环境变量表原文 TDAI_GATEWAY_API_KEY | Unset | HTTP Bearer authentication; required for non-loopback binding。默认值一栏写的是 Unset。
B 处(代码层):MemoryCore/src/gateway/server.ts:739-746。当 !loopback && !authOn 时,走的是一条 logger.warn,文案末尾写着「Bind to 127.0.0.1, or set TDAI_GATEWAY_API_KEY, before continuing.」。
与之配套的是同一个文件里的鉴权函数 verifyAuth(server.ts:1116-1118):取 this.config.server.apiKey,若该值未配置就直接 return "ok",旁边的注释原文写的是 auth disabled — default behaviour。我们在 server.ts 里找到的唯一一处 process.exit 在 :3040,属于优雅退出路径。
再补一条同一快照里的事实:MemoryCore/Dockerfile:146 的 ENV 段里默认写着 TDAI_GATEWAY_HOST=0.0.0.0。也就是说镜像本身给出的绑定地址默认值,与「非回环」这个前提是对得上的,而镜像里没有强制 API Key 的动作。
两处都摆出来了,说完就停。
同一件事在四层配置里的口径
这类问题真正麻烦的地方,是「apiKey 该不该填」这件事在这个仓库里散落在四层,每层写法都不一样。把它们并排看一遍,才知道自己手上那份配置属于哪层:
| 位置 | 关于 apiKey 的写法 |
|---|---|
README.md:204(文档) | 默认 Unset,说明写 required for non-loopback binding |
tdai-gateway.yaml:29(默认配置模板) | server.apiKey 整行被注释掉,注文写「不填则所有 v2 接口默认开放(仅本机)」 |
tdai-gateway.standalone.yaml(单机配置模板) | 这个键完全不出现 |
openclaw.plugin.json:28-36(插件配置 schema) | server.apiKey 有默认值,写的是 local |
server.ts:1116-1118(代码) | 未配置 → verifyAuth 直接返回 "ok" |
注意最后两行不是一回事:openclaw.plugin.json 是这个模块的插件配置 schema,它的 server 段除 apiKey 默认 local 外,还有 url 默认 http://127.0.0.1:8420、instanceId 默认 default(:28-36);而 server.ts 里 verifyAuth 读的是 this.config.server.apiKey,走的是 Gateway 进程自己那份配置。同一个字段名出现在两份文件里,看配置前先分清自己在改哪一份。
怎么确认自己撞的是这一条
我们没有部署、也没有运行过这个项目的任何一个模块,所以下面全是在仓库副本里可执行的核对动作,不是运行验证。
- 先确认分支。
git branch --show-current看是不是feat/server_team。分支不同,下面的行号就别照抄了。 - 核 A 处。打开
MemoryCore/README.md,找到环境变量那张表里TDAI_GATEWAY_API_KEY那一行,看说明列是否仍写着required for non-loopback binding。 - 核 B 处。打开
MemoryCore/src/gateway/server.ts,在文件里检索TDAI_GATEWAY_API_KEY与non-loopback,看命中的是logger.warn还是别的分支;再检索process.exit,看这个文件里一共有几处、分别在什么位置。 - 核鉴权函数。在同一文件里定位
verifyAuth,看第一段是不是「expected为空则return "ok"」。这一步决定了「没配 key 时请求会怎样」这个语义。 - 核你实际用的那份配置。如果用的是
tdai-gateway.yaml,看server.apiKey那行是不是仍被注释;如果用的是tdai-gateway.standalone.yaml,确认这份文件里根本没有这个键。 - 核容器侧。如果走的是
Dockerfile,看:144-148的ENV段里TDAI_GATEWAY_HOST是什么值、TDAI_GATEWAY_CONFIG指向哪个路径;同一文件:150有EXPOSE 8420。
这六步走完,你手上会有一份「文档怎么写、配置怎么写、代码怎么写」的三方对照,而不是一句「好像不生效」。
源码语义给出的处置,以及处置后怎么核
README 自己给出的安全建议在 README.md:251,原文口径是绑非回环地址时务必配置 TDAI_GATEWAY_API_KEY。这是仓库文档自己的说法,不是我们的建议。
配置层面能核到的落点有两个:环境变量 TDAI_GATEWAY_API_KEY(README 那张表里登记的就是这个名字),以及 tdai-gateway.yaml:29 那行被注释掉的 server.apiKey。改完之后能做的核对,同样是读文件比对:配置里 server.apiKey 是否有非空值、绑定地址是不是你以为的那个。
这里还有一条容器侧的细节值得抄下来。Dockerfile:154-155 有一段注释,解释为什么 TDAI_GATEWAY_PORT 被刻意不写进 ENV:一旦它成为镜像环境变量,就会盖掉挂载配置里的 server.port。而同一份 Dockerfile 的 :146 却把 TDAI_GATEWAY_HOST 写进了 ENV,值是 0.0.0.0。两条并排放着,不必替它解释原因,但你在核「我挂的 yaml 到底说不说了算」时,得知道这两个变量在镜像里的待遇不一样。
有两条相关行为也值得一并知道,免得核错地方:
GET /health是无条件可达的。server.ts:1046处理/health的分支排在鉴权门之前,紧邻的注释写的就是它无条件可达。Dockerfile:156-157的HEALTHCHECK打的也正是http://127.0.0.1:${TDAI_GATEWAY_PORT:-8420}/health。换句话说,/health能通不代表鉴权生效。- CORS 的默认是「不发头」。
applyCorsHeaders(server.ts:1176-1180)在允许列表长度为 0 时直接返回、一个 CORS 头都不发,注释称之为strict default。这与 apiKey 是两条独立的开关,别把「浏览器跨域被拦」当成鉴权在起作用。
什么情况说明你遇到的不是这一条
排查文章最该写清的是排除项:
- 你根本没起 Gateway。
MemoryCore/index.ts那条路径是作为 OpenClaw 插件在宿主进程内注册工具与钩子(api.registerTool(...)三处、api.on("before_prompt_build", ...)、api.on("agent_end", ...)),不经过这条 HTTP 鉴权路径。 - 你绑的是回环地址。
tdai-gateway.yaml:27-28与tdai-gateway.standalone.yaml:12-13的server.port/host默认值都是8420/127.0.0.1,README.md:44也写Listens on 127.0.0.1:8420 by default。前提不成立,这条差异就与你无关。 - 你收到的 401 文案是
Missing x-tdai-service-id header。那来自 v2/v3 侧的parseV2Auth(v2-router.ts:350-360),是缺少隔离头,不是 apiKey 这条线。同一函数在缺Bearer时返回的文案是Missing or invalid Authorization header. Expected: Bearer {api_key}。 - 你的问题出在
/v3的三元组上。/v3侧要求team_id/agent_id/user_id,由collectV3Missing(v2-router.ts:130-150)收集,可从 body 或x-tdai-team-id/x-tdai-agent-id/x-tdai-user-id头取,其中session_id不强制。这属于参数校验,与启动时那条告警不是一回事。
另外说明我们核到哪为止:上面这些都是 dispatch 层的判定,至于 parseV2Auth 取到的 key 之后在每个 handler 里如何比对,我们没有逐个 handler 通读,这一段不下结论。
最后一句必须说的话
这个项目会采集并存储团队的对话、文档与代码——MemoryCore 侧落盘的目录结构里包含 conversations/(L0 每日 JSONL 分片)、records/(L1 每日 JSONL 分片)、scene_blocks/(L2 Markdown)、persona.md(L3 画像)与 vectors.db。仓库自己的诊断导出文档里,memory-tdai/ 这一栏的隐私风险标的是「高」,说明写的是「包含用户对话原文」。
所以「apiKey 到底是 warn 还是拒绝启动」这件事,落到你身上不是一个语义细节,而是一份配置该不该合并的判断依据。至于防火墙、密钥托管、网络隔离怎么做——那些属于通用运维做法,不是该项目文档里的内容,本文不展开,也不构成安全方案建议。
延伸阅读
- 从头读起:TencentDB Agent Memory 是什么:团队级 Agent 记忆中枢怎么读
- 本专题共 40 篇,完整分组目录见专题页
- TencentDB Agent Memory 的两份 gateway 配置逐字段对照
- TencentDB Agent Memory 的三份 SKILL 文档,与那个不存在的脚本
本文依据 TencentDB Agent Memory 官方仓库(github.com/TencentCloud/TencentDB-Agent-Memory)
feat/server_team 分支上的 README、INSTALL、CHANGELOG、ROADMAP 与四个模块的源码整理,
核对日 2026-08-16,对应仓库快照 97f9465。该仓库的默认分支即为 feat/server_team。
本文内容为仓库源码与文档口径,我们没有部署、也没有运行过该项目的任何一个模块,
因此不涉及运行效果、检索质量与性能的任何描述。
该项目主模块处于 beta 阶段、其余模块版本号仍为 0.1.0,参数与接口随版本变动,请以仓库最新内容为准。
该项目会采集并存储团队的对话、文档与代码,属于敏感数据,是否使用请结合自身合规要求评估。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。