Selena runs tools — including, if you enable it, arbitrary shell commands — on your machine, driven by a model's decisions. This document is an honest account of what Selena enforces, what it only labels, and where the real risks are. Read it before enabling bash or trusting external tools.
The short version: capability labels are advisory, not OS-enforced. bash and auto_accept: true together let a model run any command on your machine without asking. Optional environment sandboxing (sandbox.enabled) hides your environment secrets from tools, but there is no kernel sandbox yet — a tool can still reach the network and the filesystem.
These are real, code-backed controls:
- Confirmation gating. A tool with
require_confirmation: trueis blocked whenauto_accept: false. The execution does not happen; the model receives an error. - Trust + integrity checking.
selena tools trust <name>records a combined SHA-256 hash of the tool's manifest and its script. On every startup, the current files are re-hashed; if either changed since trust was granted, trust is automatically revoked and the revocation persisted. A tampered script cannot silently run as "trusted." - Per-tool timeouts and output caps. Every command tool has a timeout and stdout/stderr byte caps; processes are killed (by process group on Unix) on timeout or cancellation.
- Environment isolation (opt-in). With
sandbox.enabled, subprocess tools do not inherit the parent's full environment. They receive only an allowlist (sandbox.env_allowlist, with cross-platform defaults) plus their own declaredenv, so API keys and other secrets in your environment are not exposed to tools. This is real and enforced — but it is environment isolation, not filesystem/network isolation. - Audit logging. Every command-tool execution is appended to
.logs/tools/audit.jsonl(see below).
- Capability labels (
network,filesystem,execute) are advisory. They categorise risk, drive warnings, and are recorded in the audit log — but they impose no OS-level restriction. A tool labellednetwork: falsecan still open a socket. A tool labelledfilesystem: falsecan still read and write files. Nothing stops it. - There is no kernel sandbox. Beyond the optional environment isolation above, tools run as ordinary subprocesses with the same privileges as the Selena process. There is no namespace isolation, no seccomp, no container, no WASI boundary — so filesystem and network access are not contained. (Kernel enforcement paths are sketched in
crates/selena-core/src/tools/security.rsand tracked on the roadmap, but are not implemented.) - Trust is not a safety guarantee. It detects changes since you reviewed a tool. It does not analyse what the tool does. Trusting a malicious tool runs malicious code.
Enabling bash gives the model the ability to run any shell command your user account can run. It is categorised execute + filesystem and emits a startup note to that effect.
Combined with auto_accept: true (the shipped default is false), the model can run commands without any confirmation. If you don't fully trust the model or the inputs it sees, do not enable bash with auto_accept: true.
To require a gate, set auto_accept: false. Note: built-in tools (including bash) are registered as trusted with require_confirmation: false, so auto_accept: false does not by itself gate bash — it only blocks custom tools that set require_confirmation: true. To gate bash, use the permission block: "permission": { "bash": "ask", "interactive": true } prompts the operator per call (the CLI installs a stdin Allow/Deny handler), and "bash": "deny" blocks it outright. Or don't register it at all.
| Value | Effect |
|---|---|
true |
All tools run without confirmation. |
false (default) |
Tools with require_confirmation: true are blocked and return an error to the model. Tools without that flag (all built-ins, and any custom tool that doesn't set it) still run. |
auto_accept: false is a hard block of confirmation-required tools, not a prompt. If you want an interactive "approve this call?" prompt, that exists separately: set a tool to "ask" in the permission block and enable permission.interactive — the CLI then prompts ⚠ approve run '<tool>' on '<subject>'? [y/N] mid-turn. Without an interactive handler, ask falls back to honouring auto_accept.
Rather than wiring auto_accept and per-tool ask rules by hand, set a preset with permission.mode (see CONFIG.md):
| Mode | Behaviour |
|---|---|
default |
Ask before editing files or running commands. |
auto_accept |
Auto-edit files; ask before commands. |
plan |
Read-only — deny all edits and commands. |
auto |
Autonomous — allow everything, including confirmation-required tools. This is the bash + run-anything combination; use only when you trust the model and its inputs. |
custom (default) |
Your hand-written rules; honours the legacy auto_accept. |
The CLI switches modes live with /mode <name>. auto mode is the explicit, named version of the old bash + auto_accept: true risk called out above.
When you trust a tool:
- Selena reads the manifest JSON and the referenced script.
- It computes
SHA-256(manifest || 0x00 || script)and stores it in.selena/trusted_tools.jsonunder the tool name.
On startup, for each trusted, discovered tool:
- The current manifest + script are re-hashed.
- If the hash differs, Selena logs a warning, moves the tool to the untrusted set, and saves the file.
This means: edit a trusted tool's script, and it is untrusted again on the next run until you re-review and re-trust it.
selena tools trust-status shows, per tool, the trust state and whether the stored hash still matches (ok, no stored hash, or MISMATCH).
Every command-tool execution writes a JSON line to .logs/tools/audit.jsonl containing:
- tool name and a hash of the arguments (arguments themselves are not logged verbatim),
- duration, exit code, whether it timed out or was cancelled,
- the tool's trusted flag and capability labels,
- how stdout was parsed.
Set runtime.audit_max_bytes to rotate the log when it grows past a threshold. Built-in tools (bash, read_file, etc.) do not currently write to this audit log — it covers command tools.
debug.dump_provider_payloads: true writes raw request/response JSON to .logs/provider/. These payloads contain your prompts, tool outputs, and model responses in clear text. Selena's providers carry no auth tokens in the body, but the dumped content is sensitive — treat .logs/ as confidential and do not commit it.
- Don't enable
bashunless you need arbitrary command execution. Prefer narrow custom tools with specific commands. - Keep
auto_accept: falseand setrequire_confirmation: trueon any custom tool that touches the network or filesystem — but remember this blocks, it does not prompt. For an actual prompt, usepermission: { "<tool>": "ask", "interactive": true }. - Review and
trustevery custom tool; rely on the hash-integrity check to catch later edits. - Run Selena as an unprivileged user. Capability labels will not contain a tool — your OS user account is the actual boundary.
- Add
.logs/and.selena/to.gitignore. - Until OS-level sandboxing lands, treat every enabled tool as code you are choosing to run.