2026-09-23·by Sijie Wang#software

facade-directions

门面的方向——两个信任平面

上级:structure · 邻居:facade-parity · owner-facade-from-registry · acl-and-quota-granularity

facade-parity 让每一个门面都成为唯一那份注册表的投影。但那个模型没有方向这根轴 —— 它的基础 reach 只有 OwnerRead() / OwnerAction(),接的也就两个门面,两个都朝向 owner。这一篇补上缺的那根轴;而"为什么必须在任何对外门面碰这套机制之前补",值得说白:

天真地把一个对外门面注册进去,会让每一个 OwnerRead() 的 op 在它上面显示为"缺失" —— 棘轮会要求把 admin 控制台的读操作暴露给陌生人。 一个没有方向概念的对等机制,不只是保护不了你,它会主动往错的方向推

模型

一个门面的身份,是它服务的那个行动者。对等把门面往上归一层,按信任平面分组 —— 持有同一级授权的行动者。平面正好只有两个。

平面行动者授权对等语义
owneradmin 控制台、owner 自己的 AI(经 MCP);将来还有 Electron、IM 摄入登录会话 / 密钥对完备性 —— 每一个 owner op 出现在每一个 owner 门面上
outward访客的人(码 / BYOAI)、访客的程序(API key)、匿名读者与爬虫一个角色,从授权解析出来角色一致性 —— 一个可授予角色的能力,出现在每一个对外门面上,除非它那一类被豁免

"公开"不是一个平面

匿名访客是那个退化的授权:他的角色就是 owner 的公开角色。这正是 BYOAI 已经限定到的那个角色,也正是 reader 页面上 published 所闸住的东西。于是在对外这个平面内部,一切收敛成一行:

reachable(caller, cap) =
    facade-renderable(cap)      # can this facade physically carry it
  ∧ opened(cap)                 # owner made it an API candidate (API facade only)
  ∧ role(grant).grants(cap)     # code role / key role / public role
  − denials(grant, cap)         # per-code / per-key deny rows

逐行读:这个门面承载得了它吗;owner 把它成 API 候选了吗(仅 API 面);这次授权解析出的角色授予了它吗;再减去按码 / 按 key 的拒绝行

这个旋钮从匿名 → 带 key → 带码,走在同一根轴上。把"公开"收进 ACL 的回报是:owner 将来可以直接把某个能力授予公开角色本身(比如匿名只读搜索),不需要任何新机器

泄露不变量,两处强制

一个 owner 平面的 op,永远不许渲染在任何对外门面上。 这就是为什么方向必须是结构性的,而不是一条约定:

  • manifest 层 —— 一种 leak 违规类型:任何一个暴露里含有"平面跟门面不一致"的 op,就是硬红,两个方向都查,启动时直接失败。不是"不要求",是"禁止"。
  • 注册表层 —— 仅 owner 的能力,其访客绑定本来就返回 ErrHidden,而对外门面走的是同一条装配路径,于是这类能力在结构上不可能出现。一致性测试会往对外暴露里注入一个假的 owner op,断言它变红(a-gate-that-cannot-go-red)。

同一条不变量用两套机制守是刻意的:manifest 抓的是声明错了,注册表抓的是装配错了,两者谁也盖不住对方的失误。

第四个门面:API key

方向这根轴就是为了容纳它才建的。在它之前,一个外人够到这台实例只有两条路:访客对话(要码、有 LLM 在回路里、按轮次计量)或者匿名公开页 —— 没有任何办法让一个程序直接调用 owner 的能力。 API-key 面就是那个位置:像一张访问码,减去脑子和油钱 —— 同样是按角色限定的授权,但形态是"端点进、结构化数据出",用速率限制而不是轮次/会话配额来节流。

它对外、非 agentic、按角色限定;而它之所以立得住,正因为平面模型已经先回答了"它可以承载什么"。已上线,2026-07-10(b42a15e7f):backend/internal/routes/pubapi/pubapi.go —— 只渲染非 agentic 的对外工具,按 key 的固定窗口限流器(rateWindow)负责节流,候选闸门(上面那行 opened(cap))的棘轮已归零;2df6b795b(2026-08-20)让经 key 发起的预约能说明是替谁订的(on_behalf_of.go)。

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →