Sandbox 寫回 Custom Table
已發布的 Sandbox task version 可以宣告 Custom Table output contract。符合資格的人類提交該版本時,TeamSync 會在建立 Sandbox run 的同一個 transaction 裡產生一組 run-scoped callback credential,之後把它注入該 run 的 execution manifest。Script 可以用它在一張綁定資料表上新增 records,或新增並更新 records。
這組 credential 不是一般 user session。Public callback 不會在每個 request 重新解析 caller 的 live ACL;token 本身就是在剩餘生命週期內操作整張綁定資料表的 authority。這正是 authoring、submission、confirmation、reveal 與 revocation 必須各自 fail closed 的原因。
精確 output_policy shape
output_policy 屬於 Sandbox task version:
{
"output_policy": {
"kind": "custom_table_writeback",
"table_id": "22222222-2222-4222-8222-222222222222",
"allowed_ops": "create"
}
}Contract 是 strict:
| 欄位 | Contract |
|---|---|
kind | 必須剛好是 "custom_table_writeback" |
table_id | 同公司、仍存活的 Custom Table UUID;帶 channel 規則的資料表會被拒絕 |
allowed_ops | "create"(預設)或 "create,update" |
未知 key 與其他 operation 字串都會被拒絕。Target 會在 draft create/update、publish、run submission,以及 secret reveal 給 execution manifest 前分別檢查。不存在、已軟刪除、跨公司或後來加上 channel 規則的 target,不會退化成一個已付費卻拿到無效 credential 的 run。
只有 owner-local 的 chatroom/department job 能 mint
支援兩條 owner-local lane。Chatroom-owned job 只能在完全相同的 owner room 執行時 mint;Quick Run 建立的 hidden task 就走這條 lane。Department-owned job 可在 owning department 的任何 room mint。Company-owned job 帶著 output_policy 發布版本時會被拒絕:
{
"detail": {
"code": "output_policy_owner_scope_unsupported",
"message": "output_policy is not supported on company-owned tasks; use a department-owned task and run it from a room in the owning department"
}
}對 department-owned job 而言,owner department 內的 room 即使是透過 explicit share grant 取得 job,仍屬於 owner audience,可以 mint;不能只因儲存的 share_generation 非零就把它判為 borrowed。
Acting room 位於 task 的 home audience 外時,run 就是 borrowed:對 chatroom-owned job,是任何不同於精確 owner room 的 room;對 department-owned job,則是其他 department 的 room,包括透過 department/company share 被納入者。作為 legacy 或其他不合法狀態的 defense in depth,mint path 也會把意外帶有 output_policy 的 company-owned version 在任何 room 都視為 borrowed;受支援的 publish path 不會建立這種狀態。Borrowed run 不會 mint token;平台會寫下 ct_writeback.mint_skipped audit,reason 為 borrowed_run_share_generation。Cross-tenant writeback 永遠不允許。
Author 與 submitter 都必須覆蓋 token authority
Token 能寫整張表,而且 callback request 沒有 per-request row 或 column ACL context。因此,TeamSync 要求兩個參與把 authority 交給 script 的身分,本來就各自擁有至少同樣寬的權限:
| Gate | 身分與必要權限 |
|---|---|
| Publish | Task-version author/publisher 必須是 table manager,或具 insert 權限且沒有 hidden columns;create,update 另外要求不受 filter 限制的 can_edit: "all"。失敗為 403 output_policy_author_denied。 |
| Submit/mint | 提交的人類接受同一套檢查。失敗為 403 writeback_authority_denied;不會排入一個帶過寬 credential 的付費 run。 |
| Manifest reveal | 再次檢查 target 與 submitter 權限;撤權或 policy drift 會阻止 secret injection 並留下 audit。 |
只有以 JWT 互動登入的 user 可以 mint。Sandbox API key、restricted Sandbox key 與 non-user principal 永遠拿不到 writeback authority。已宣告 policy 卻走到這些防禦性 skip path 時會留下 audit,不會被靜默當成成功 mint。
一組 credential、一個 run、有限生命週期
符合資格的 submission 會一起建立 token row 與 run。Execution 取得:
TEAMSYNC_CT_WRITEBACK_URL,作為 callback URL;- 透過 sealed secret channel 提供的
TEAMSYNC_CT_WRITEBACK_TOKEN。
Secret 以 deployment HMAC key 從 run ID 與 token ID 推導;資料庫只保存 hash,之後的 read API 不會再回傳明文。Manifest reveal 時,validity window 會縮到 run work timeout 加 finalization envelope。Signing key 不存在/太短,或 target 已不可用時,execution 在取得 authority 前就會 fail closed。
Run 進入 terminal transition 時 token 會被撤銷,包含取消與 offboarding cleanup paths。只有 token 仍 live 且 reveal gates 仍通過時,重建 manifest 才會得到相同 run-bound values。Key rotation 可以暫時保留 previous HMAC key 服務已 mint 的 runs;找不到對應 key 時,reveal 會拒絕 injection,不會猜測。
Callback 仍會套用 Custom Table schema、rules、links 與 allowed-operation checks;但它不會重新解析被標記使用者的 row/column ACL,created_by 只是 attribution,不是 authentication。它同樣不在 agent 工具箱那道受 command 治理的寫入轉導之內:那道閘門位於 agent 的資料列寫入工具裡,所以帶有 settings.agent_writes_via_commands_only 的資料表,仍然接受這組 credential 的寫入。不要把那個設定當成「強迫 Sandbox script 改走 command」的手段。兩個 environment values 都必須視為 secret:外洩等於在到期或撤銷前,取得對整張綁定資料表執行宣告 operations 的能力。Callback 以統一的 not-found 回應避免 token probe 成為 existence oracle。Callback request 協定見外部回呼。
Agent submission 一律需要稍後另一輪確認
Agent-facing Sandbox version 只要有 output_policy,即使一般 requires_confirmation 為 false,也屬於 confirmation-grade。第一次 tool call 只會準備並顯示 proposal,此時尚未建立 run;必須由一則不同、時間上嚴格較晚的人類訊息,確認釘住的 task version 與 digest、input、timeout、room 及帶 policy 的 request,TeamSync 才會建立並 dispatch run。同一輪 prepare 與 confirm 會被拒絕。
這能防止 prompt injection 把使用者的資料表 authority 花在一組送進 task-author script 的 credential 上。只有 confirmation receipt 與所有 live submission gates 都通過後才會 mint token。
Trigger 永遠不能提交帶 policy 的版本
submit_sandbox_job 沒有人類 confirmation turn,因此任何帶 output_policy 的 task version 都會被拒絕,即使 requires_confirmation 為 false。Validator 會在 trigger 設定階段拒絕,worker 也會在 fire time 重複檢查,以防 pinned version 在 trigger 儲存後發生變化。
設定時錯誤為:
submit_sandbox_job cannot use a task version that declares a custom-table writeback output_policyFire-time denial 為:
submit_sandbox_job denied: task version declares a custom-table writeback credential; trigger submissions cannot mint one不能以清掉 requires_confirmation 繞過;output_policy 本身就會啟動 fence。請改用人類 JWT submission;若走 agent lane,完成明確的第二輪確認。Trigger action 其餘 contract 見 submit_sandbox_job。
整合 checklist
- 在 chatroom scope 為一個精確 owner room author task,或在 department scope 為 owning department 的 rooms author task;不要使用 company scope。目標必須是同公司、live、沒有 channel 規則的 Custom Table。
- 只選
create或create,update;update authority 要求不受 filter 限制的 edit-all 權限。 - Publish 前確認 author 具完整 token-covering authority。
- Chatroom-owned task 必須從精確 owner room 執行;department-owned task 必須從 owning department 的 room 執行;兩者都要由獨立具備相同 authority 的 JWT 互動使用者提交。
- Agent lane 必須有不同且較晚的 confirmation turn;這個版本永遠不能交給 trigger。
- Callback URL 與 token 只在 run 內讀取、不要記錄,run 終止後立刻停止使用。