lea · Teams of agents
Plugins and permissions in lea
Each projects directory's container has its own writable copy of your ~/.claude/settings.json
and ~/.claude/plugins, made from yours on the host when it first starts. Inside lea,
/plugin install, /plugin marketplace add and the marketplaces' auto-update work as on the
host, on that copy, for every agent of that projects directory. Permission rules are the exception:
they come from your host's settings.json only (below).
-
Kept across restarts. The copy lives in lea's data (
state/claude/<id>/);lea stopand the nextleakeep what the agents installed or allowed. -
It follows your host while it is untouched. When you change your host's settings or install a plugin on the host, the next start takes it into a copy the agents did not change. When both changed, lea keeps the copy and says so once; to take the host's, remove the copy (the path lea prints) and restart lea.
-
Nothing is ever written back to your host. The host's
settings.jsonandpluginsare not mounted at all; lea reads them on the host to make the copy, and never copies your plugins' own data (plugins/data). To use a plugin you tried in lea on the host, install it there yourself (claude plugin install <plugin>@<marketplace>): lea has no path from an agent to your host's configuration, so a prompt-injected agent cannot plant a hook or a plugin that would run the next time you startclaudeoutside lea. -
lea's guards cannot be undone from there. lea's hooks (the shell guard and its hard denies, your grants, the approvals in the deck) are in Claude Code's managed settings,
/etc/claude-code/managed-settings.json, root's in the image: Claude Code ranks them above the agents' settings and runs their hooks even when those setdisableAllHooks. An agent that deletes lea's hooks or allows every command in its copy is still guarded. The same hooks also come in through--settings(an organisation's server-managed settings replace the file-based ones); Claude Code runs each once.lea doctorchecks the managed settings in the image and in every running container, and that Claude Code is the version lea's CI verified them with. -
Permission rules come from your host only. The managed settings make their own rules the only ones Claude Code honours (
allowManagedPermissionRulesOnly), disable bypass mode (disableBypassPermissionsMode): an agent that writesallowrules into its copy or into a project's.claude/settings*.jsongains nothing. lea sets no default mode of its own (#333): the agents start in the mode Claude Code starts in on your host (its own default, auto on the accounts that have it), or in yours when yoursettings.jsonsets one. AdefaultModean agent writes into its copy can change its own mode, never to bypass. At each start the container's root adds your host's ownpermissions(allow,ask,deny,additionalDirectories, anddefaultModeunless it isbypassPermissions) to lea's, read from your host's~/.claude/settings.jsonby lea on the host and mounted read-only, never from the copy. So a rule you add with/permissionsinside lea, or the deck's "always", is not kept: add it withlea config claude(below) or to your host'ssettings.json, then restart lea (lea stop --all, thenlea). -
Your base settings for lea:
lea config claude(#334). Without editing a file, and without touching your own Claude Code outside lea:lea config claude # every effective setting, with its source lea config claude defaultMode auto # default, acceptEdits, plan, dontAsk or auto lea config claude defaultMode --unset # back to your settings.json's, else Claude Code's lea config claude allow add 'Bash(make *)' # allow, ask, deny, additionalDirectories lea config claude allow remove 'Bash(go *)' # lea's built-in rules and your host's can go too lea config claude env MY_TOOL_HOME /opt/tool lea config claude model opus # also language, outputStyle, alwaysThinkingEnabledThey are kept in
claude.jsonnext to yourlea.conf(never in~/.claude/settings.json), outrank your host'spermissions, which outrank lea's image, and apply to the containers started from then on (lea stop --all, thenlea).lea doctorlists them;lea config exportnever carries them (--include-personalneither). lea keeps what keeps it working and its guards effective, and refuses it with the reason: its hooks,allowManagedPermissionRulesOnly,disableBypassPermissionsMode(so nobypassPermissions:dontAskorautodrop the prompts and keep the guards), the status line and its own variables (LEA_*,OTEL_*,SSH_AUTH_SOCK,GH_CONFIG_DIR,GLAB_CONFIG_DIR, the proxy's, telemetry and update switches). The command runs on the host only, and the agents' guard denies editing lea's configuration. -
The guard fails closed. lea's PreToolUse hook denies a call it cannot check: lea missing, a crash or a timeout (
onFailure: "block"), a bus that does not answer, a Claude Code without a lea agent identity (LEA_AGENT: run Claude in lea as an agent, from the deck orlea-agent run). The variables the hooks read that lea sets alike in every container (LEA_BUS_SOCK,SSH_AUTH_SOCK,GH_CONFIG_DIR,GLAB_CONFIG_DIR) are pinned in the managed settings and the--settingsfile, above an agent's ownenv.
The worst a compromised agent can do is change its own container's copy, which it could already
do by running code in its container: there is no persistence on your host, and no wider
permission. The agents of one container share one user, so they are one trust domain: an agent
that names another agent of its container in LEA_AGENT is still guarded by the same hard denies
and the container's grants.