“Codex 私域商用超级站”常被描述为“快速搭建一套能收钱、能运营的私域系统”。但同一套技术栈的安全审查文档给出了一个精确的边界:当前版本仅批准用于本地单用户部署;workspace-write 限制写入,但不是通用的机密读取沙箱;本地 bearer token 是单一共享操作员机密,不承载按用户的身份或授权。多租户商用部署需要的是事务性存储、租户隔离、按主体的配额、分布式限流、加密的机密管理和带签名的审计导出。演示站与商用站之间的距离,不在功能多少,而在私域数据在数据库层和执行层是否都无法越界。
私域站最危险的失败模式,是应用层某条查询漏写了 WHERE tenant_id = ?。仅靠代码层过滤,一个疏漏就是跨租户泄漏。PostgreSQL 的 RLS 把隔离下沉到引擎:即使应用查询完全忘记过滤,返回的行也只属于当前租户。商用部署的底线是三件事同时做到:ENABLE + FORCE ROW LEVEL SECURITY,策略使用 current_setting 读取会话租户,并且策略同时覆盖 USING(可见性)与 WITH CHECK(写入归属)。FORCE 不可省略——没有它,表 owner 会绕过策略,迁移连接或应用自身连接可能“看起来隔离了,实际什么都看得到”。
ALTER TABLE customers ENABLE ROW LEVEL SECURITY;
ALTER TABLE customers FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON customers
FOR ALL
USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid)
WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid);NULLIF(current_setting(..., true), '') 的写法是 fail-closed:没有设置租户上下文时,current_setting 返回空字符串,映射为 NULL,而 tenant_id = NULL 永远不为真,结果返回零行,而不是“所有行”。租户上下文必须在每个事务开始时通过 SET LOCAL 设置,使其在 PgBouncer 事务池模式下也不会漂移到下一个请求。运行时连接还必须使用 非超级用户、非 BYPASSRLS 角色,否则 RLS 会被整体忽略。
Codex 的安全边界由两个可以独立组合的配置项决定:sandbox_mode 限定 Agent 能“碰到多大范围”,approval_policy 决定动手前是否必须停下来请求批准。安全审查推荐的基线是 workspace-write 加默认断网,并使用经过清理的环境。危险的是把 danger-full-access 或 --yolo 当作省事默认值——它们会绕过全部沙箱和审批,只应在已有外部隔离的容器中使用。
商用场景下更关键的一步,是把 Codex 的运行上下文按租户切开。CODEX_HOME 承载账号登录态、会话历史和本地配置,多个租户共用一个 ~/.codex 会导致登录状态互相覆盖、会话数据混用。为每个租户指定独立的 CODEX_HOME,认证、插件和会话历史才会落在租户各自的目录下。以下启动器把租户隔离、沙箱和审批策略同时写入进程参数:
import os, subprocess
def launch_tenant_codex(tenant_id: str, workspace: str, task: str) -> None:
home = f"/srv/codex/tenants/{tenant_id}/home"
os.makedirs(home, exist_ok=True)
env = {**os.environ, "CODEX_HOME": home}
subprocess.run([
"codex", "exec",
"--sandbox", "workspace-write",
"--ask-for-approval", "on-request",
"--disable-network",
"--cd", workspace,
task,
], env=env, check=True)CODEX_HOME 按租户隔离解决的是状态不串;workspace-write 解决的是写入范围受限;on-request 解决的是越界动作必须请求授权。三者不能互相替代。
从“能跑”到“能商用”的最后一步,不是增加页面或会员功能,而是在发布前机械检查隔离边界是否就位。以下门禁把 RLS 强制状态、跨租户测试、租户级凭据隔离和签名审计可用性写成硬性条件,任何一项缺失都拒绝进入商用环境:
def commercial_gate(tenant_id: str, db_conn) -> list[str]:
errors = []
rows = db_conn.execute("""
SELECT relname FROM pg_class
WHERE relname IN ('customers','orders','messages')
AND (relrowsecurity = false OR relforcerowsecurity = false)
""").fetchall()
if rows:
errors.append(f"RLS_NOT_ENFORCED: {[r[0] for r in rows]}")
if not audit_exists(tenant_id, "cross_tenant_rls_test", passed=True):
errors.append("RLS_TEST_MISSING: 跨租户测试未通过或未留痕")
if not tenant_secret_isolated(tenant_id):
errors.append("SECRET_SHARED: 租户凭据未隔离")
if not signed_audit_export_ready(tenant_id):
errors.append("AUDIT_UNSIGNED: 签名审计导出不可用")
return errors
if errors := commercial_gate(tenant_id, db):
raise PermissionError(f"商用门禁未通过:{errors}")跨租户测试必须以真实的非超级用户身份、穿过视图和函数去尝试读写另一个租户的数据,预期结果为零行或显式拒绝,而不是“看起来配了策略”。Codex 进入 CI 后,security 和 Codex 评审必须能够阻断合并,与人工 PR 走同一条门禁流水线。
Codex 私域商用超级站的成熟度,最终不以“多久能搭出页面和会员功能”衡量,而以RLS 是否在引擎层强制隔离且 fail-closed、CODEX_HOME 是否按租户隔离运行状态、沙箱与审批是否构成两道独立边界、签名审计和跨租户测试是否作为商用放行条件来衡量。课程可以教你快速生成一套站,但商用资格只能由代码中那几道模型无法绕过的隔离边界来授予。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。