How Lucius's permissions work
Every tool Lucius can call — reading your ledger, syncing a connector, sending an email, executing a trade-adjacent write, deleting a category — is governed by one setting: its permission level. One of the three levels below has a consequence you need to understand before you flip it.
The three levels
| Level | What happens when Lucius wants to call this tool |
|---|---|
needs_approval | Pauses. You see a confirm card with exactly what would happen; you approve or deny. Nothing runs until you tap. |
always_allow | Fires immediately. No card, no pause, no notification first. |
blocked | Refused outright. Lucius is told it isn’t permitted and never executes it — not even once. |
If you’ve never touched a tool’s setting, it behaves as needs_approval. Absence of a
stored decision resolves to asking you first, every time — with one exception: a small,
explicitly-reviewed set of tools whose only “writes” are internal bookkeeping (search cursors,
cache entries) rather than anything in your ledger or accounts. Those run without asking, the
same way a get_ tool would — refusing to let Lucius search your own email or re-check a
statement diff without a click first would serve no purpose.
always_allow means allow everywhere — including unattended
This is the one thing to understand before you set anything to always_allow: it fires on a
scheduled Automation, a headless run, or any surface with no human present — money movement
included. It is not quietly downgraded to needs_approval just because nobody is watching. If
you’d only be comfortable with a tool firing while you personally supervise it, leave it at
needs_approval (or set it to blocked). always_allow is not “trust it while I’m around” — it
is “never ask again, on any run, ever.”
Concretely: set a transaction-writing tool to always_allow, then schedule an Automation that
calls it at 3 AM — it runs at 3 AM with no card and no confirmation, exactly as if you’d approved
it by hand at your desk. Deliberate, not an oversight: the app is self-hosted and single-operator,
so an unconfigurable “always ask when only a scheduler triggered this” floor was judged to protect
against nothing an attacker couldn’t already reach some other way, while permanently blocking
automations you’ve genuinely decided to trust. The tradeoff is real, and it’s yours to make per
tool — not the app’s to make for you.
One narrower exception: an Automation’s own run mode governs its approval-tier calls first, before your global settings are even consulted.
- A
manualAutomation pauses for your review on any call that would otherwise ask — even a tool you’ve globally set toalways_allow— unless that specific tool is on that Automation’s own allowed list. - An
autoAutomation runs everything through on its own, global level or not, except two tools with no override at all (see Automations).
Your global always_allow is what makes an ordinary chat turn skip the card; inside an
Automation, it’s the Automation’s own mode that decides.
Setting a level
| Where | What it’s for |
|---|---|
| On a confirm card, in the moment | Allow once approves just this call. Always allow approves this call and stores always_allow for that tool, everywhere, from now on. Deny refuses just this call without changing the stored level. See Confirm cards. |
| Ahead of time, per connector | Each connector’s detail page (Connectors → the connector) lists its tools with a ladder you can set directly — pre-approve a read-only connector before you talk to Lucius about it, or lock one to blocked before you’ve tried it. |
| Settings → Permissions | A review-and-revoke list, not a place to grant new permissions — every tool with a stored decision, grouped, with one-click Reset back to the unset/default state. |
Setting a level for a whole group (every tool a connector owns) stamps that level onto every tool in the group in one action; a group with mixed levels shows as “Custom” — display-only, not a level you can pick.
Note: the Permissions screen doesn’t show a specific “this also applies during unattended automation runs” reminder next to Always-allow. The behavior above is real regardless — read this page once and you know it — but the UI itself won’t re-warn you in the moment.
Who owns a tool
Every tool has an owner: the connector it’s scoped to (plaid, telegram, an external MCP
server as mcp:<server-id>, a web-search provider as web_search:tavily, …), or the reserved
built-in bucket for a tool tied to no connector. Permissions are set per (owner, tool) pair, which
is why a connector’s detail page and the app-wide Permissions list show the same data from two
angles.
For an unscoped built-in tool with no stored decision, the fallback isn’t a blanket
needs_approval: a plain read runs freely, a write asks first. Once you’ve made an explicit
decision, it runs, full stop, either direction — nothing overrides an explicit blocked, nothing
downgrades an explicit always_allow.
blocked always wins
Refused before anything else is considered, no override fires it anyway. Use it for a tool you never want Lucius to call, on any surface, under any condition.
What this means the first time you ask Lucius to write something
Because needs_approval is the default, an ordinary write — logging a transaction, editing
one, moving budget money — shows a confirm card the first time, and every time after, until you
grant it. The app asks by default and lets you decide which tools you’re tired of approving,
rather than deciding for you which writes are “safe enough” to skip. Tap Always allow once
and the same tool writes immediately from then on, with a plain “here’s what I did” summary in
the reply instead of a pause. See Confirm cards for exactly what that first
card shows and what each button does.