[DEV] DobotAI 2.0生产库只读账号、治理双连接池与问答查询接入 #4
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
目标与已批准方向
用户已决定取消“新增DobotAI运营API”,由knowledge-governance直接读取DobotAI 2.0生产PostgreSQL,使用独立最小权限只读账号。
本工单交给开发Agent实施。第一阶段只完成数据库双连接、真实问答只读查询和安全验证;不开发UI、AI评估、统计报表或邮件。
已核实证据与实际访问路径
weknora-prod;应用127.0.0.1:8090/version返回部署SHA1bbaf2f07a1c8f6f41e58c9f2948dc0e273fb3db。/opt/dobotai/config/dobotai.env,DATABASE_URL定位目标RDS。不得输出DSN/密码或修改该配置。ssh -W与socketpair连接,不新增监听,不改SSH/NPS。25000与25001都实测为SSH banner,不是PG端口;生产SSH别名有效port为25001;NPS bridge配置是41256。用户已澄清此前数据库端口是随手填写,不得继续硬编码25000或把41256当PG目标端口。当前代码依据
治理仓库根目录:
/home/zheromiku/knowledge-governance。internal/appconfig/config.go:15,77–80:治理自身DATABASE_URL。cmd/api/main.go:48–60:治理主pool创建、Ping、ApplyMigrations、管理员初始化。绝不能把它改指向外部库。cmd/worker/main.go:40、cmd/release-worker/main.go:47:保留原治理连接,不默认注入外部运营pool。internal/httpapi/auth.go:100–198:已有Cookie/RBAC,GET默认viewer,登录会话刷新有写操作;外部问答正文权限需独立保护,本阶段不开发UI。weknora-prod:/opt/dobotai-src-1bbaf2f中coverage为v5;不能按旧副本v3规则实施。本地参考文档(尚未因此工单自动提交到Git):
docs/architecture/DobotAI PostgreSQL 双连接设计与实施交接.mddocs/architecture/DobotAI 2.0 生产问答数据接入与知识运营建设方案.md执行顺序与开发拆分
1. 专用只读账号与安全凭据
用户已明确要求代创建只读账户。本项仅授权目标DobotAI生产库的专用角色和必要查询权限配置,不是授权修改业务数据、表结构、全库权限或应用账户。由具备生产权限执行职责的Agent/DBA操作;权限不足则报告阻塞,不擅自提权。
kg_dobotai_ops_ro;先查同名角色,存在则核验,禁止盲目覆盖/重置。~/.config/knowledge-governance/secrets/,目录0700。禁止密码进聊天、日志、命令行参数、Issue、Git;不覆盖未知已有secret。2. 独立配置与连接通路
3. 第二连接池与故障隔离
internal/dobotaiops/建议独立包internal/dobotaiops/,显式注入只读Repository,不复用治理Store/发布写adapter。4. 参数化只读查询模块
建议内部接口,不要求新建DobotAI服务/API:
QueryCounts:服务端租户、start/end、可选channel/status,返回总量及状态/渠道分组。
QueryRuleCoverage:同一范围返回版本/verdict/reason分组及成功turn缺决策量。
ListTurnMetadata:稳定turn_id、时间、状态、渠道、Agent、引用计数、维度缺失标志;(created_at,id)游标,limit≤100。
GetTurnForAuthorizedRead:必要时按租户+turn_id读取正文/引用;测试输出不含原文或员工个人值。
时间统一半开区间时间统一半开区间
created_at >= start AND created_at < end,校验start<end、时区和范围上限。SQL绑定参数SQL绑定参数,不拼接调用方SQL/排序字段;固定查询列。
turns→messages→actor连接同时校验tenant/channelturns→messages→actor连接同时校验tenant/channel;按turn计次,不能混计入站/出站消息。
coverage按turn_id及tenant/channel关联coverage按turn_id及tenant/channel关联;规则结果明确命名rule_coverage,保存evaluator_version/reason_code。
v5 covered包含非知识响应/纯视觉识别v5 covered包含非知识响应/纯视觉识别,不能当AI正确性或正式知识覆盖率;本阶段不新增AI评估器。
目录属性与提问时快照区分目录属性与提问时快照区分;staff_id/union_id关联规则验证,不按姓名盲JOIN。缺失返回unknown。
5. 安全测试、回归和交付
拟议修改文件
不修改DobotAI业务源码,不创建生产表或迁移。不开发报表UI、AI评估、邮件;不替换治理数据库。
第一阶段验收标准
执行权限与交付边界
估算及阻塞
连接/仓储/安全测试按约2–3个单人工作日估算;账号权限、SSH运行条件和TLS整改等待不计入,若需要新增SSH拨号适配则核实后重估。先账号/访问与配置pool,再只读查询及验收。
当前主要阻塞:无合格只读登录角色;应用级SSH拨号/容器访问需确认;RDS段TLS安全策略需明确。不得以复用应用写账号或硬编码错误端口绕过。
实施进展(开发Agent,2026-10-14)
第 1 项「专用只读账号与安全凭据」已完成并核验;验收标准前三条中两条已核验。逐项证据见下方评论。
两点披露
pg_stat_ssl.ssl=false):本阶段按现状连接,未改任何服务器安全配置,也未显式降级(DSN 未写sslmode=disable,交由 libpq 默认prefer协商)。生产内网 TLS 方案需运维核实后另行批准。TEMP/CONNECT是 PUBLIC 平台默认(datacl显示=Tc/postgres):我没有放宽它;单独收紧必须改 PUBLIC(全局),按工单要求暂停并申请批准,未执行任何全局 REVOKE。directory_profiles的 SELECT 未授(按工单「仅在必要关联确实使用时授权」),留待第 4 项确需关联时再单独申请。审查与修复记录(2026-10-14)
671469b;我此前汇报里写的d4406a4是 amend 前的旧号,报告有误。ctx/子进程生命周期、ACL 门禁覆盖面)在
671469b时并不成立,审查据此判"暂不验收"。3802d86(分支feat/dobotai-ops-readonly-phase1,未推送)。逐条修复证据见下方评论;修复过程中又自查出两个缺陷(见评论"额外自查")。
make verify15/15。status/working,等待复审;不推送、不合并、不部署、不关单。第 1 项完成:最小权限只读账号已创建并核验(开发 Agent)
已勾选项的证据(全部来自生产库只读查询 / ACL 函数,未执行任何写语句)
角色与授权(
kg_dobotai_ops_ro,本次新建;建前确认不存在,脚本幂等、不覆盖)rolsuper=f, rolcreaterole=f, rolcreatedb=f, rolcanlogin=t, rolreplication=f, rolbypassrls=f, rolconnlimit=4default_transaction_read_only=on、statement_timeout=10s、idle_in_transaction_session_timeout=15sturns/messages/coverage_decisions/channel_actors:SELECT=t,INSERT/UPDATE/DELETE/TRUNCATE 全 f;directory_profiles未授(SELECT=f)CONNECT=t、CREATE=f;publicUSAGE=t、CREATE=fnone;可SET ROLE到的角色数 0/root/.config/knowledge-governance/secrets/dobotai_ops_ro.env(目录 0700、文件-rw------- root:root,密码 48 位、生产机内生成,未进聊天/日志/命令行参数/Git/工单)用新账号真实登录 + 只读实测(不是"DDL 返回成功"算过)
独立对拍(同一时刻、同口径,管理账号再读一次)
两点披露(需要你/运维拍板,未擅自处理)
ssl=false):未改任何服务器安全配置,也未显式写sslmode=disable(交给 libpq 默认prefer协商)。生产内网 TLS 需运维核实后另行批准。TEMP/CONNECT属 PUBLIC 平台默认(datacl==Tc/postgres):未由我放宽;单独收紧必须改 PUBLIC 全局(会影响其他账户),按工单要求暂停并申请额外批准,本次未执行任何全局 REVOKE。下一步(第 2–4 项,代码层)
internal/appconfig:DOBOTAI_OPS_READ_ENABLED/DOBOTAI_OPS_READ_DSN_FILE/ 超时与 pool 上限(默认关闭、缺 secret 即关闭、错误脱敏)。internal/dobotaiops/:独立dobotaiReadPool(max_conns=2、min_conns=0、连接超时 5s、查询 3–5s;启动default_transaction_read_only=on、每次查询显式 READ ONLY、统计走 REPEATABLE READ READ ONLY 固定 as_of)、ACL 门禁自检(连接后检查 schema/ACL,不跑迁移/DDL/写 SQL)、参数化仓储(QueryCounts / QueryRuleCoverage / ListTurnMetadata / GetTurnForAuthorizedRead)。cmd/api/main.go第二 pool 接线与生命周期(主库迁移路径不变;外部不可用时明确 unavailable,绝不回退主库或应用写账号)。待你确认的两个决策:① 治理侧生产实例若在云端,secret 需要落在哪台机器(当前放在生产跳板机
weknora-prod的/root/.config/knowledge-governance/secrets/,0600);② 第 2 项 SSH 拨号是否复用既有weknora-prod别名(容器内拨号可行性待核)。这两点我先按"最小假设"实现为可配置,不写死。按工单约定:不自动合并、不部署、不推送;等你验收。
第 2–5 项实现完成(开发分支,未推送)
分支:
feat/dobotai-ops-readonly-phase1提交:d4406a4按工单约定:不推送、不合并、不部署。make verify15/15 success(592 通过 / 0 失败 / 81 可选跳过;新用例全部必需、无豁免)。新增代码
internal/appconfig/config.goDOBOTAI_OPS_READ_ENABLED、_READ_DSN_FILE(必须绝对路径)、_DIAL_MODE(direct|ssh)、_SSH_TARGET(只接受单个 SSH 别名,拒绝空格/路径/端口/选项注入)、_CONNECT_TIMEOUT ≤15s、_QUERY_TIMEOUT ≤15s、_MAX_CONNS[1,8]、_MIN_CONNS[0,max]internal/dobotaiops/dsn.goGuardGovernanceDatabase拒绝把只读接入指向治理主库internal/dobotaiops/pool.gopgxpool(会话层default_transaction_read_only=on+statement_timeout+idle_in_transaction_session_timeout+ 连接上限);Ping 后跑 ACL 门禁internal/dobotaiops/acl.goErrNotReadOnly(fail closed);不执行任何写语句,不用"写后回滚"证明只读internal/dobotaiops/window.go/dto.go(created_at,id)游标编解码internal/dobotaiops/repository.goQueryCounts/QueryRuleCoverage/ListTurnMetadata/GetTurnForAuthorizedRead:全绑定参数、列固定、按 turn 计次、连接同时校验tenant_key+channel、rule_coverage保留evaluator_version/reason_code、部门/区域快照为空如实missing、授权读取必须显式回调(无默认放行、只带actor_type不带员工姓名)internal/dobotaiops/sshdial.gossh -W+ socketpair 的 direct-tcpip 拨号:Go 侧是支持 deadline 的真实 net.Conn;不新增监听、不改 SSH/NPS/systemd/Docker 网络、不复制私钥、不放宽 known_hostscmd/api/main.godocs/DobotAI_OPS_READONLY.md.env.example、scripts/test-scope.json测试与实证
与工单的三点偏差(已在正文/评论显式披露,未擅自扩围)
DOBOTAI_OPS_TENANT_KEY_FILE,而是查询入参(每次查询绑定tenant_key)。若你要"平台级默认租户 + 文件引用",我可以加一层默认值,但多租户查询本来就要显式传租户,所以第一阶段没引入这个文件。directory_profiles未授权、未读取(按工单"仅在必要关联确实使用时授权"),所以"staff_id/union_id 关联规则验证"这一条在第一阶段不适用;需要时再单独申请授权。等你拍板的两件事
/root/.config/knowledge-governance/secrets/dobotai_ops_ro.env(0700/0600,仓库外、未被 Git 跟踪)。若治理生产实例在别的机器/容器,需要决定 secret 挂载方式(_READ_DSN_FILE指向挂载点即可,不用改代码)。ssl=false,本实现未改服务器配置也未显式降级(交由 libpq 默认prefer)。要不要推动运维开 TLS 并配 CA。按工单:等你验收通过再关闭工单;需要我推送/合并/部署时请单独指示。
审查意见逐条修复 + 生产组合验证(提交
3802d86,未推送)审查结论我认可:只读账号合格,但代码在
671469b时不足以验收。以下逐条给出"我的独立复核 → 修复 → 证据"。先给事实(我用生产只读探测独立复现,全部为 SELECT)
messages.actor_id → channel_actors(id);错关联 0 行 / 对关联 2234 行('processing','succeeded','failed','cancelled');实际值 succeeded 2198 / failed 35 / processing 1TEST_DATABASE_URL就建表/删表Config()泄凭据、ctx、Err()修复清单
KG_DOBOTAI_TEST_ISOLATED=1,且 DSN 必须通过硬条件(拒绝含 prod/governance/dobotai 的库名;非本机主机要求库名含 test;本机只额外放行一次性postgres)。未声明=跳过,声明了不合规=直接失败。新增 9 例"拒绝误配置"测试channel_actors.id = messages.actor_id;并新增ActorLinked区分"未关联"与"类型为空",夹具照真实外键重建(id 与 external_actor_id 取值不同 ⇒ 再犯必被抓)validateQueryWindow():租户、半开区间、MaxWindow跨度上限、未来时间全部落在每次查询上(不再只依赖NewWindow);补"绕过 NewWindow"用例sshdial拨号阶段尊重 ctx(取消⇒不建连、不起子进程);连接建立后刻意不绑该 ctx(pgx 连接成功即取消拨号 ctx,绑上去会掐断健康连接),改为显式绑定"连接关闭 ⇄ 子进程回收"(Close 幂等 + SIGTERM/Kill 兜底);补取消与回收测试=(如?sslmode=require)不再被误当 KEY=VALUE;护栏identityOf归一化默认端口并忽略查询串/协议差异(省略 5432 也绕不过)channelRows.Err():渠道分组中途失败不再返回部分成功结果Config()清空DSNFile/GovernanceDatabaseURL/DialContext;补脱敏测试SET ROLE到的角色数、拥有 relation/schema 数、库级CREATE、REPLICATION、可执行的 SECURITY DEFINER 函数;逐个新增检查面都有 fail-closed 用例额外自查(修复过程中被发现的两个缺陷)
tunnelConn有 panic:reapedchannel 未初始化就close⇒close of nil channel(组合验证第一次运行直接崩)。已修 + 幂等 Close +Exited()兜底。补齐你未能完成的那一步:生产只读组合验证(Go 客户端 + 新只读账号 + SSH 通路)
其他验证
仍未做/待你定
~/.config/knowledge-governance/secrets/dobotai_ops_ro.env,0600、仓库外、未进 Git)——只为跑组合验证;需要我删除或保留请示下。