-
Notifications
You must be signed in to change notification settings - Fork 7
Releases: ginkida/rustyhand
v0.7.84 — Usability & discoverability sweep
Usability & discoverability sweep — make what RustyHand can do visible at a glance, smooth out first contact, and stop a partial-update data-loss bug. 11 commits since v0.7.83. 1,728 tests, zero clippy warnings.
New dashboard surfaces
- Capabilities page — one searchable view of what this can do: the 72 built-in tools grouped by category, the agent templates, and the installed skills (with source badges). Backed by
GET /api/tools//api/templates//api/skills. - Security page — the live runtime guardrail posture, not static feature flags: exec mode (
fullflagged amber = any shell command), taint checking, auto-approve, the require-approval list, and per-channel gating (⚠ open to anyoneflagged). Trust-by-default (v0.7.75+) is permissive — the page shows that honestly instead of implying a lockdown. - CLI:
rustyhand tools [--json]andrustyhand agent templates [--json]make the same capability catalog discoverable from the terminal.
First-contact UX
doctor --repairnow writes a validanthropic/claude-sonnet-4-6config (was a deadgroqconfig); Docker default model corrected toclaude-sonnet-4-6.- Missing-API-key errors link the exact provider console; permission-denied tool errors tell you to grant the tool via
PATCH /api/agents/{id}/config. - Dashboard first-run: provider-aware "get a key" link (follows the selected provider), Chat empty-state CTA, and an honest demo-mode banner.
- Channel Connect dialog now surfaces the per-channel setup guidance (quick-setup line, numbered steps, and a "Get a token →" link to BotFather / the Discord developer portal / api.slack.com) instead of bare input boxes.
Channels
- Channel send failures map to human-readable causes — Telegram/Discord HTTP 401/403/404/429/5xx and Slack
ok:falseerror codes — so an operator sees why a send failed.
Honesty (no stubs pretending to work)
- WASM skills are now rejected at load time (with an actionable message) instead of registering tools that always fail when called.
describe_videofails fast with one honest message (no false "set a Gemini key" hint).media_describehonors its optionalpromptargument (was silently discarded); the tool description no longer claims "NOT YET IMPLEMENTED".
Fixes
PUT /api/agents/{id}/updateno longer silently clobbers omitted fields. The live-apply handler deserialized the body into a typed manifest whose container-level#[serde(default)]filled every omitted field with a non-empty default, defeating the value guards — so a partial PUT that set onlynamereset description/temperature/max_tokens/system_prompt/model/provider. Now it uses true PATCH semantics (apply only keys actually present in the submitted TOML). Regression test added.
All checks green: cargo build, cargo test --workspace (1,728), cargo clippy --all-targets -- -D warnings, cargo fmt --check.
Assets 14
- sha256:4c134801b8477df1e5e0d164af1256ec3fc258f039868cc76c9a353d7eeab15915.7 MB
2026年06月12日T13:30:52Z - sha256:81cbc932df5e2f489bdfb08e00a6f37aa9c3ee86380852b0314ef7f9f083377b104 Bytes
2026年06月12日T13:30:52Z - sha256:e51bbd3fd440a7965aa9078f40567044bac4ce6f3e95abfeb22bc4196b78f25816.5 MB
2026年06月12日T13:34:25Z - sha256:d279a7c583ee430e28b1ba909a85ddc661b5037efcbe9bc110abbf0c5582d40a105 Bytes
2026年06月12日T13:34:25Z - sha256:bfe0807b774f49f6acbbadfb84cd7dd41ef241dbac1583eefd709bb84a55c67816.1 MB
2026年06月12日T13:25:17Z - sha256:b541c08600f35408db3cc2e4b51f261c013d5bcfbb576d2f1a8b372ad2f11db7109 Bytes
2026年06月12日T13:25:17Z - sha256:22076bcc1e65659021bc06803aa6568784cb7c5e52c1a250cf4cbda010ec879d17.4 MB
2026年06月12日T13:26:55Z - sha256:19b6f52d8c5bc01542502637e9202618f2e14b6aca6e5178d411f41d3ab3af8f103 Bytes
2026年06月12日T13:26:55Z - sha256:1b25dd537146851121195798bb0518b0e2c11113fbb44105862a676fbe9b86c118.4 MB
2026年06月12日T13:33:28Z - sha256:b79a5815e517943b2b3df508ef8c4d10bb5073b6c2e337965216ef2de091f8fd104 Bytes
2026年06月12日T13:33:28Z -
2026年06月12日T13:13:08Z -
2026年06月12日T13:13:08Z - Loading
v0.7.83 — Elevation: capability completeness, channel UX, trust visibility
Elevation release: finishing wired-but-inert machinery and killing stubs that reported fake success, moving RustyHand toward the best-in-class agent-OS bar. Everything below ships verified — fmt + clippy --all-targets -D warnings clean, full workspace suite 0 failures.
New built-in tools (65 → 72)
Filesystem (agents stop shelling out to rm/mv/cp/mkdir)
| Tool | Purpose |
|---|---|
file_stat |
metadata (exists/type/size/mtime/readonly); missing path returns exists:false |
file_delete |
file or dir (recursive flag); refuses to delete the workspace root |
file_move |
move/rename, creates missing destination parents |
file_copy |
file or recursive directory tree |
file_mkdir |
mkdir -p within the workspace |
All routed through a new resolve_sandbox_path_allow_missing that enforces containment via the deepest existing canonical ancestor (nested mkdir/move/copy destinations can't escape the sandbox). Added to the Coding/Automation/Minimal tool profiles; file_stat is in the MCP safe-default allowlist.
Knowledge graph
knowledge_delete_entity/knowledge_delete_relation— the graph had add+query but no agent-facing delete.
Streaming & channels
- Tool-execution progress — new
StreamEvent::ToolExecutionProgress. Slow tools (shell/web/browser/builds) previously went silent for up to 120s between the call and its result; the streaming loop now emits an immediate marker then a heartbeat every 10s, wired into TUI, WebSocket, SSE, and the React dashboard (live elapsed counter). - Discord + Slack streaming — progressive message edits (Discord
PATCH, Slackchat.update), matching the existing TelegrameditMessageTextpath. The bridge already selected the streaming path generically offsupports_streaming().
Reliability & trust
- Extension/MCP hot-reload is actually applied — config reload built a
ReloadExtensions/ReloadMcpServersplan but only logged it. It now spawns the real reconnect (reload_extension_mcps); the remaining not-yet-wired actions honestly say "restart the daemon to apply". - Memory consolidation dedup —
ConsolidationReport.memories_mergedwas hardcoded0. Consolidation now collapses byte-identical memories within(agent_id, scope)to the highest-confidence survivor (soft-deletes losers, folds access_count/confidence/recency) and reports the real count. - Visible security posture —
GET /api/securitynow exposes the real runtime guardrails (exec_mode,taint_check_enabled, approval policy, per-channel allowlist gating) and fixedtaint_tracking.enabledwhich was hardcodedtruewhile the trust-by-default posture actually runs it off.
Image vision
describe_image(previously a hardErr(\"lands in a later release\")) is implemented —resolve_image_base64(base64 / file / SSRF-checked URL) feeds an Image+Text completion through both the Anthropic-wire and OpenAI-compat drivers.
Foundations (Phase 0)
- Streaming path now records metering/cost (the real gap — non-streaming already did).
ProviderCooldowncircuit breaker activated via a process-global (was constructed but passedNoneeverywhere).- TUI agents screen fixed (envelope shape
{agents,...}vs bare array). - Bundled the 7 missing on-disk agent templates + a drift-guard test.
- Onboarding now verifies a provider key (
POST /providers/{name}/test) after save.
Security & build
tar0.4.45 → 0.4.46 (advisory GHSA-3pv8-6f4r-ffg2).- Security posture endpoint no longer over-reports guardrails as enabled.
[profile.dev] debug = \"line-tables-only\"— keeps file:line panic backtraces while dropping heavy debuginfo; a full from-scratch build+test now sits at ~3.6 GB oftarget/instead of ballooning.
Stats
- 10 crates · 72 built-in tools · ~1718 tests passing · 0 clippy warnings.
Assets 13
v0.7.82 — Autonomous-by-default agents + live tool-grant
Agents that just work
This release removes the two main sources of friction operators hit with fresh agents: agents that asked too many questions, and agents that had most tools unavailable. Tuned for RustyHand's trust-by-default, single-operator posture.
Autonomous-by-default behavior
- System prompt (
prompt_builder.rs): the safety section now biases toward completing the task. Confirmation is required only for genuinely irreversible/high-stakes actions (real-money payments, account/data deletion, destructive ops). Removed the blanket "when in doubt, ask the user". - No more forced "what should I call you?" question on the first reply — the agent serves the request directly and stores the name only if volunteered.
- Generated identity files (
SOUL.md/AGENTS.md/BOOTSTRAP.md) tell new agents to act, assume-and-proceed on ambiguity, serve before onboarding, and never claim a capability is unavailable when the tool is in their list. - There is no runtime approval gate — tools are not blocked at runtime.
Tool access
- Dashboard spawn now defaults to the
fulltool profile (all 65 builtins). It previously defaulted toresearch(4 tools), which is why dashboard-spawned agents had "everything unavailable". - Live tool-grant for existing agents:
PATCH /api/agents/{id}/configacceptstools(["*"]= all, or a specific list). The kernel re-grants capabilities so the change takes effect on the next message with no respawn, and persists it. Implied network/shell/agent/memory caps are widened to match the new tools but never narrowed (a hand-set host allowlist is preserved). - Dashboard agent editor gains a Tools field + an "All tools (*)" button — open a stuck agent, click it, save.
Upgrade note
New agents are autonomous and fully-tooled automatically. Existing agents created with a narrow profile keep their old tools until you grant more — use the dashboard editor's "All tools (*)" button or PATCH /api/agents/{id}/config {"tools":["*"]}.
Quality
- New regression tests for
registry.update_tools(grant-all widens caps + clears profile; specific list widens only relevant caps; existing allowlist preserved). cargo build+clippy --all-targets -D warnings+fmt --checkclean; 1709 tests pass.
Full diff: v0.7.81...v0.7.82
Assets 14
v0.7.81 — UTF-8 panic fixes
Bug Fixes — UTF-8 Panic Safety
Patch release closing the remaining byte-slice string truncations that could panic on multi-byte UTF-8 input (Russian, CJK, emoji). Same panic class fixed ~10 times across v0.7.46–v0.7.60 — these were the last known sites. All six now use the UTF-8-safe helpers rusty_hand_types::text::truncate_bytes / truncate_chars.
Fixed
| Site | Limit | Impact |
|---|---|---|
shell_exec stdout/stderr truncation |
100 KB | High — subprocess output is frequently non-ASCII (Unicode filenames, localized tool messages, git logs) |
| Docker sandbox stdout/stderr truncation | 50 KB | High — same, for containerized exec |
web_search legacy response-body truncation |
50 KB | Med — non-ASCII JSON/HTML bodies |
derive_session_label (first 40 chars) |
40 chars | Med — Cyrillic/CJK session labels |
A raw &s[..n] slice panics when byte n lands inside a multi-byte char; the safe helpers snap down to the nearest char boundary.
Tests
- Added regression tests for multi-byte session labels (3-byte hiragana — worst case at byte 40) and Cyrillic input.
cargo build+clippy --all-targets -D warnings+fmt --checkclean; 1706 tests pass.
Full diff: v0.7.80...v0.7.81
Assets 14
v0.7.80 — dashboard reliability pass
Exercised every interactive path in the React control panel against a live kernel and fixed 21 bugs across the dashboard and backend.
Dashboard (frontend)
| Fix | Before |
|---|---|
| 5 dead buttons wired | Overview export, Chat new-session/export/reset, trigger enable/delete did nothing on click |
window.prompt() → themed modals |
Fork-agent + bulk-move-to-group used blocking native dialogs |
| Live sidebar counts | Hardcoded placeholders (10/5/9/4/3) that never updated |
| AgentKvEditor field | Read resp.kv, API returns kv_pairs — KV store always showed empty |
| Analytics spend-by-model | Always 0ドル.00 — read spend/cost_usd, API returns total_cost_usd |
| Audit verify count | Showed undefined for any chain with >0 entries |
| Chat slash-commands | Static 12 → merged with kernel's 73 (keeps curated usage hints) |
| Chat input stuck | No longer sticks in "sending" when WebSocket drops |
| Mock-data flash | Removed D.agents/D.approvals cold-load flash + dead data.js (~9 KB) |
Backend (API / kernel / memory)
- KV
list_kvcrash —set()writes a BLOB butlist_kvread it asString, so every populated KV list returned "Memory operation failed". Now reads bytes, matching the single-keyget()path. - Knowledge standalone entities — entities with no relations were invisible on the Knowledge page until an edge connected them. Added
list_entities()+ merge intoGET /api/knowledge. - EntityType double-encoding —
{"custom":"\"concept\""}instead of"concept". - Cron create/update returned double-encoded JSON (
{"result":"{\"job_id\":...}"}). - WorkflowEngine boot panic —
blocking_writeon a tokioRwLockfrom the sync constructor panicked whenworkflows.jsonexisted. GET /api/sessions/{id}returnedidnotsession_id, breaking Chat export/reset.GET /api/agentsmissingmessage_count; metrics endpoint missingtotal_tokens/message_count/last_activitythat the drawer sparkline depends on.
Docs
webchat.rsdoc comment refreshed (Alpine.js → React 18, 18 pages).
Stats: 1704 tests passing · clippy clean · fmt clean · 14 commits
Assets 14
v0.7.79 — catalog refresh + MCP allowlist UI
Highlights
Model catalog refresh
- Anthropic:
claude-sonnet-4-20250514→claude-sonnet-4-6,claude-opus-4-20250514→claude-opus-4-7. Old IDs are kept as aliases so existingagent.tomlfiles keep resolving. - Context windows: 1M on Sonnet 4.6 / Opus 4.7 / DeepSeek V4 Flash & Pro.
- Output ceilings: Haiku 4.5 → 64K, DeepSeek V4 → 384K.
Pricing updates
| Model | Input $/M | Output $/M |
|---|---|---|
| Opus 4.7 | 5.00 (was 15.00) | 25.00 (was 75.00) |
| Sonnet 4.6 | 3.00 | 15.00 |
| Haiku 4.5 | 1.00 (was 0.25) | 5.00 (was 1.25) |
| Kimi Code (K2.6) | 0.95 (was 0.60) | 4.00 (was 2.50) |
| DeepSeek V4 Flash / chat / reasoner | 0.14 | 0.28 |
| DeepSeek V4 Pro | 1.74 | 3.48 |
Both the heuristic estimate_cost_rates() in metering.rs and the catalog table are updated together so heuristic and catalog billing paths stay in sync.
Onboarding & spawn flow
/api/providersstarted returningauth_status="configured"/"not_required"in v0.7.78; the spawn modal + onboarding wizard previously hard-coded"ok"and hid every configured provider. Both now accept all three statuses.- Onboarding writes catalog-canonical model IDs per provider (
claude-sonnet-4-6,kimi-for-coding,deepseek-v4-flash,MiniMax-M2.7,glm-4-plus,llama3.2,mock-model). SpawnAgentModaland onboarding now write real tool names intocapabilities.toolsby expanding the chosen profile via/api/profiles. Previously they wrote the profile name as a single literal tool, so spawned agents ended up with zero working tools. Default onboarding tools are["web_search", "web_fetch", "memory_recall"].- All emitted manifest strings go through a real TOML basic-string escaper.
MCP allowlist UI (Settings)
- New
McpAllowlistCard: enable/disable the MCP server, anallow_all_toolstoggle gated by a confirm dialog warning aboutshell_exec/file_write/skill_install/config_replaceexposure, and per-tool checkboxes forextra_allowed_tools. GET /api/confignow exposeslog_leveland amcp_serverblock so the card renders without parsingconfig.tomlclient-side.json_to_toml_valuehandles Array/Object recursively (soPOST /api/config/setcan writeextra_allowed_tools) and maps Null → empty string.
Approval env overrides
load_config() now honours these env vars after the TOML load — useful for headless deploys that don't want to rewrite config.toml:
RUSTYHAND_INTERACTIVE_APPROVAL/RUSTY_HAND_INTERACTIVE_APPROVAL— force interactive approvals.RUSTYHAND_AUTO_APPROVE/RUSTY_HAND_AUTO_APPROVE/RUSTYHAND_AUTONOMOUS/RUSTY_HAND_AUTONOMOUS— force auto-approve.
Interactive takes priority if both are set.
Tests
panel_dashboard_testpinsMcpAllowlistCardwiring and the newauth_status === "configured"+kimi-for-codingmarkers.panel_jsx_static_auditadds two static checks:SpawnAgentModalaccepts both"configured"and"not_required"auth, and onboarding writes catalog-canonical model IDs + real tool names.
Verified
cargo build --workspace --libcargo test --workspace— all green (1776+ tests)cargo clippy --workspace --all-targets -- -D warnings— cleancargo fmt --check
Migration notes
- Existing agent manifests with
claude-sonnet-4-20250514/claude-opus-4-20250514keep working via the alias map — no changes required. - The pricing changes affect
/api/usage/dailyand/api/budget/agents/:idfigures starting with this release. Spend on Haiku 4.5 will be ×ばつ higher per token, on Opus 4.7 ×ばつ lower, on Kimi ×ばつ higher. Cost-based budgets and rate limits should be reviewed after upgrade.
Assets 14
v0.7.78 — SpawnAgentModal live provider/model dropdowns
Highlights
Fixed: Spawn agent modal no longer hardcoded to anthropic
The Custom tab of Spawn Agent had two free-text inputs prefilled with
anthropic and claude-sonnet-4. If your kernel was running on openai,
ollama, or any other provider, you had to know the exact provider id and
model name and type them by hand — and a typo silently produced a broken
agent on first message.
Now both fields are live dropdowns:
-
Provider
<select>— sourced from/api/providers, filtered to
entries that are actually usable:auth_status == "ok"(has an API key configured)is_local && reachable(ollama / vllm / lmstudio running)fallback/mock(demo mode)
Each option carries a status badge (
ok/local/fallback), and
the kernel's currently-configured default provider is shown as a hint
next to the label. -
Model
<select>— populated from
/api/models?provider=X&available=truewhenever the selected provider
changes. For local providers without a catalog entry, falls back to
provider.discovered_models[]from the kernel's health probe, with a
manual text input so you can still type tags likellama3.2:latest. -
Defaults pre-filled from
/api/config.default_model— the modal
opens already pointing at whatever provider/model the kernel would use
by default, so for the common case you can just type a name and click
Spawn. -
"No usable provider" banner — replaces a silent dead form when no
provider has a key and nothing local is reachable. -
System prompt textarea added (optional, defaults to
"You are a helpful agent.").
Manifest TOML escaping hardening
Generated manifest TOML now properly escapes the backslash, quote,
newline, CR, and tab characters that come from user input
(name, model, provider, system_prompt, customProfile).
Pasting a multi-line system prompt or typing a name with a quote no
longer produces invalid TOML.
Stale-data race guard
useApi keeps the previous response while a new fetch is in flight,
so switching provider briefly leaked models from the OLD provider into
the dropdown. If you clicked Spawn during that window you got a 400
back. Fixed by filtering catalog models client-side against the
currently-selected provider id.
Verification
cargo build --workspace --lib✓cargo clippy --workspace --all-targets -- -D warnings✓cargo fmt --all -- --check✓cargo test --workspace— 1607 passed, 0 faileddashboard_wires_every_kernel_endpoint_it_usesalready pins
/api/providers,/api/models,/api/config,/api/templates,
/api/profiles— the new wiring inherits that regression net.
Upgrade notes
No backend changes — this release is dashboard-only. If you were
spawning agents programmatically via POST /api/agents, nothing
changes; the modal is just the UI surface.
Assets 14
v0.7.77 — full config editor + MCP config tools
Highlights
Fixed
- Overview → New agent button was unwired (dead primary button). Now routes to the Agents page and opens the spawn modal via the same
rh:hotkey:newevent the Cmd+K palette uses. - Agents refusing network commands. The default system prompt now explicitly authorizes
shell_exec,web_fetch, andweb_searchfor any network operation (curl, wget, ping, dig, ssh, git, package installers, HTTP/HTTPS). Theshell_exectool description also calls out that network access is enabled. Aligns prompt behavior with v0.7.75/v0.7.76 policy (shell_exec free, taint check opt-in).
Added — Full config.toml editor in Settings
- New Edit raw config button in the Settings page opens a monospace TOML editor preloaded from
/api/config/export. - Save triggers new
POST /api/config/replace:- Validates TOML syntax before writing
- Writes a
.toml.bakbackup before each save - Substitutes
<redacted>placeholders with on-disk values, so secrets survive round-trips when the operator only edited unrelated fields - Triggers
reload_config()and reports the plan (applied / applied_partial / no_changes / saved_reload_failed) plus whether a daemon restart is required
- Audit-logged like other config changes.
Added — MCP can now manage application config
Three new builtin tools (also callable by agents):
config_get— read~/.rustyhand/config.tomlwith secrets masked as<redacted>. Added tosafe_default_tools()— available to MCP clients out of the box.config_set— set a single dotted-path field (e.g.default_model.provider,memory.decay_rate). Privileged: opt-in viamcp_server.extra_allowed_tools.config_replace— replace the entire config.toml with the same redacted-secret round-trip and.toml.bakbackup as the HTTP endpoint. Privileged: opt-in.
KernelHandle trait gains config_home() and reload_config_json() so tool handlers can locate the config file and trigger reloads.
Expanded MCP safe defaults
By default (no operator opt-in) MCP clients can now also call:
self_evaluate,cron_list— read-only introspectionimage_analyze,media_describe,location_get— read-only media/locationa2a_discover— external agent discoveryconfig_get— read-only config inspection (secrets masked)
RCE-class tools (shell_exec, agent_spawn, agent_kill, skill_install, file_write, apply_patch, cron_create, cron_cancel, self_update, config_set, config_replace) remain opt-in via extra_allowed_tools — verified by mcp_server_default_blocks_privileged_tools.
Verification
cargo build --workspace --lib✓cargo clippy --workspace --all-targets -- -D warnings✓cargo fmt --check✓cargo test --workspace— 1607 passed, 0 failed- New regression pins:
ConfigEditorModalindashboard_wires_write_paths,/api/config/replaceindashboard_wires_every_kernel_endpoint_it_uses,config_*tools inmcp_server_default_*andtest_builtin_tools_include_config_tools.
Notes for upgraders
- The new
config_replaceHTTP route inherits the global 4 MB body limit and the standard auth middleware (same asconfig_set/config_reload). - To expose
config_set/config_replaceover MCP, add them tomcp_server.extra_allowed_toolsin~/.rustyhand/config.toml. Only do this on trusted networks — both tools can rewrite the daemon's runtime configuration.
Assets 14
v0.7.76 — taint heuristic opt-in (curl / wget / ping unblocked)
Why this exists
After v0.7.75 flipped `exec_policy.mode` to `Full` by default, operators reported the agent still refused `curl`, `wget`, and `ping`. Investigation found a second security layer the v0.7.75 release didn't touch — a heuristic taint check in `tool_runner.rs` that pattern-matches `curl `, `wget `, `| sh`, `eval `, `python -c`, etc. and blocks the command as a "potential exfil vector".
The check is heuristic (`/usr/bin/curl`, `CURL`, subshell escapes all bypass it trivially) so its real-world security value in single-operator RustyHand deployments — where the channel adapter's `allowed_users` is the real boundary — was friction without payoff.
Change
New field `exec_policy.taint_check_enabled: bool`, default `false`.
The shell_exec gate now reads:
| Mode | `taint_check_enabled` | Behavior |
|---|---|---|
| Full | any | skip everything (since v0.7.75) |
| Allowlist | true | allowlist + run heuristic |
| Allowlist | false (new default) | allowlist only |
| Deny | any | block all |
Restoring the heuristic
For deployments running untrusted LLMs against shared shells:
```toml
[exec_policy]
taint_check_enabled = true
```
How to apply on a running production
- Upgrade binary to v0.7.76. Default `mode = Full` + no taint check ⇒ `curl` / `wget` / `ping` just work.
- If `config.toml` already has `mode = "allowlist"` saved from an older install, either remove that block (new defaults apply) or omit `taint_check_enabled` (default is `false` regardless).
- Env-only path: `RUSTYHAND_EXEC_MODE=full` (from v0.7.75) bypasses both layers without editing config.
Verification
- 1601 workspace tests pass
- `cargo clippy --all-targets -- -D warnings` clean
- `cargo fmt --check` clean
Assets 14
v0.7.75 — shell_exec free by default + RUSTYHAND_EXEC_MODE env override
Default policy shift
The rustyhand agent's `shell_exec` was double-gated: `ExecPolicy.mode` defaulted to `"allowlist"` AND `ApprovalPolicy.require_approval` defaulted to `["shell_exec"]`. Combined with the existing `auto_approve_autonomous = true` default (since v0.7.21), the approval gate was friction-only — every shell_exec produced a WARN-logged auto-approval, then hit the allowlist gate and got rejected unless the command was in `safe_bins`.
For RustyHand's stated design (single-operator agent OS where the channel adapter's `allowed_users` is the real security boundary and the operator already authorized the action by sending the message), both gates were friction without security. This release aligns the defaults with the design philosophy.
Defaults changed
- `ExecSecurityMode::default()`: `Allowlist` → `Full` (any command allowed)
- `ApprovalPolicy::require_approval`: `["shell_exec"]` → `[]` (dead entry since v0.7.21 trust mode default)
Env override (new)
```
RUSTYHAND_EXEC_MODE=full|allowlist|deny
```
Changes `exec_policy.mode` at boot without re-rendering `config.toml`. Useful for Docker/Portainer flows. Unknown values warn and ignore.
Restoring strict mode
```toml
[exec_policy]
mode = "allowlist"
[approval]
require_approval = ["shell_exec"]
auto_approve_autonomous = false
```
Or env-only: `RUSTYHAND_EXEC_MODE=allowlist`.
What's still gated (defense in depth)
- Agent capabilities — per-agent `tools` and `shell` allowlist in `agent.toml`
- Subprocess sandbox — env vars filtered through `allowed_env` (creds don't leak)
- Timeouts — `timeout_secs` (30s default) + `no_output_timeout_secs` (30s)
- Output cap — `max_output_bytes` (100KB default)
- Audit log — every `shell_exec` recorded in `audit.jsonl`
- Channel adapter — `allowed_users` gates who can send commands
Verification
- 1601 workspace tests pass (post-fixup of 6 tests that pinned the old defaults)
- `cargo clippy --all-targets -- -D warnings` clean
- `cargo fmt --check` clean
How to apply on a running deployment
```
Docker
docker run -e RUSTYHAND_EXEC_MODE=full ...
Portainer
add RUSTYHAND_EXEC_MODE=full to the stack's env vars, redeploy
systemd
Environment=RUSTYHAND_EXEC_MODE=full in the service unit, restart
Or just upgrade to v0.7.75 — the defaults are already permissive
```