Security Limitations — What We Do Not (Yet) Enforce
PersonalClaw’s strongest claim is that its controls are enforced at the point of execution, not merely requested in a prompt. Honesty requires naming the places where that is not (yet) literally true. These are deliberate, documented tradeoffs — not oversights — and each is stated here in the same terms the internal architecture uses, without softening.
This page is referenced from the public
threat model and from SECURITY.md. Verified against the
codebase at the commit that introduced this file.
1. ACP agents under auto-approve (YOLO) rely on system-prompt framing, not rails
Section titled “1. ACP agents under auto-approve (YOLO) rely on system-prompt framing, not rails”Task modes (agent / ask / plan / build) decide which tools may run. For
the native runtime, this gate is hard-enforced: task_modes.py is enforced
in _guard_and_invoke before approval is consulted, so a Trust/YOLO
auto-approve can never bypass a task-mode restriction
(src/personalclaw/task_modes.py).
For ACP agents (external CLI agents driven over the Agent Client Protocol),
the same module is applied in the dashboard’s permission handler as
“belt-and-suspenders for ACP runtimes that gate via their own protocol path”
(task_modes.py). But an ACP agent running under YOLO ultimately gates through
its own protocol path, and the architecture states the tradeoff plainly:
Task-mode tool-gating postures are hard-enforced at the permission prompt for the native runtime; ACP agents under YOLO rely on system-prompt framing (a documented tradeoff —
task_modes.py). —docs/architecture/security.md
What this means for you: if you enable auto-approve (YOLO) and run an external ACP agent, that agent’s tool use is bounded by prompt framing rather than by the same hard rail the native runtime enforces. Running a trusted native agent, or leaving approval prompts on, keeps the hard rail in force.
2. The app network permission is declaration-only
Section titled “2. The app network permission is declaration-only”An app manifest declares a permission scope (api / events / mcpTools /
storage / network / memory / cron). Most of these are enforced
server-side by the gateway. network is not, by design:
can_use_network— DECLARATION-ONLY (unenforced by design). An app backend is an isolated subprocess with its own OS-level network stack; there is no in-process egress hook the gateway can intercept. The flag records INTENT so the Store can surface it (install consent lists “network access: yes/no”) and a future OS-level isolation layer (cgroups/nftables/seccomp) can enforce it. Every gateway-MEDIATED reach is already bounded bycan_use_api— anetwork:falseapp can still reach the internet through its own subprocess … Until then, treatnetwork: trueas an honest declaration, not a security boundary. —src/personalclaw/apps/permissions.py
The consent surface is honest about this: at install time the Store lists the
app’s network declaration, and an app’s gateway-mediated reach is separately
bounded by its api permission. What is not enforced is an app’s own
outbound traffic from its own process.
What this means for you: treat an installed app’s network: true as a stated
intent you are consenting to, the same way you would trust any program you choose
to run — not as a sandbox that prevents the app from talking to the network. The
supply-chain scanner (quarantine → scan → consent → install, with dangerous
terminal) is the control that vets what you install; the network flag is
disclosure, not containment.
Why these are listed, not fixed
Section titled “Why these are listed, not fixed”Per the project’s lifecycle discipline, a control gap discovered while writing documentation is recorded as a candidate for the security-hardening track — never patched inline in a docs change. Both items above have a named future direction (OS-level app isolation for #2; extending the hard rail to ACP protocol paths for #1). This page will shrink as those land.