Version 1.0.0
Execution guardrails
What bounds an agent session: sandbox, policy, human approval, session limits, loop detection and delegation.
An agent session is a series of actions proposed by the model and executed, or not, by the system. Lemniscate assumes the agent may misbehave, by mistake or under the influence of booby-trapped content, and builds the environment so that this behavior has no consequence outside the sandbox (security white paper V3, section 4).
Containment bounds what the agent can reach. Policy bounds what it can do inside. This page describes both, then the guardrails that stop an agent running too long, going in circles or stepping outside the project.
The seven guarantees of a session
Section titled “The seven guarantees of a session”Seven guarantees are upheld by the gateway and the sandbox, not by the model. They are independent of one another (security white paper V3, section 4.1).
| Guarantee | Statement | Where it is described |
|---|---|---|
| G01 | scope limited to the copy of the repository authorized by the project policy | What the agent sees |
| G02 | human approval of every write kept, presented as a diff | Human approval |
| G03 | command policy defined by the administrator, enforced by the gateway | Project and command policy |
| G04 | execution confined in a disposable sandbox | The execution sandbox |
| G05 | no reachable network destination, external or internal | The execution sandbox |
| G06 | traceability: instruction, proposed actions, decisions, results | The agent session cycle |
| G07 | stop by the developer and revocation by the administrator, at any time | Stopping and revocation |
The execution sandbox
Section titled “The execution sandbox”The security boundary is the sandbox, not the list of allowed commands. A command list is a matter of hygiene: an allowed interpreter runs anything. What bounds the agent is the environment it runs in (security white paper V3, section 4.2).
| Aspect | What the sandbox enforces |
|---|---|
| Isolation | one sandbox per session, created on demand and destroyed at the end of the session; no state shared between sessions or between projects; unprivileged execution, under seccomp and AppArmor profiles |
| Files | the working copy of the repository is the only writable volume; the root filesystem is read-only; writes are returned as a diff |
| Network | no interface, no egress, no name resolution; exchanges with the model are relayed by the gateway; a single control channel, opened by the gateway |
| Resources | time, CPU, memory and processes capped; automatic stop when exceeded, logged; nothing survives the session |
The absence of network egress is enforced by the isolation itself, not by a configuration the agent could change. Confinement profiles are provided and adapt to your standards. On an execution host shared between several projects, isolation can be carried by a microVM or a user-space kernel, depending on your reference framework.
Preparing the image used by the sandbox is described in Prepare the session container.
What the agent sees, what it does not carry
Section titled “What the agent sees, what it does not carry”An agent that holds no secret cannot leak one (security white paper V3, section 4.3).
| What the agent sees | What it does not carry |
|---|---|
| the files of the open repository, minus context exclusions | no account, no identity on the forge |
| the output of the commands it runs | no key, no token, no mounted secret |
| the developer’s instruction | no environment variable from the workstation |
| nothing else on the host or on the workstation | no access to another repository or another project |
Context exclusions are the files the agent does not see in the working copy: environment files, secret directories, certificates. The developer’s home directory, keys, forge credentials and environment variables are not mounted in the sandbox.
The agent has no account and pushes nothing. The approved diff is applied to the developer’s repository, who integrates it under their own identity, with their usual tools and through their review process.
Dedicated execution host or workstation
Section titled “Dedicated execution host or workstation”The sandbox runs on a dedicated host or on the workstation. The reference mode is the dedicated host (security white paper V3, section 4.4).
| Dedicated execution host, reference mode | Sandbox on the workstation, variant |
|---|---|
| sandboxes on a host separate from the gateway | unprivileged container with no network, on the workstation |
| no container engine on the workstations | local repository mounted, only writable volume |
| single environment, hardened by the administrator | the workstation’s toolchains available |
| chosen for sensitive scopes and Diffusion Restreinte | for platforms that allow a container engine |
In the reference mode, the agent does not have the user’s rights on their workstation, and the workstation only carries the extension. The execution host keeps no state. The trade-off: the host concentrates the sandboxes of several projects, which justifies reinforced isolation, and a copy of the repository passes through it for the duration of the session. The choice is made per scope, with the security officer. The setting that carries this choice is described in The agent session cycle.
Project and command policy
Section titled “Project and command policy”The policy is written by the administrator in the console, evaluated by the gateway before each action, and is not interpreted by the model. It cannot be disabled from a workstation (security white paper V3, section 5.1).
| Free, if the administrator decides so | Subject to developer approval |
|---|---|
| compilation, unit tests, formatting and analysis | any command not on the list |
| reading files of the open repository | any write kept, presented as a diff |
| commands listed by the administrator | any tool marked sensitive by the policy |
| a command already approved, if the policy allows it | any change to a file executed by a third party |
By default, no command is free: every change and every command is presented to the developer. The project administrator can:
- allow the usual compilation, test and analysis commands, and any command they list;
- disable one of the agent’s tools for a project, without redeployment;
- allow the developer not to re-approve a command already approved, or forbid this exemption.
Every policy change is logged. How to write it is covered in Set workstation policy from the console.
Separating control from data
Section titled “Separating control from data”The defense against prompt injection is structural: it does not rely on a filter (security white paper V3, section 5.2).
- The developer’s instruction is the only source of control.
- Content read from the repository, from a command output or from a dependency is marked as untrusted and presented to the model in a separate area.
- The available tools are fixed by the policy before any read. Content that is read cannot enable new ones.
- The policy applies to every tool call.
The model reasons over the whole context, but it does not hold the rights. An injection remains possible: the architecture guarantees that its effect does not leave the sandbox and goes before a human (security white paper V3, section 11.4).
Files executed by third parties
Section titled “Files executed by third parties”A confined agent can change a file that someone else will run later, with other rights. These files form a class of their own (security white paper V3, section 5.3):
- continuous integration configuration and build scripts;
- version control hooks and repository configuration files;
- dependency manifests and lock files;
- binary files and diffs beyond a size threshold.
Changing them is forbidden by default, or subject to a dedicated approval, separate from the ordinary approval of a diff and flagged as such to the developer. The list of classes is defined per project in the policy.
Human approval
Section titled “Human approval”Every action with a side effect is approved by a human (security white paper V3, section 5.4):
- every write kept is presented as a diff and accepted by the developer;
- every command not listed by the administrator is submitted for approval before execution;
- the administrator can take a tool away from the agent, per project, without redeployment;
- the developer can stop the session at any time, and the administrator can revoke it.
The approval request shows the exact diff or command, and flags files executed by third parties.
The proposed code may contain vulnerabilities: human review remains necessary, and accepted code follows the same review process as any contribution (security white paper V3, section 11.4).
Stopping and revocation
Section titled “Stopping and revocation”The developer has a one-gesture stop from their environment. It interrupts the work in progress, destroys the sandbox and rejects unapproved writes. The administrator has a revocation from the console, per session or per user, effective without a restart and logged (security white paper V3, section 4.5).
Running too long: session limits
Section titled “Running too long: session limits”Every session has a planned end. Four budgets bound it: duration, volume of exchanges with the model, number of commands and sandbox resources. Exceeding them triggers a clean stop (security white paper V3, section 4.5).
Each user message opens a session bounded in actions (tool calls and context compactions combined, 150 by default) and in duration (45 minutes by default). The next message starts again with a full budget.
When a limit is reached:
- In an interactive session, an extension can be requested from the human. Each extension grants a limited allowance (50 actions or 15 minutes), their number is capped (5 by default), and each one leaves a trace in the audit log.
- Without an extension, the agent produces a summary of its work (done, in progress, remaining) in a final model turn where no tool is available: nothing runs after exhaustion.
- The stop message says this is a limit, not the end of the task.
In non-interactive mode (lemni -p), there is nobody to ask for an extension: the
session stops with exit code 3, distinct from a success (0) and a crash (1). A
script thus knows the work is not finished without confusing it with an error.
Limits are set in the configuration file, under the execution key:
execution: sessionMaxActions: 150 sessionMaxMinutes: 45 sessionMaxExtensions: 5The LEMNISCATE_SESSION_* environment variables are read as a fallback, and the configuration
takes precedence. This fallback applies on both surfaces: in the editor as in the
terminal, precedence is defaults, then environment, then configuration. An
unreadable value is refused with a message; it is not replaced by the default.
The environment fallback is resolved once per process: after fixing the variable,
restart the editor so it is read again.
Going in circles: loop detection
Section titled “Going in circles: loop detection”The cap bounds length; it does not see an agent reading the same file twenty times. Loop detection catches this case: when the same tool call, with exactly the same arguments, comes back a fourth time in a row, the call is blocked before it runs and the session is handed back to the user, along with what was looping.
Two choices are worth knowing:
- The model receives an explicit message, “blocked for repetition, do not retry”. Without this message, it would retry and the guardrail would itself become the loop.
- The window is consecutive: reading a file again after changing it, or reading it three times over the course of a long session, triggers nothing.
In non-interactive mode, the exit code is the same, 3: from a script’s point of view, a guardrail stopped the agent.
execution: toolLoopThreshold: 3 # répétitions identiques consécutives tolérées toolLoopDetection: false # désactivation possible, mais toujours expliciteStepping outside the project: file scopes
Section titled “Stepping outside the project: file scopes”The agent’s scope is the copy of the repository authorized by the project policy (guarantee G01). File tools enforce this scope before any read or write, in addition to the sandbox confinement. “The project” is the root of the git repository (the working directory outside a repository), symbolic links are resolved before the check, and a file to be created is judged on its path.
- For writes as for reads (
Read,List,Search), a path outside the project is refused, with the reason, without a permission request: the terminal often runs with nobody to answer, and a request with no one to answer it is a blockage. - Shell commands (
Bash) do not go through this path check: they are analyzed by their own guardrail (@lemniscate/terminal-security), and bounded by the sandbox.
Agents: one format, rights that only narrow
Section titled “Agents: one format, rights that only narrow”An agent is a Markdown file: the YAML frontmatter carries the configuration, the body of the file is the system prompt.
---description: Relit le code avant une PRtools: Read, SearchsessionMaxActions: 25---
Tu es un relecteur de code exigeant…- The file name is the agent’s name.
revue.mdis invoked withlemni --agent revue. Anamefield in the frontmatter is ignored with a warning, and a future version will refuse it. - Agents are discovered in the project’s
.lemniscate/agents/and in~/.lemniscate/agents/. On a name collision, the repository’s wins. A discovered agent is available; it is not active on its own. - Loading an agent does not widen rights. An agent with no
toolskey inherits the user’s permissions, a declaredtoolslist closes the catalog instead of opening it, andplanmode survives the loading of an agent. sessionMaxActionsin the frontmatter overrides the global cap for the sessions this agent drives.disable: trueremoves an agent without deleting its file;hidden: truehides it from autocompletion while leaving it invocable by name.
Activating an agent in the editor
Section titled “Activating an agent in the editor”From VS Code and JetBrains, a discovered agent is activated through the agent selector in the input bar, next to the mode and model selectors. The selector shows the name of the active agent as long as it drives the session, and No agent when idle. It only offers activatable agents, and does not appear when no agent is discovered.
An activation applies, on par with lemni --agent:
- the persona (the body of the file), which becomes the session’s system prompt;
- the
toolslist, which closes the catalog: a tool not listed, MCP tools included, is not offered to the model, and the agent can only restrict (a tool disabled by your settings stays excluded whatever the agent declares, chat mode stays without tools, plan mode stays read-only); sessionMaxActions, which bounds the driven session, with the same precedence as in the terminal (defaults, environment, configuration, agent);- the declared
model, selected if it matches a configured model. If it cannot be found, a warning appears and the current model is kept.
The refusals are the same as in the terminal: a mode: subagent agent is reserved for the
delegation tool, disable: true is neither listed nor activatable, and a namesake of the
reserved names general and explore is discarded and flagged. Activation is a session
state: a new session starts again with no agent, and a configuration reload that
makes the active agent disappear (file deleted, switched to disable, turned
mode: subagent) deactivates it and says so.
Two limits are announced at activation:
- the
ruleskey (registry rule packs) is not loaded by the editor: the persona and the tool restrictions apply, the registry rules do not; - an MCP reference in the
toolslist (owner/paquetor URL) is resolved by the editor against the configured server name, not against the registry slug as in the terminal. If no configured server bears that name, all the tools of the reference stay excluded: closure works in the direction of restriction.
Delegating to subagents
Section titled “Delegating to subagents”The agent has a delegation tool (Task): it hands an instruction to a subagent,
which runs in its own session, with its own context and its own guardrails, and
returns a final report. The mental model is developed in
Delegating to subagents, and the
procedure in
Delegate a task to a subagent.
The rules:
- Nothing is delegatable without an explicit
mode. An agent discovered in a repository or on the workstation is not delegatable by its mere presence: its frontmatter must declaremode: subagent(delegatable by an agent only) ormode: all(delegatable and invocable by the user). Withoutmode, an agent stays invocable by the user only. - Two subagents built into the product are delegatable without installation:
general(generalist) andexplore(read-only explorer, which declares onlyRead,List,Search). Their names are reserved on both funnels: a discovered file with the same name is flagged and discarded from delegation, and it is not invocable through--agent generaleither. Loading such a file remains possible by passing its explicit.mdpath. - A subagent does not widen the parent’s rights. Its policy is the parent’s,
restricted by its persona: a tool excluded for the parent stays excluded for it
whatever its frontmatter declares, the parent’s
planmode leaves it read-only, and an undeclared MCP server is neither described nor accessible to it. - The cascade is bounded by global safety nets, adjustable under the
executionkey:subagentMaxDepth(depth, default 3;1forbids any nesting),subagentMaxPerSession(default 200) andsubagentMaxConcurrent(default 20). When a limit is reached, the agent receives an explicit error and finishes the work itself. Zero is refused on these keys: a cascade safety net is adjusted, not removed. - A subagent’s budget is its own (nothing is deducted from the parent) and has no
action cap by default; a
sessionMaxActionsset in its frontmatter applies, and loop detection stays active in every subagent. A guardrail stop in the subagent is marked in the report returned to the parent, which distinguishes “work cut short” from “work finished”. - A subagent’s duration limit comes from the workstation configuration (default 45 minutes), not from the time already consumed by the parent: each child session starts again with its full duration budget, and with no possible extension, for lack of a human in the child session.
- Permission requests come up to you, not to the model. In an interactive
session, a tool in
askfor a subagent appears on the usual approval screen, the single approval point: there is only one human surface per process, all the requests from the tree converge there, and each one is labeled with the name of the agent making the request. That name is the agent’s file name, as is: only the two built-in names (general,explore) are protected against namesakes; a discovered agent with a misleading name is still shown under that name. In non-interactive mode (-pmode,lemni serve, background task), the request becomes a flat, announced refusal, with no self-approval by the model, and the tool call log attributes this refusal to the automated process. - The
Questiontool is offered to the main session, in an interactive terminal; a delegated agent does not have it in its catalog. Faced with an ambiguity, the main session asks you a question at the approval point, with suggested options or a free-form answer. Escape rejects it, and the agent then states its assumption and continues. With no reachable user, the answer is an announced default, which asks the model to state its assumption and carry on. - Background:
Taskwithbackground: trueimmediately returns atask_idand the subagent runs detached. The end is announced in the conversation (terminal bell opt-in:LEMNISCATE_NOTIFY_BELL=1), and the report is read with theTaskResulttool or in the persisted child session. In one-shot execution (lemni -p), the background falls back to an announced synchronous execution, because the process does not survive the turn. Onlemni serve, a durable process even without a screen, detachment is real and thetask/startedandtask/settledevents announce it on the event stream; permission requests from a detached task stay at a flat refusal there, as everywhere nobody can approve. A detached task dies with the process that carries it: closing the terminal (or stopping the server) interrupts the tasks in progress, without notice, and their trace stops in the persisted child session. @mention in the terminal:@general <consigne>launches a delegatable subagent yourself, as a detached task, under the same global safety nets as the model’s delegations. Any other@keeps its meaning as a file mention.- Interrupting the parent interrupts its delegations: interrupting a turn propagates to the synchronous subagents in progress, and their report comes back marked “interrupted”. A detached task survives the turn and stays bounded by its duration, its loop detection and the global safety nets.
- The child session is persisted like an ordinary session: what the subagent did can be read back afterwards.