June 3, 2026
One gate for every tool, no exceptions
The permission system gets rebuilt around a single flat table and one enforcement chokepoint, with every framework timeout removed in the same pass.
Chalie’s permission model had grown a services layer, a rules table, scattered channel-specific carve-outs, and a policy check living in more than one place depending on which door a tool call came through. I wanted one door. Today that became real: a single flat policy(channel, permission, setting) table and one gate, PolicyManager.wrap, that every tool call passes through regardless of origin.
The mechanics mattered as much as the shape. A boot migration creates, copies, and seeds the policy table, and the order had real teeth: seeding too early tricked schema convergence into thinking the database wasn’t fresh, skipping the pass marking api_key as sensitive — so the REST API key was written to disk in cleartext instead of through the vault, a real regression. Moving the seed to run after convergence closed the gap, with a regression test asserting encryption on a genuinely fresh database.
The old dispatch path was deleted in favor of one linear chain, and MCP tool calls now route through the same lazy wrapper instead of their own seeding logic. Thirteen read-only and scratch tools (memory, search, find-tools, and similar) became a hardcoded internal bypass short-circuiting past the database on every channel — no seed rows, no per-channel visibility, since a tool that can never meaningfully be denied doesn’t need to pretend it can. That dropped 66 rows from the seed file, on top of 101 removed earlier when a never-used policy channel was deleted, dead weight once the delegate model shipped its own scoping.
Delegate scoping got tightened too: the research- and summary-flavored delegates were retired, leaving web search and web browse as the two surfaces reaching outside Chalie, both moved from inline config dictionaries to typed config subclasses matching the rest of the channel configs. Raw browser/search primitives and the pattern-graph write tools are no longer reachable from tool discovery on the main chat, background, or external-agent channels — want the web, go through the delegate, a narrower door than existed yesterday, on purpose.
The other half of the day was a bet I’d been sitting on: remove every framework-imposed execution timeout. Tool calls ran under a watchdog that could kill a slow-but-legitimate operation for no reason other than a clock. That’s gone, replaced by a synchronous call path with no deadline, on the theory that a model should discover a slow tool by waiting on it, not get silently cut off mid-thought. The one exception is code execution: CPU-bound code can’t be interrupted from its own thread, so a subprocess with a hard ten-minute cap that force-terminates remains the sole timeout left. Network and I/O timeouts stay, since a socket talking to a dead peer still needs to give up.
A code-quality pass also closed out issues blocking the release branch: a hardcoded temp path became a single shared helper so upload-file handling can’t drift across call sites, and a frontend bug quietly swallowing the activity trail got fixed — the backend’s tool-start and tool-end events use one set of field names, but the chat handler was still reading the old ones, so tool indicators silently failed to render and web search activity was invisible in chat.
None of today’s changes add a capability you’d notice from outside. What they do is make the boundary around every capability the same, checked the same way, every time — the thing I actually care about as more tool surfaces start composing together.
-
Policy system rebuilt around one flat table and a single
PolicyManager.wrapgate;Ability.dispatchdeleted in favor ofuse → match → wrap → execute -
Boot-order bug fixed where early policy seeding skipped schema convergence and left the REST API key stored in cleartext on fresh installs
-
Thirteen read-only/internal tools became a hardcoded bypass frozenset; the unused SUBAGENT policy channel and its 101 dead seed rows were deleted
-
Delegate tools narrowed to
web_searchandweb_browse, both now typed configs; raw browser/search and pattern-write tools scoped out of the main chat channels -
Every framework execution timeout removed except a hard ten-minute cap on code execution; a frontend field-name mismatch that was hiding the tool activity trail got fixed