Uses: CLI · Claude Code · Codex
What Flo will never do
Four tools are the only ones Flo refuses by name - the never-verbs: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:- Flo’s instructions name them as never. This is prose: a model follows it, nothing enforces it.
- Flo’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.
The guard (Claude Code only)
The guard is a hook installed with the plugin. It needs Python 3.8 or later (python3 or python) and refuses these:
- The four never-verbs. An
engini tools callnaming any of them is refused 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 refused too, because it could be one of the four. - Commands that handle a raw key:
engini connect,engini login,engini connections create|update|delete,engini connections auth-providers create|update, and any--api-key. - 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 Flo’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 Flo is working. It also refuses your ownengini connections delete, or a never-verb you genuinely meant to run. Run those in your own terminal, or turn the plugin off for the session with /plugin disable flo@engini-flo (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 Flo is working. Run those in your own terminal, or turn the plugin off for the session.When the guard refuses something
The message starts withFLO-HOOK-DENY and says which of these it is:
The plugin’s troubleshooting guide covers each case in more detail.
Claude Code’s own deny rules (layer 3) also catch the four verbs inside a compound command such as
cd /tmp && engini tools call ... - tested on Claude Code 2.1.316. 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 Flo 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 refuses only the four verbs. Flo’s rule against calling other apps’ tools (for example
slack_send_message) is an instruction Flo 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 Flo on auto-accept or--dangerously-skip-permissions. Flo holds a shell and your key; this is the widest control by far. - The two
askrules make Claude Code ask you, outside the conversation, before every create or update - the one approval the model cannot give itself. - 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 Flo 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.
engini login, never--api-keyon the command line. - What Flo 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.