Skip to content

Governance layer: per-agent permission matrix, OS sandbox profiles, audit trail #262

Closed

Description

@currentbits

Inspired by opencode's agent frontmatter (per-bash-command glob permissions, permission.task subagent control), Roo Code's mode tool-groups with file-path write regexes, Copilot CLI's /sandbox policy UI, and opcode's OS-level sandboxing (Seatbelt/seccomp).

Beyond #190 (profiles as presets): a declarative per-agent/per-profile matrix of allowed tools, bash command globs, file-write path scopes, and network policy, enforced via OS sandbox where possible (macOS Seatbelt), with every allowed/denied action written to an audit log. gh-aw's safe-outputs pattern (agent is read-only; mutations pass through a declared, capped proxy) is the stretch goal.

Activity

  1. currentbits commented on Aug 17, 2026

    @currentbits
    OwnerAuthor

    Research notes to fold into the design: (1) Kiro blocking hooks: PreToolUse/UserPromptSubmit hooks that can veto execution, with a confirmCommand whose stdout dynamically decides what to ask. (2) Coder's Agent Firewall, local edition: per-worktree network egress allowlist so agents cannot exfiltrate via curl. (3) Continue's Checks: PR policies as plain-Markdown rules enforced by headless agents in CI. (4) Goose's continuous adversary reviewer watching agent actions for unsafe behavior, not just gating permission prompts.

  2. currentbits commented on Aug 17, 2026

    @currentbits
    OwnerAuthor

    Research note (OpenClaw failure postmortems, worth encoding as invariants): (1) context compaction must never drop standing approvals/safety directives — an agent bulk-deleted 200+ emails after compaction dropped the directive; (2) hard per-task budget caps, see the budget-caps issue; (3) any community skills/plugins surface needs a vetting story — ClawHub shipped 1,184 malicious skills.

  3. currentbits commented on Aug 17, 2026

    @currentbits
    OwnerAuthor

    Research note (Governance Decay paper, arXiv 2606.22528): compaction silently drops in-context policy constraints — violation rate 0% to 30%+ after compaction, and adversarial compaction-eviction defeats all tested models. Mitigation is constraint pinning: enforce policy in the harness (tool-call interception), never in prompts, and re-inject a pinned policy block verbatim at every turn boundary. Also from Snyk Evo's Policy Agent: natural-language policy compiled to executable gates ('never push to main, require approval above ') as the authoring UX for this layer.

  4. currentbits commented on Aug 18, 2026

    @currentbits
    OwnerAuthor

    Deep-dive (Zed tool-permission engine): the precedence stack to adopt — built-in security rules > always_deny > always_confirm > always_allow > tool default > global default, with per-tool regex patterns (command/path/URL/query), case-insensitive by default, chained-command parsing across all shells (echo x && rm y checked per subcommand), non-overridable rm -rf / ~ /Users/willem rules, and a UI that writes config from the prompt ('Always for tool' vs 'Always for pattern'). Sandbox notes: .git never writable, create_directory cleaned up on deny, and the symlink-swap TOCTOU — resolve paths and fail closed between approval and sandbox invocation. Their explicit lesson: rules-based filtering is useless against a motivated agent; sandbox is not rules.

  5. currentbits commented on Aug 18, 2026

    @currentbits
    OwnerAuthor

    Round 18 intel from Devin Local (changelog 2026-07-29), their permission model matured in ways that map directly onto this governance layer:

    • Layered rules compose predictably: enterprise, mode, user, project, and subagent levels all contribute rules; an explicit deny: always wins regardless of level.
    • Denial attribution: a denied action says which layer denied it. Without this, layered policy is undebuggable.
    • Grant visibility: "always allow" options only appear when the grant would actually take effect (i.e. no higher layer denies it). Prevents users collecting useless grants.
    • Session-wide subagent grants: a permission answered on a subagent's prompt applies to the whole session, so the root agent and sibling subagents do not re-prompt for an already-granted scope.
    • Tool-level symlink refusal: their edit/write/apply_patch tools refuse to write through symlinks, so an approved edit cannot be redirected to an unexpected file (fixed 2026-08-01). Worth checking each Solenta adapter's behavior here.

    Also filed this round: #509 (edit command before approve, interacts with allow-always keying) and #510 (CI/workflow diffs need hard human sign-off; Wiz/Snowflake incident).

  6. currentbits commented on Aug 18, 2026

    @currentbits
    OwnerAuthor

    Round 19 intel, three Cursor items plus a CVE:

    • Team MCP distribution (2026-06-30): admins configure MCP servers once and fan them out to cloud agents, the agents window, IDE, and CLI through team marketplaces; marketplace access restrictable by org groups. This is the distribution half of the governance layer: policy decides what is allowed, marketplace decides what is present.
    • Unified Customize page (2026-06-22): plugins, skills, MCPs, subagents, rules, commands, and hooks managed in one surface at user/team/workspace levels. The scoping model (three levels, explicit precedence) is the same shape as Devin's layered permissions (see earlier comment): the industry has converged on user < team < workspace layering with deny-wins.
    • CVE-2026-50548 / CVE-2026-50549 (Cursor <3.0, CVSS 9.8): sandbox write confinement escaped because path canonicalization failed open; when canonicalization errored, the write went through outside the workspace. Rule for every Solenta sandbox/adapter path check: canonicalization failure must deny, never fall through. Worth an audit line in this issue's checklist.
  7. currentbits commented on Aug 18, 2026

    @currentbits
    OwnerAuthor

    Round 22, three governance principles from Claude Code's latest hardening (v2.1.220-224):

    1. Checked-in config can restrict but never expand privilege: a repository's checked-in settings can no longer turn ON Remote Control auto-connect; only user or managed settings can enable it, and project/local settings can only turn it OFF. Any repo-controlled knob (CLAUDE.md, .solenta/, WORKFLOW.md) must be unable to grant itself powers the user did not personally approve. This belongs in the audit checklist next to Install-time scanning for MCP servers, skills, and agent-added dependencies #305.
    2. Internal side tasks do not inherit user bypasses: PreToolUse auto-allow hooks no longer apply to Claude Code's internal side tasks (summaries, compaction). If Solenta ever runs background summarization/title-generation/labeling with tool access, those runs get a fresh minimal permission scope, not the session's grants.
    3. Normalize before matching: see the Allow-always is a command prefix, not the whole tool #479 comment on invisible-Unicode padding; the governance layer should centralize command normalization so every matcher (prefix lists, deny rules, audit log) sees the same canonical form.
  8. currentbits commented on Aug 18, 2026

    @currentbits
    OwnerAuthor

    Round 23 intel: Factory Droid's commandBlocklist setting (v0.147.0) fills the missing layer in the permission stack: commands that can never run, even under full autonomy or skipped approvals. This is distinct from deny rules (which live inside the review/approve flow) and from sandboxing (which contains, not forbids). The blocklist is the absolute floor an org or user sets once: think rm -rf /, git push --force to protected branches, kubectl delete against prod contexts. For Solenta's matrix: three enforcement tiers make sense: blocklist (never, not reviewable), deny (visible rejection with reason), ask (default). Also from Droid: per-MCP-server risk levels and a quick mcp off shortcut (v0.138.0), and org-central governance of MCP/marketplace access (v0.152.0).

  9. currentbits commented on Aug 18, 2026

    @currentbits
    OwnerAuthor

    Round 29 intel: bernstein (bernstein.run) ships an HMAC-signed, chained audit log for orchestration events, so the audit trail is tamper-evident rather than just append-only text. That is the right integrity bar for this issue's audit trail: each event references the previous event's hash, and verification is a local offline check. Also from bernstein: their scheduler is deterministic plain Python by design ("just don't put the LLM in the scheduler tick") with LLM-assisted parts (routing, best-of-N judge) explicitly opt-in and pluggable. A clean separation worth copying: deterministic core loop that is replayable and auditable, LLM judgment only at declared decision points.

  10. currentbits commented on Aug 30, 2026

    @currentbits
    OwnerAuthor

    Mirroring opencode, Roo and Copilot CLI is a solid baseline, but an agent that already holds a permitted shell scope can emit a denial record that never blocked anything, so the audit log's credibility depends on enforcement being separable from reporting. Worth flagging: the Seatbelt profile and the declared permission matrix are two sources of truth that can diverge, and the audit entry should record which one made the call when they disagree, otherwise you cannot debug a pass that the sandbox vetoed. For the proxy stretch goal, do you plan to capture the agent's declared intent from the pre-approval step alongside the actual executed arguments, so the trail can prove what was promised matched what ran? I work on a control-and-evidence layer, and that declared-versus-executed seam is the exact gap I care about.

    Good questions, and they land on seams the design notes already worry about, so folding the answers in here.

    On enforcement versus reporting: agreed, and that separation is a hard requirement, not a nice-to-have. The audit writer must live in the enforcing component, not in anything the agent can influence. Today Solenta only reports what flags it passes (the sandbox badge in the UI is pure reporting, which is exactly the credibility gap you describe). The plan is harness-level interception for tool calls plus OS sandbox for confinement, with the log appended by that layer, chained and tamper-evident per the bernstein pattern noted above. A denial record is written only by the thing that actually denied.

    On the two sources of truth: yes, and this is already captured as the attribution requirement (a denied action must say which layer denied it, per the Devin note earlier in this thread). Your version is the sharper framing though: attribution has to cover disagreements in both directions, including a pass the matrix granted that Seatbelt then vetoed. Adding "record the deciding layer on every entry, allow and deny alike" to the checklist.

    On declared versus executed for the proxy: yes, both sides go in the log. Declared intent is captured at the approval step (the exact command or mutation as presented and approved), executed arguments at dispatch, and the entry links the two. This is what makes the TOCTOU rules auditable after the fact: symlink-swap and edit-before-approve (#509) only stay honest if you can diff what was promised against what ran. Approval covers a specific declared action hash, not a scope; anything that deviates is a fresh decision, fail closed.

  11. added and removed on Sep 2, 2026
  12. currentbits commented on Sep 2, 2026

    @currentbits
    OwnerAuthor

    v1 slice (this thread): policy matrix + harness gate + HMAC audit + profile UI.

    Seatbelt OS sandbox and gh-aw safe-outputs stay follow-ups. Enforcement will extend the existing #409 classifyTool seam on Claude can_use_tool (electron/guardrails.js + electron/runner.js). electron/sandbox.js remains reporting-only in this slice.

    Design in progress on thread 36cfee86-4dc4-4d12-8917-2c1f5568a4bc.

  13. 11 remaining items

  14. currentbits commented on Oct 16, 2026

    @currentbits
    OwnerAuthor

    v1 implementation is up as PR #1655. It contains the matrix, the Claude gate with a re-check when the card is answered, the HMAC audit, and the profile UI. On a CI-like env, test:electron is 4825 pass / 0 fail and the renderer suite is 3103 / 0. Two deviations from the spec are listed in the PR body:

    • Bash rules match word prefixes per subcommand.
    • Governed threads don't send Claude session rules, which would bypass the matrix.

    #803 (Seatbelt) is next, stacked on this branch.

  15. added and removed on Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions