Uses: CLI · Claude Code · Codex
What the agent will never do on its own
Four tools are the only ones the agent asks you about by name - the never-verbs. It never calls one on its own initiative. It calls one only when you explicitly ask for that action on that workflow, and Claude Code then asks you to approve each call:engini_workflows_delete_workflowengini_workflows_run_workflowengini_workflows_enable_or_disable_workflowengini_workflows_rerun_execution
The layers
Four layers stand between those four verbs and your account. Only the last one is a boundary, and it refuses nothing:- The agent’s instructions say: never on its own initiative; only when you explicitly ask for that exact action, after telling you a permission prompt is coming. This is prose: a model follows it, nothing enforces it.
- The plugin’s guard hook - Claude Code only. Installed with the plugin and live at once. See The guard.
- Your own settings - the settings below. Copy-paste; a plugin cannot install them for you.
- The Engini server refuses nothing here: your key is allowed to do all of it.
Permission modes
What the Claude Code docs say, not something we ran end to end:- Default mode: the guard’s question for a never-verb reaches you. That a hook’s question also beats the
Bash(engini:*)allow rule below has not been verified yet; youraskrules cover the same four verbs either way. - Auto mode: documented to still send a hook’s question to you (the classifier does not decide it), and to still prompt on
askrules in your settings. --dangerously-skip-permissions(bypass): what a hook’s question does here is undocumented, so assume the guard’s question may not reach you. The docs sayaskrules in your settings still prompt in this mode, so paste the safety settings below - those rules are the layer that holds in bypass mode. They match the literal command form only (see Settings to paste yourself).-p(non-interactive): nobody can answer, so the docs say the call is refused rather than asked.dontAskis expected to do the same (inferred, not documented for a hook).
--dangerously-skip-permissions: it holds a shell and your key.
The guard (Claude Code only)
The guard is a hook installed with the plugin. It needs Python 3.8 or later (python3 or python). A question is a prompt, not a block: it works only while your permission mode lets the prompt reach you (see Permission modes).
It asks you to confirm these:
- The four never-verbs. An
engini tools callnaming any of them is asked about in any argument order and through quoting, case andbash -cwrappers. A tool name the guard cannot read (built from a shell variable or a substitution, such asengini_workflows_$V) is asked about too, because it could be one of the four.
- Commands that handle a raw key when the agent runs them:
engini connect,engini login,engini connections create|update|delete,engini connections auth-providers create|update, and any--api-key. The refusal tells the agent to hand you the command to run in your own terminal. - Reads of your stored login and of key variables: reading the CLI’s stored login (see Where the CLI stores your login), an
ENGINI_API_KEY/ENGINI_API_TOKEN/ENGINI_COMPANY_TOKENvariable, or dumping the environment. A presence test such as[ -n "$ENGINI_API_KEY" ]is fine. - Edits to the hook itself or to your Claude Code or Codex settings. This includes any command that names your
.claudeor.codexfolder itself, a wildcard name or a./..step inside one, or acdinto a folder under one - so evenls ~/.claudeis refused. Read, Write, Edit, Grep and Glob paths are judged as given and again after resolving them against the session’s folder and following symlinks.
engini tools call with a heredoc is judged as code unless it is the agent’s own one-call shape: the <<'EOF' opener ends the command’s first line and the reader is the engini CLI itself (engini, npx @engini/cli or npx @engini/cli@<exact version>).
Scope of the guard
The guard applies to every Claude Code session where the plugin is enabled - not only while the agent is working. It asks you before your own never-verb call (approve it - you meant it) and refuses your ownengini connections delete or engini login when the agent runs it: run those in your own terminal, or turn the plugin off for the session with /plugin disable workflow@engini (that removes the guard’s other protections too). Everything else - your builds, git, other tools, other Engini apps’ tools - runs normally.
This applies to every project’s own
.claude and .codex folders too. With the plugin enabled, ls .claude, git add .claude, grep -r x .claude and a cd into a folder inside .claude are refused in every project and every session, not only while the agent is working. Run those in your own terminal, or turn the plugin off for the session.When the guard speaks
The message starts withFLO-HOOK-ASK (a prompt for you) or FLO-HOOK-DENY (a refusal the agent reads) and says which of these it is. A command that trips both is refused.
The plugin’s troubleshooting guide covers each case in more detail.
Claude Code’s own permission rules (layer 3) match the four verbs inside a compound command such as
cd /tmp && engini tools call .... That is tested for deny rules on Claude Code 2.1.316. The permissions docs say deny and ask rules apply when any subcommand of a compound command matches them, so the ask rules below are expected to match the same way, but that was not re-tested. The guard’s extra reach is that it needs no paste, and the quoting and wrapper forms above.
What the guard does not stop
While the agent has a shell and your key, it can do anything your key can do, including acting through your other connections. The guard narrows one literal command form. It is a text matcher, not a sandbox. These are deliberately open, and tested as open:- Every other tool. The guard asks about only the four verbs. The agent’s rule against calling other apps’ tools (for example
slack_send_message) is an instruction it follows, not something enforced; so is “read execution data only when asked”. Builder tools Engini adds later run normally too. - Indirection the guard never sees:
eval "$X",bash -c "$X", a shell function, a script file. - Anything that skips the CLI: a
python -cagainst the SDK, or a plain HTTPS call with your key. - A tool name that is never in the command text at all, for example
xargs engini tools call --args - < slugfile. - The key file reached through a glob (
cd ~/.config && cat engini/conf*), a recursive search from a parent folder in the shell (grep -r ... ~/.config), and a customXDG_CONFIG_HOMEfolder named by its own path in a shell command. - A settings folder name built from a variable or a wildcard in the folder name itself (
~/.cl*). - In the shell, a symlink to your settings or key folder that already exists (the command names only the link), and a program that changes folder itself before it writes (
os.chdirin a script,git -C,find -execdir). Read, Write, Edit, Grep and Glob follow symlinks, so those tools are covered.
Settings to paste yourself
Each rule below matches the literal command form only.bash -c, a shell variable, eval, reordered arguments or a python -c against the SDK walk past every one of them. They are what holds when the guard does not run, and in Codex they are all there is.
- Allow only
Bash(engini:*)and never run the agent with--dangerously-skip-permissions. The agent holds a shell and your key; this is the widest control by far. - The
askrules make Claude Code ask you, outside the conversation, before every create or update, before each of the four never-verbs and before the key-handling commands (engini login,engini connect,engini connections create|update|delete,engini connections auth-providers create|update,engini login --api-key) - the approvals the model cannot give itself. With the plugin’s guard installed, those last commands are refused outright when the agent runs them, so theaskrule is what applies when the guard is not installed. In Codex, which has no guard, the rules file only asks, so an approved command there would carry the key through the agent: decline it and run the command yourself in your own terminal. Only the stored-loginReadrules, the onecatrule and the environment dumps aredeny. - The
Readrules cover the stored login on each platform for Claude’s own file tools; they do not bind a shell command, and the oneBash(cat ...)rule covers only that exact command. What keeps other shell reads (grep,head, the other paths) from running unasked is thatBash(engini:*)is the onlyallowrule, so any other command asks you first.
Claude Code settings
Merge this into your user settings (settings.json in your ~/.claude folder):
Codex rules file
Save this as~/.codex/rules/flo.rules:
Codex sandbox
Codex runs commands without network by default, so everyengini call fails. Allow network for commands in your Codex config:
~/.engini/flo/, outside the workspace: add it as a writable root or the agent skips them. Not yet verified on a real Codex install.
Where the CLI stores your login
The guard refuses reads that name any of these paths, and Grep or Glob searches started from a folder above them, such as your home folder or~/.config - however that folder is spelled: a relative path, no path while the session runs in it, a symlink, or a second macOS name for your home folder such as the one under /System/Volumes/Data.
~/.config/engini/config.toml(macOS, Linux)$XDG_CONFIG_HOME/engini/config.toml(whenXDG_CONFIG_HOMEis set)%APPDATA%\engini\config.toml(Windows)%USERPROFILE%\engini\config.toml(Windows, whenAPPDATAis unset)
Things to know
- Your key carries its creator’s whole permission set. Use a dedicated, least-privilege, workspace-scoped key you can rotate. See API keys.
- Log in interactively, in your own terminal.
engini login, never--api-keyon the command line and never through Claude Code’s!prefix (no terminal there, so the key cannot be hidden as you type it). - What the agent reads enters the model’s context: workflow definitions, and execution data when you ask for it.
- The stored key is reachable by the agent. It is a file in your own home folder; the agent can reach it through the shell as much as through file reads. The guard refuses the literal reads; what actually protects you is a scoped, rotatable key plus the
Bash(engini:*)allow rule - not secrecy from the model.