Retool 与受治理 AI 应用平台:能否审查业务权限
Retool 的 RBAC、审计日志、SSO 和自托管能力很强。真正要比较的是业务权限层:退款、审批、写入动作如果散落在 JavaScript 绑定里,AI 改动就很难被业务负责人用 diff 审查。
TL;DR: Retool 的访问治理很强:RBAC、审计日志、SSO、源代码管理、Enterprise 自托管都是真实能力。问题不在“Retool 没治理”,而在治理的层。RBAC 控制某个角色能触及哪些资源;真正的业务权限——这个人能不能发起一笔 1.2 万美元的退款?——往往分散在按钮 Hidden、输入框 disabled、查询 transformer 和脚本里。工程团队可以通过规范和代码审查管理它,但 AI 参与修改后,业务负责人很难只看一份 diff 就判断“谁能做什么”是否改变。缺口不叫安全功能缺失;它叫业务权限没有被声明成可审查的事实。
设想世上最寻常的那个 Retool 应用:一个退款控制台。一位工程师花一个下午就把它建好——一张订单表、一个“退款”按钮、几条查询。客服在用。它很棒。这正是 Retool 最出彩的样子,而 Retool 在这件事上确实是品类里最好的。
如今半年过去了。当初构建它的工程师已经换了团队。安全团队正在做访问审查,向新的负责人抛出一个一句话的问题:“哪些角色能发起超过 1 万美元的退款,以及它究竟在哪里被强制执行?”
看看新负责人为了回答得做些什么。没有一个单一的地方可供查看。他打开应用,开始读 JavaScript。
grep 测试
“它在哪里被强制执行”,在一个真实的 Retool 应用里实际长这样。发起一笔大额退款的权限,至少散落在三处彼此独立的表达式中,每一处都是手写的,每一处形状都不同:
// 1. refundButton → “Hidden” 属性
{{ currentUser.groups.includes('admin') ||
(currentUser.groups.includes('support_lead') && refundAmount.value <= 10000) }}
// 2. refundAmount input → “disabled” 属性
{{ !currentUser.groups.includes('support_lead') }}
// 3. issueRefund query → transformer(这才是真正写入的那部分)
if (refundAmount.value > 10000 && !currentUser.groups.includes('finance')) {
throw new Error('Refunds over $10k require finance');
}
三个地方。三个微妙不同的条件。按钮对某些用户隐藏自己;输入框对另一些用户禁用自己;查询则对第三组人抛出错误。它们本应加总成一条自洽的规则——“二级客服可退到 1 万美元,更高的归财务”——但没有任何东西保证它们会如此。把一个按钮隐藏掉,只要有人换条路触发,底下那条查询照样会跑。下个季度添加第二条写入路径却忘了那道 finance 检查,你就有了一个任何屏幕都不会暴露的漏洞。
把这叫作 grep 测试:要回答“谁能做 X”,你是读一条声明过的规则,还是得对整个应用 grep?如果是 grep,那么这个应用在权限层就没有被治理——它仅仅是被编码出来的,而访问治理(Retool 做得无可挑剔)所在的那一层,恰好高过真正能伤到你的那个东西一层。
作为对照,这里是同一份权限作为一项声明过的事实——一条规则,无论你是从哪个按钮或哪条查询进来的,运行时都会在每一条路径上强制执行:
action: issue_refund
who_can_run:
- role: SupportLead when: amount <= 10000
- role: Finance when: amount > 10000
requires_approval: when: amount > 10000 # 一道预防性的闸门,而非一个被抛出的错误
audit: always
只有一处可读。只有一处可改。只有一件事要让审查者——或一个 AI——去推敲。三处手工维护的表达式与一条声明过的规则之间的差别,正是“应用是被编码出来的”与“应用的权限是被治理的”之间的全部差别。
为什么源代码管理填不上这道缺口
精明的 Retool 工程师的反驳很在理,你得精确地回应它:“我们有 Git 支撑的源代码管理、审计日志和 RBAC。改动会在 PR 里审查。访问是被治理的。还缺什么?”
具体缺三样东西。
第一,对一个 Retool 应用的 PR,审查的是散落在各组件间的 JavaScript 与绑定——你在做代码审查,这意味着 (a) 它需要一位工程师,且 (b) 不存在一个结构化的表征来说明*“这次改动改变了谁能发起一笔大额退款。”* 审查者必须自己察觉到:三个组件之外的某个 Hidden 表达式的一点小改,挪动了一道权限边界。散落逻辑的 diff,恰恰把你最需要看见的那个东西藏了起来。
第二,源代码管理审查的是代码,而非作为事实的权限。“这个 PR 改变了谁能退款超过 1 万美元吗?”不是一个 Git diff 能直接回答的问题;这是一个你得在脑子里追着代码去回答的问题。而上面那条声明过的规则让这份 diff 变得微不足道:who_can_run 改了,或者没改。
第三——也是对行业走向最要紧的一点——低代码的整个前提,就是非工程师和 AI会去改这个应用,常常是在可视化编辑器里,常常不经过传统 PR。你所倚仗的那种治理(代码审查),未必覆盖低代码本来要服务的那些改动。审计日志会在事后告诉你某个 Hidden 属性变了——是检测式的,不是预防式的——但在它上线之前,业务负责人未必看见过它改变了什么权限。
AI 不会打破这一点。它会成倍放大它。
多年来 grep 测试还能活下去,是因为有一个人写下 JavaScript,另一个人承载着每条规则存活在何处的知识。散落的逻辑,只要是人构建并记得的,就还摸索得通。
把一个 AI 对准同一个应用,经济账便在两个方向上同时反转。AI 能在一分钟内生成五十个绑定和十几个 transformer——它产出权限决策的速度比任何审查者能追踪的速度都快。而要审查它产出的东西,你又回到了一屏接一屏地读 JavaScript,因为依然没有一份单一的、结构化的权限表征可供 diff。“Retool AI”和智能体(2025)真实存在且有用,但它们是在同一套手工搭建的底座之上做自动化:智能体是被治理的(Retool 能限定它可以调用什么),但智能体所编辑的那个应用却不是——它在任何一种 AI——或人类——能当作事实而非代码来审查的形式上,都没有被权限治理。
AI 最擅长快速产出的东西,恰恰也是这种模式下最需要结构化审查的东西。
Retool 才是正确选择的场合——以及元数据模型让你付出代价的场合
两个方向上都要诚实,因为一篇一边倒的文章,对一个真要做决定的人毫无价值。
Retool 的访问治理确实是同类最佳,而且对一大批应用来说,grep 测试很可能咬不到你:一个由可信员工操作、藏在 SSO 之后、由当初编写它的同一批工程师维护的内部工具,其中“二级客服可退到 1 万美元”被强制执行在三处恰好彼此一致、又极少改动的地方。对这样的场合——以及对那种需求一旦古怪就能随时落到 SQL 和任意 JavaScript 的自由——Retool 都很出色,而“快速接好一个工具”这件事我们是赢不了的。
而声明式元数据模型在另一个方向上有实打实的代价:你拿原始的灵活性,换来了可读性。当模型里没有容纳它的地方时,你没法只是往一个属性里粘一段任意的 JavaScript;把真正不寻常的逻辑表达成声明过的规则,比随处可用的 {{ }} 更受约束。对一个用完即弃的工具来说,这份约束是纯粹的额外开销。只有当“谁能做什么”是某种你会被要求去证明的东西时——被审计人员、被监管方,或被下一任负责人——并且当做改动的是一个 AI、而非原作者时,这个模型才值回它的代价。
那就是那条线。线之下,Retool 凭实力取胜。线之上,grep 测试正是半年之后、在某个拿着写字板的人面前,你会栽的那一项。
ObjectStack 的立场
ObjectStack 保留了 Retool 做对的东西——强大的访问控制、自托管、你自己的基础设施——并把权限下沉到同一个被治理的层。应用的逻辑是开放、可读的元数据:一个像 issue_refund 这样的操作,携带着它自己的 who_can_run、它自己的审批闸门、它自己的审计要求,声明一次,由运行时在每一条路径上强制执行。一次 AI 改动,就是针对那份声明过的权限的一份 diff——“这次改动让 SupportLead 能退到 2.5 万美元”是一句非工程师能在上线前读懂并批准的话——而不是一个你日后才靠 grep 发现的 Hidden 表达式。
我们并不打算在“落到 JavaScript、午饭前就发布一个工具”这件事上胜过 Retool。这个主张更窄,且只在那条线之上才要紧:当 AI 在编写你的内部软件时,谁能做什么应当是一项你能在一个地方读到、并能当作 diff 来审查的事实——而不是三处表达式的某种涌现属性,它们必须彼此一致、通常也确实一致,直到它们不一致的那个季度。