mirror of
https://github.com/qwibitai/nanoclaw.git
synced 2026-07-09 18:57:08 +08:00
76984ef395
Every privileged action crossing the container or channel boundary now passes one decision function — guard() in the new src/guard/ leaf — before it executes: allow | hold | deny (hub: engineering/requirements/guarded-actions + engineering/discovery/guarded-actions-decisions, phase 2). - Registration-derived catalog: registry.register() derives one entry per ncl command (dotted action names stamped by registerResource); module-edge guard.ts adapters register the domain baselines (agents.create, a2a.send incl. the agent_message_policies hold, self_mod.*, senders.admit, channels.register). The baseline is the decision — policy-as-data (tighten-only rules) is deferred to phase 3. - All four handler registries wrap at registration: dispatch consults the guard; guarded delivery actions (create_agent, install_packages, add_mcp_server) store only the precheck → guard → handler wrapper (the raw handler is never stored; spec-less re-registration throws); response handlers + message interceptors take guard specs (channel registration's click + free-text name capture). - Grant-carrying replay: approved continuations re-enter their entry point with the verified approval row as the grant (ApprovalHandlerContext.approval). A grant satisfies a hold, never a deny — live-row + approvalAction + grantMatches checks; the forgeable approved:true boolean is deleted. - Boot conformance: the registry walk (src/guard-conformance.ts) runs in CI and at boot — an unmapped privileged registration stops the host with a banner (skill-installed code never runs this repo's CI). Baselines are main's behavior verbatim (host trusted-caller, cli_scope allowlist, create_agent scope branch, a2a ACL order, unconditional self-mod hold, unknown_sender_policy, channel click auth incl. the anchor-group approver). Sender/channel holds stay on their own tables; guard holds map onto the existing requestApproval options (approverUserId). Deliberate outcome changes inherent to the replay semantics (called out in CHANGELOG too): (a) a2a approve-then-revoke no longer delivers — the structural baseline re-runs live on replay; (b) forged, already-consumed, or mismatched grants refuse instead of executing; (c) the channel-name free-text reply re-checks approver eligibility at reply time. The D1/D2/D4 click-auth fixes are deliberately NOT here — they belong to the approval-contract PR (D2's host fallback at dispatch replay is preserved verbatim). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>