Make the policy decision point pluggable (the rules are injectable, the evaluator is not)
維護者通常 1 天內回覆
還沒有人認領這個 Issue。
評估
- 難度
- 5/5
- 預估耗時
- 一週以上
- 新手友好度
- 35/100
- Issue 類型
- 功能
- 描述清晰度
- 基本清楚
- 活躍度
- 活躍
- 技術堆疊
- typescript
研究方向
從 server/src/computer/gateway.ts 的469-535行附近,以及 server/src/computer/policy.ts 的4-12、38-42、44-153和210-243行附近開始。確認 enterprise gateway 是否已經有可插拔的決策點,接著定義並記錄 decider contract,同時保留 snapshot resolution、dry-run 處理、audit 順序和 fail-closed 行為;完成標準包括預設路徑和一個完整示範的範例 decider。
由索引模型根據 Issue 內容生成。
描述
What I see today
The policy data is already fully injectable. options.policy() is a getter
(server/src/computer/gateway.ts:523), backed by createPolicyStore with
ACTION_POLICY_TOPIC for live reload (server/src/computer/policy-store.ts).
The evaluator is not. evaluateActionPolicy is a direct import from ./policy,
called synchronously:
const decision = evaluateActionPolicy(options.policy(), context);
So a deployment can change the rules, but not who answers.
Why that's worth a seam
Three cases the current shape can't serve:
-
A deployment that already owns a policy engine. OPA/Rego, Cedar, an internal
service. Today they have to transcribe their rules into CEL and keep two sources in
sync, which is exactly the setup where the two drift and nobody notices. -
Decisions that need state the gateway doesn't hold. "This bot has already
submitted three orders today." "This action is above the amount this actor may
approve alone." "A human granted this, twenty minutes ago, and the grant expires."
CEL over a statelessPolicyContextcan't express any of those — not a flaw in the
expression language, just a different question. -
Decisions that take time. Anything involving a human in the loop is async by
nature, and the current call site is synchronous.
The good news is that most of the work is already done: PolicyContext
(server/src/computer/policy.ts:44-153) is a clean, serialisable, effect-level
object — intent, mcp.effect, file.extension, command, resolved element. That
is a better PDP boundary than most systems that set out to build one.
Concrete shape
export type PolicyDecider = (
context: PolicyContext,
) => PolicyDecision | Promise<PolicyDecision>;
Default stays exactly what happens now:
const decide: PolicyDecider = options.decide
?? ((ctx) => evaluateActionPolicy(options.policy(), ctx));
// ...
const decision = await decide(context);
Line 523 is already inside an async method that awaits write(...) on the next
line, so the await costs nothing structurally.
Two things I'd want written into the contract, not left to the implementer:
- A decider that throws, or exceeds its timeout, is a refusal. Same discipline as
matches()and itsonErrorargument (policy.ts:210-243). A remote PDP that is
merely unreachable must not be a way to get an action through, and the timeout should
be required rather than optional. dry-runstays in the gateway, not in the decider. Otherwise an external decider
can silently disable enforcement for the whole deployment, which is the one thing this
seam must not make possible.
What I would not touch
- Snapshot resolution stays server-side (
gateway.ts:469-471). A decider receives the
element the server resolved, never what the caller claimed. The comment at
policy.ts:38-42is the reason the whole thing works and an external decider must not
get a chance to weaken it. - The gateway still writes the audit row before forwarding (
gateway.ts:524-535),
whoever decided. The trail records the decision, not the decider's opinion of it.
One question before any of this
policy.ts:4-12 says this mirrors the policy engine in CopilotKit's enterprise agent
gateway, deliberately, so a rule means the same thing in both. Does that engine already
have a pluggable decision point — and if not, is divergence here something you'd rather
avoid? If so, say and I'll drop it; that's a good reason.
Otherwise I'm happy to send the PR: default path byte-for-byte unchanged, the type, the
fail-closed timeout, and one worked example decider.
Disclosure: I build an authorization service for agents, so an external PDP is
obviously useful to me. I've tried to write this as it would stand without that —
push back if it reads otherwise.
- 主要語言
- TypeScript
- 星號
- 7.1k
- 分支
- 962
- 平均合併
- 20 小時 42 分鐘
- 30 天內合併 PR
- 135
環境準備
- 提供 Dockerfile 或 Docker Compose 檔案
- 有 Pull Request 範本
- 沒有貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
CopilotKit/OpenBot 的其他 Issue
-
難度 2/5 1-3 小時 新手友好度 78/100
CopilotKit/OpenBot#817 · 1 則留言 ·
維護者通常 1 天內回覆
-
Telemetry scrubber: case-insensitive mysql rule redacts an attached port (-P3306) and lets a real password survive in the same command可能已有人在做 關聯的 PR 仍在進行中或已合併。 未關閉
難度 2/5 1-3 小時 新手友好度 82/100
CopilotKit/OpenBot#815 ·
維護者通常 1 天內回覆
-
Wrong key location at startup可能已有人在做 @charan-rathore 於 8 天前認領。 未關閉
難度 1/5 1 小時以內 新手友好度 68/100
CopilotKit/OpenBot#800 ·
維護者通常 1 天內回覆
-
setup-learning.ts fails on Windows with "spawn npx.cmd EINVAL"可能已有人在做 @Caldas 於 11 天前認領。 未關閉
難度 2/5 1-3 小時 新手友好度 80/100
CopilotKit/OpenBot#792 ·
維護者通常 1 天內回覆
-
123未關閉
難度 3/5 1-2 天 新手友好度 48/100
CopilotKit/OpenBot#835 ·
維護者通常 1 天內回覆
查看 CopilotKit/OpenBot 的全部 Issue
相似的 Issue
-
難度 2/5 1-3 小時 新手友好度 76/100
openremote/openremote#3487 · 1 則留言 ·
-
難度 2/5 1-3 小時 新手友好度 82/100
coollabsio/jean#867 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 76/100
ehmpathy/rhachet-roles-bhuild#616 ·
維護者通常 1 天內回覆
-
📘 Docs supabase/cli
難度 2/5 1-3 小時 新手友好度 83/100
維護者通常 1 天內回覆
-
bug(cli): theme file home-directory check uses startsWith (sibling dirs pass, Windows casing fails)未關閉area/core kind/bug priority/p2 status/bot-triaged
難度 2/5 1-3 小時 新手友好度 79/100
google-gemini/gemini-cli#29955 · 2 則留言 ·
維護者通常 1 天內回覆