May 28, 2026

Vault recovery, image OCR, code_eval silent-fail, bash safety

A major vault hardening landed across four commits

A major vault hardening landed across four commits. The 2026-05-27 incident where a vault re-init orphaned every encrypted credential drives a new filesystem backup scheme: every VaultService.initialize() now writes the wrapped-DEK key to data/secure/vault_backup.json (owner-read-only 0o400 in 0o700 dir, rotated to .prev.json). Login drives a new unlock_or_restore() with three explicit outcomes — unlocked, restored from backup, or unrecoverable (wipes master_account + vault, logs loudly, returns 401). The old auto-reinit code path is deleted end-to-end (banner, endpoints, column, JS/HTML/CSS), netting -101 SLOC; SchemaConvergence auto-drops the stale column. Recovery later migrated to an append-only, never-deleted timestamped backup scheme with newest-first iteration.

Image OCR is finally wired in. text_extractor.extract_text now routes image MIME types (png/jpeg/webp/gif/bmp/tiff + image/* wildcard) through _extract_image, delegating to the previously-orphaned image_context_service.analyze (EXIF strip + dimension normalize + RapidOCR). Dead compute_hash and its self-test are gone. document.upload re-fetches terminal status after synchronous extraction, catches exceptions via _mark_upload_failed, and reports a clear “Failed to process document {name}” on any non-ready terminal state. DocumentAbility.TIMEOUT rises 10s → 120s with a named timeout helper in act_dispatcher_service; the 1299-test unit suite passes.

code_eval no longer returns a silent empty success that baited the ACT loop into 43-call retry storms (nightly pipeline #880, 905s). execute() now always returns one of four explicit outcomes — printed output, full traceback with preserved partial stdout, no-output error, or no-code error — via a split _run/_captured/_error helper. A related policy fix allows code_eval by default in subconscious and external_agent channels (RestrictedPython sandbox makes it safe everywhere), with the dead _EXTERNAL_AGENT_DENY entry removed.

Bash safety gets a real fix: rm -rf was classified as modify_file instead of compound. A two-layer fix escalates rm with recursive flags in _classify_heuristic (severity 6) and unconditionally blocks rm with both recursive and force flags in _check_destructive regardless of policy. 126 new parametrized tests cover the classification helpers. TKT-636 changes search_files default root from Path.home() to / with an internal 80%-of-timeout budget for partial results (TIMEOUT 15s → 30s). TKT-642 enables location-only reminders with a 30-day placeholder and adds KIND_PLACE to memory graph recall.

Dashboard polish: native browser confirm() dialogs in the Brain dashboard (providers, documents, scheduler, Skills tab) are replaced with Radiant design system modals. Each IIFE gets a private _showConfirm() helper using data-cancel/data-confirm attribute selectors to dodge ID collisions, and title/confirmLabel are escaped inside all four files (including skills.js) to close a latent XSS. Misc housekeeping: copyright headers swap personal names for “Chalie AI”, grck.lan references and personal paths are removed, fixtures use generic names, and abilities.sqlite is rebuilt to sync with TKT-642 schema changes.

  • Vault backup + restore with three explicit outcomes (unlocked / restored / unrecoverable-wipe); append-only timestamped backups replace single-generation rotation

  • Document timeout raised 10s → 120s with named “Failed to process document {name}” message; upload loop now reports terminal state accurately

  • Image OCR extraction path wired in for png/jpeg/webp/gif/bmp/tiff, delegating to image_context_service.analyze

  • code_eval execute() split into four explicit outcomes, eliminating the silent {“text”:“”} retry storm

  • rm -rf now escalates to compound (severity 6) and is unconditionally blocked with both recursive and force flags

  • Native confirm() dialogs replaced with Radiant modals across Skills, providers, documents, and scheduler tabs with XSS-safe escaping