You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Tracking Issue — umbrella for exposing the in-tree security enforcement,
reporting, and incident-response surface behind a single trait. Each
Definition-of-Done item is required for delivery and lands as its own scoped
PR. Discussion happens here.
ZeroClaw's security enforcement, audit reporting, and incident-response logic
are implemented as concrete in-tree types with no pluggable boundary. The
decision logic that gates command and tool execution, the audit trail that
records those executions, and the incident-response engine are each callable
only as fixed implementations. There is no trait a third party (or an alternate
in-tree implementation) can satisfy to participate in these decisions or receive
these events.
This is inconsistent with the rest of the codebase, where capabilities are
trait-driven and selected from a registry (channels, providers, storage, and the
OS-isolation Sandbox trait already follow this pattern). The security layer is
the notable exception: it is hardcoded at every call site.
Exposing this surface behind a single, stable interface lets the runtime select
an implementation, lets alternate implementations be supplied without forking,
and gives the enforcement, reporting, and incident-response responsibilities one
cohesive seam instead of being scattered across unrelated call sites.
2. Goals
Define one trait that abstracts the security enforcement layer, mirroring the
existing Sandbox trait precedent (crates/zeroclaw-runtime/src/security/traits.rs).
Route the existing per-call-site enforcement entry points through the trait
rather than calling the concrete type directly.
Make the existing audit/reporting sink and incident-response engine reachable
through the same interface, so an implementation can decide, report, and
respond as one unit.
Keep the current behavior as the default implementation — zero behavior change
for existing deployments when no alternate is configured.
Registry is canonical in the backend crate; surfaces enumerate it via RPC (no
hardcoded implementation lists in any surface).
3. Non-Goals
Changing what the default implementation decides, records, or responds with.
This issue is about exposing the existing surface as an interface, not about
altering its behavior.
Replacing the OS-isolation Sandbox trait (it already exists; this composes
with it).
Network protocol or transport work (covered separately).
4. Surface To Expose
The following in-tree code is currently concrete and must become reachable
through the interface. This section names the source, not the semantics.
Enforcement entry points (the decision seam):
SecurityPolicy::validate_command_execution — called from tools/shell.rs, tools/cron_run.rs, tools/cron_add.rs, tools/skill_tool.rs, cron/mod.rs.
SecurityPolicy::enforce_tool_operation — called from tools/model_switch.rs, tools/delegate.rs, tools/verifiable_intent.rs.
SecurityPolicy::is_command_allowed, command_risk_level, is_path_allowed / is_resolved_path_{readable,allowed}, is_tool_allowed
— the decision primitives these entry points compose.
SecurityPolicy::ensure_no_escalation_beyond — the inheritance invariant that
must continue to hold across the interface boundary.
Reporting sink (the report seam):
security/audit.rs — AuditLogger, AuditEvent, AuditEventType. Already
the tamper-evident (hash-chained) record of command/tool executions and
security events. Currently invoked separately from the decision path.
Incident-response engine (the respond seam):
security/playbook.rs — the incident-response engine (Playbook, PlaybookStep, builtin_playbooks).
security/vulnerability.rs, security/leak_detector.rs, security/prompt_guard.rs — existing finding/detection sources that feed
incidents.
Construction pipeline (the source of the materialized object):
SecurityPolicy::from_profiles / for_agent — builds the per-agent
enforced object from its inputs. The interface must be able to supply or wrap
what this produces.
5. Design Overview
Introduce a trait in crates/zeroclaw-runtime/src/security/ alongside the
existing Sandbox trait. It carries three responsibilities so an implementation
participates as one cohesive unit:
Decide — evaluate a command/tool invocation and return an
allow / deny / require-approval decision plus the risk classification. The
per-call-site enforcement entry points (§4) call the trait instead of the
concrete type.
Report — receive a structured record of every evaluated invocation
(reusing AuditEvent as the canonical record shape) and forward it to its
sink. The default forwards to the existing hash-chained AuditLogger.
Respond — on a qualifying signal, raise an incident through the existing
playbook engine (or an alternate).
The default implementation wraps the current concrete types verbatim
(SecurityPolicy + AuditLogger + playbook.rs), so behavior is unchanged
when nothing else is configured. The trait + registry follow the Sandbox
precedent: name(), availability, and selection at construction time.
Selection is per agent (resolved in for_agent) and, where an authenticated
principal is in scope (#7141), per principal.
6. Invariants
These must hold regardless of which implementation is active:
Deny-by-default. An unknown or unresolved decision is a denial.
No escalation.ensure_no_escalation_beyond semantics survive the
interface boundary; an implementation can only narrow, never widen, an
inherited context (SubAgent spawn).
Per-agent context. There is no global security context; resolution stays
per agent.
The default decision primitives remain authoritative unless an
implementation explicitly owns the decision, and even then the escalation
and deny-by-default invariants are enforced by the runtime, not the
implementation. An alternate implementation may further restrict but cannot
loosen the default guarantees.
7. Risk and Rollback
The decision primitives (command lexer, path confinement) are security-critical
and heavily tested. Routing them through a trait must not bypass or weaken
them; the default implementation calls the exact same code paths.
Rollback: the interface is opt-in; with no alternate configured the default
implementation is byte-for-byte the current behavior. A misconfiguration fails
closed (deny / refuse), never open.
Breaking change: No — additive interface; default preserves current
behavior; existing configs unaffected.
8. Definition of Done (all required)
Every box is a hard gate. None optional. Each maps to one or more scoped PRs.
Interface
Trait defined in security/ mirroring the Sandbox precedent
(name(), availability, selection); registry canonical in the backend crate.
Trait spans decide / report / respond as one cohesive interface.
Surfaces enumerate available implementations via RPC (no hardcoded lists).
Decision seam
validate_command_execution call sites (shell, cron_run, cron_add, skill_tool, cron/mod) route through the trait.
enforce_tool_operation call sites (model_switch, delegate, verifiable_intent) route through the trait.
Decision primitives (is_command_allowed, command_risk_level,
path checks, is_tool_allowed) reachable through the interface.
ensure_no_escalation_beyond enforced at the boundary, not inside any
single implementation.
Report seam
Every evaluated invocation produces a structured record (reusing AuditEvent) delivered through the interface to its sink.
Default implementation forwards to the existing hash-chained AuditLogger
with no change to the on-disk trail format.
Respond seam
Qualifying signals raise an incident through the existing playbook engine
via the interface.
Existing finding sources (vulnerability, leak_detector, prompt_guard) feed incidents through the interface.
Construction + selection
from_profiles / for_agent produce the default implementation; the
interface can supply or wrap the materialized object.
Tracking: Pluggable security provider interface
Target v0.9.0 · Type: Security / Architecture · Tracking Issue
Revision History
SandboxtraitTable of Contents
1. Motivation
ZeroClaw's security enforcement, audit reporting, and incident-response logic
are implemented as concrete in-tree types with no pluggable boundary. The
decision logic that gates command and tool execution, the audit trail that
records those executions, and the incident-response engine are each callable
only as fixed implementations. There is no trait a third party (or an alternate
in-tree implementation) can satisfy to participate in these decisions or receive
these events.
This is inconsistent with the rest of the codebase, where capabilities are
trait-driven and selected from a registry (channels, providers, storage, and the
OS-isolation
Sandboxtrait already follow this pattern). The security layer isthe notable exception: it is hardcoded at every call site.
Exposing this surface behind a single, stable interface lets the runtime select
an implementation, lets alternate implementations be supplied without forking,
and gives the enforcement, reporting, and incident-response responsibilities one
cohesive seam instead of being scattered across unrelated call sites.
2. Goals
existing
Sandboxtrait precedent (crates/zeroclaw-runtime/src/security/traits.rs).rather than calling the concrete type directly.
through the same interface, so an implementation can decide, report, and
respond as one unit.
for existing deployments when no alternate is configured.
authenticated principal — see [Feature]: OIDC Authentication Provider support for the RPC/WSS transport #7141).
hardcoded implementation lists in any surface).
3. Non-Goals
This issue is about exposing the existing surface as an interface, not about
altering its behavior.
Sandboxtrait (it already exists; this composeswith it).
4. Surface To Expose
The following in-tree code is currently concrete and must become reachable
through the interface. This section names the source, not the semantics.
Enforcement entry points (the decision seam):
SecurityPolicy::validate_command_execution— called fromtools/shell.rs,tools/cron_run.rs,tools/cron_add.rs,tools/skill_tool.rs,cron/mod.rs.SecurityPolicy::enforce_tool_operation— called fromtools/model_switch.rs,tools/delegate.rs,tools/verifiable_intent.rs.SecurityPolicy::is_command_allowed,command_risk_level,is_path_allowed/is_resolved_path_{readable,allowed},is_tool_allowed— the decision primitives these entry points compose.
SecurityPolicy::ensure_no_escalation_beyond— the inheritance invariant thatmust continue to hold across the interface boundary.
Reporting sink (the report seam):
security/audit.rs—AuditLogger,AuditEvent,AuditEventType. Alreadythe tamper-evident (hash-chained) record of command/tool executions and
security events. Currently invoked separately from the decision path.
Incident-response engine (the respond seam):
security/playbook.rs— the incident-response engine (Playbook,PlaybookStep,builtin_playbooks).security/vulnerability.rs,security/leak_detector.rs,security/prompt_guard.rs— existing finding/detection sources that feedincidents.
Construction pipeline (the source of the materialized object):
SecurityPolicy::from_profiles/for_agent— builds the per-agentenforced object from its inputs. The interface must be able to supply or wrap
what this produces.
5. Design Overview
Introduce a trait in
crates/zeroclaw-runtime/src/security/alongside theexisting
Sandboxtrait. It carries three responsibilities so an implementationparticipates as one cohesive unit:
allow / deny / require-approval decision plus the risk classification. The
per-call-site enforcement entry points (§4) call the trait instead of the
concrete type.
(reusing
AuditEventas the canonical record shape) and forward it to itssink. The default forwards to the existing hash-chained
AuditLogger.playbook engine (or an alternate).
The default implementation wraps the current concrete types verbatim
(
SecurityPolicy+AuditLogger+playbook.rs), so behavior is unchangedwhen nothing else is configured. The trait + registry follow the
Sandboxprecedent:
name(), availability, and selection at construction time.Selection is per agent (resolved in
for_agent) and, where an authenticatedprincipal is in scope (#7141), per principal.
6. Invariants
These must hold regardless of which implementation is active:
ensure_no_escalation_beyondsemantics survive theinterface boundary; an implementation can only narrow, never widen, an
inherited context (SubAgent spawn).
per agent.
implementation explicitly owns the decision, and even then the escalation
and deny-by-default invariants are enforced by the runtime, not the
implementation. An alternate implementation may further restrict but cannot
loosen the default guarantees.
7. Risk and Rollback
and heavily tested. Routing them through a trait must not bypass or weaken
them; the default implementation calls the exact same code paths.
implementation is byte-for-byte the current behavior. A misconfiguration fails
closed (deny / refuse), never open.
behavior; existing configs unaffected.
8. Definition of Done (all required)
Every box is a hard gate. None optional. Each maps to one or more scoped PRs.
Interface
security/mirroring theSandboxprecedent(
name(), availability, selection); registry canonical in the backend crate.Decision seam
validate_command_executioncall sites (shell,cron_run,cron_add,skill_tool,cron/mod) route through the trait.enforce_tool_operationcall sites (model_switch,delegate,verifiable_intent) route through the trait.is_command_allowed,command_risk_level,path checks,
is_tool_allowed) reachable through the interface.ensure_no_escalation_beyondenforced at the boundary, not inside anysingle implementation.
Report seam
AuditEvent) delivered through the interface to its sink.AuditLoggerwith no change to the on-disk trail format.
Respond seam
via the interface.
vulnerability,leak_detector,prompt_guard) feed incidents through the interface.Construction + selection
from_profiles/for_agentproduce the default implementation; theinterface can supply or wrap the materialized object.
invalid selection.
Default-preserves-behavior
are identical to today (characterization tests pin this).
Documentation — REQUIRED for delivery (FND-002)
implementation, and how an alternate is selected.
(decide + report + respond) and how to register and select it.
principal where applicable).
Validation
every routed call site.
regardless of implementation.
integrity preserved for the default sink.
interface.
cargo fmt --all -- --check,cargo clippy --all-targets -- -D warnings,cargo testgreen; docs lint + link integrity for new pages.9. Delivery Sequencing
Each item is a scoped PR; order minimizes risk.
concrete types (no call-site changes yet; pure addition).
pin parity).
AuditLogger).before close.
Data hygiene