# Security and policies

Enso runs agent tools on a machine you control. A workspace organizes their context;
the provider CLI's permissions determine what those tools can do. Instructions in
`AGENTS.md` guide the model, but do not enforce filesystem or network restrictions.

Workspaces are unrestricted by default. Setup records unattended provider flags,
including Codex's flag that disables its sandbox. Before connecting sensitive files
or a shared chat, choose the provider permissions you want and test them.

## What restricted means

Set `workspaces.NAME.restricted` to `true` to require a provider policy file before
Enso starts that workspace's chat provider, job provider, or heartbeat assessment.
Enso checks:

1. The expected policy path exists as a file.
2. The effective provider arguments do not contain a bypass flag Enso recognizes.
   Currently these are Codex's `--dangerously-bypass-approvals-and-sandbox` and `--yolo`.
3. For Codex and Grok, the Enso home is trusted in that provider's own user
   configuration. Grok also accepts `--trust` in the workspace's arguments instead.

A failed check prevents that provider invocation and reports what is missing.
An empty or invalid policy can still pass Enso's file check: **Enso does not parse
the rules or prove that the provider enforces them.** Other flags and provider
configuration can change the effective permissions.

| Provider | Policy path inside the workspace |
| --- | --- |
| Claude Code | `.claude/settings.json` |
| Codex | `.codex/config.toml` |
| Grok | `.grok/config.toml` |
| OpenCode | `opencode.json` |

Antigravity has no supported workspace policy. Enso refuses `agy` in a restricted
workspace; choose another provider before enabling the restriction.

### Scripts and background work

The restriction checks provider invocations. Job prerun/postrun scripts, heartbeat
gates, and workflow commands, checks, and lifecycle scripts run as ordinary processes
with the service account's access. They do not inherit the provider's sandbox.
A prerun or gate can run even when the provider's policy is missing; a command-only
stage does not need a provider policy at all. See [Jobs](/docs/jobs/#prerun-scripts),
[Heartbeat](/docs/heartbeat/#execution), and [workflow availability](/docs/workflows/#availability).

A job or beat can use a different provider from chat. If chat uses Claude and a job
uses Codex, the workspace needs both policy files and each provider's launch settings.

## Example: read-only Codex

This example lets Codex inspect files without writing through its sandboxed tools.
It does not limit reads to the workspace or remove secrets from the environment.
Review connected tools separately; filesystem permissions do not define what a remote
service will let a tool do.

Start with an initialized Enso home and Codex installed, signed in, and configured
under `providers.codex`. The example uses the configured model `sol`; substitute a
model from `enso config show` if your configuration differs. Run setup commands from
your own terminal, outside an Enso agent run. Paths below use the default `~/.enso`;
substitute your `ENSO_HOME` when it differs.

### 1. Create the workspace and policy

```bash
enso workspace create research
mkdir -p ~/.enso/workspaces/research/.codex
nvim ~/.enso/workspaces/research/.codex/config.toml
```

Put these settings in that file. When adapting an existing workspace, merge them
with its configuration instead of replacing unrelated settings.

```toml
sandbox_mode = "read-only"
approval_policy = "never"
```

`never` disables approval prompts; it does not disable the sandbox. An action outside
the sandbox must remain denied. OpenAI documents this combination for
[non-interactive read-only use](https://learn.chatgpt.com/docs/agent-approvals-security).

### 2. Trust the Enso home

Codex loads project configuration only under a trusted root. Enso's root is the home,
not `workspaces/research`. Find its absolute, resolved path:

```bash
cd ~/.enso
pwd -P
```

In `~/.codex/config.toml` (or `$CODEX_HOME/config.toml`), add or update the entry for
that exact path. Use the actual path in place of this example, without a trailing slash:

```toml
[projects."/Users/you/.enso"]
trust_level = "trusted"
```

This trusts the root used by all workspaces in that home. It does not grant them
write access. CLI flags can still override project settings; see
[Codex configuration precedence](https://learn.chatgpt.com/docs/config-file/config-basic).

### 3. Select the provider and enable the check

```bash
enso config set workspaces.research.agent '{"provider":"codex","model":"sol","effort":"high"}'
enso config set workspaces.research.providers.codex.args '[]'
enso config set workspaces.research.restricted true
enso workspace audit research
```

The empty argument list replaces the global list, removing setup's bypass flag.
Enable `restricted` last, after the policy and trust entry are ready. These Enso
settings apply to subsequent work without a service restart; finish active work
before changing its permissions.

### 4. Try a harmless denied action

Create a small text file at `knowledge/policy-probe.txt` yourself. Confirm that
`drafts/policy-probe.txt` does not already exist, then run from the workspace:

```bash
cd ~/.enso/workspaces/research
codex exec 'Read knowledge/policy-probe.txt, then try to create drafts/policy-probe.txt containing only probe. Report the actual tool results.'
```

The read should succeed and the write should be denied. Inspect the tool result and
confirm the output file is absent; a model saying it complied or a successful process
exit is not proof. This uses your provider account. Repeat through a chat bound to
`research` to check the Enso path too; [bindings](/docs/configuration/#bindings) explains
how to select that workspace.

For a workspace that needs to write drafts, change `sandbox_mode` to `workspace-write`
and keep `approval_policy = "never"`. Test both an intended write and a prohibited one
outside its writable roots. This remains a provider sandbox, whose writable roots and
protected paths follow [Codex's rules](https://learn.chatgpt.com/docs/agent-approvals-security).

The sandbox limits which files a command may touch, not which commands may run. Codex
also reads command rules from `.codex/rules/*.rules` beside the workspace's `config.toml`,
under the same trusted root. To refuse one command, add
`~/.enso/workspaces/research/.codex/rules/enso.rules`:

```text
prefix_rule(pattern=["enso", "config"], decision="forbidden")
```

## Example: selected commands in Claude Code

Suppose a workspace should inspect files and run `pwd` and `git status`, while other
commands that need approval should be denied without waiting for a person.
Start with Claude Code installed, signed in, and configured under `providers.claude`.

```bash
enso workspace create review
mkdir -p ~/.enso/workspaces/review/.claude
nvim ~/.enso/workspaces/review/.claude/settings.json
```

Save this policy, merging with existing settings when applicable:

```json
{
  "permissions": {
    "allow": ["Bash(pwd)", "Bash(git status)"],
    "deny": ["Bash(enso config *)", "Bash(touch *)", "Edit", "Write"]
  }
}
```

Then select an existing configured model and replace the unattended bypass arguments:

```bash
enso config set workspaces.review.agent '{"provider":"claude","model":"opus","effort":"high"}'
enso config set workspaces.review.providers.claude.args '["--permission-mode","dontAsk"]'
enso config set workspaces.review.restricted true
enso workspace audit review
```

In `dontAsk` mode, calls that would prompt are denied. File reads and other actions
that need no approval still run, as can tools allowed by other settings. This example
controls named tools and commands; it is not an operating-system sandbox. See
[Claude Code permissions](https://code.claude.com/docs/en/permissions) for matching and
configuration precedence.

From the workspace, test one allowed command and one explicit denial:

```bash
cd ~/.enso/workspaces/review
claude --permission-mode dontAsk -p 'Run pwd, then try touch drafts/policy-probe.txt. Report the actual tool results.'
```

Start with no probe file. `pwd` should run and `touch` should be denied; inspect the
tool result and verify no file was created. Repeat through the bound Enso chat.

Setup's `--dangerously-skip-permissions` is unsuitable for this allowed-command
example: Enso's earlier checks found deny rules still applied under that flag, but
allow rules did not narrow its access. Use the complete `dontAsk` override above.

## Grok and OpenCode starting policies

These minimal policies deny the plain `enso config` command. Add the actions your
workspace must prohibit; these examples do not otherwise lock down its access.
As above, configure the provider first, write its file, set its complete workspace
argument list, then enable `restricted` and verify behavior.

### Grok

In the workspace's `.grok/config.toml`:

```toml
[permission]
deny = ["Bash(enso config *)"]
```

Set `workspaces.NAME.providers.grok.args` to `["--always-approve", "--trust"]`.
`--trust` records trust for the home. Alternatively, add this entry to
`~/.grok/trusted_folders.toml` (or `$GROK_HOME/trusted_folders.toml`) and omit `--trust`:

```toml
[folders."/Users/you/.enso"]
trusted = true
```

Use the home's resolved absolute path; Grok also accepts its trailing-slash spelling.
From inside the workspace, `grok inspect --json` should report `projectTrusted: true`
and `permissions.loaded` greater than zero. Also test an intended allow and denial.

### OpenCode

In the workspace's `opencode.json`:

```json
{
  "permission": {
    "bash": {
      "*": "allow",
      "enso config *": "deny"
    }
  }
}
```

Set `workspaces.NAME.providers.opencode.args` to `["--auto"]`. Put broad patterns
before specific ones because the last matching rule wins. `--auto` approves requests
that the policy does not deny. See [OpenCode permissions](https://opencode.ai/docs/permissions/)
for other tools and file access. From the workspace, use
`opencode run --auto "Run enso config show"` to test the denial; it should not print
the configuration.

Earlier Enso verification on September 10, 2026 used Codex
0.153.4, Grok 1.0.13, and OpenCode 1.18.27; the Claude version was not recorded.
Those observations are not a guarantee for every version. Recheck after CLI upgrades
or changes to provider flags, user configuration, plugins, or tools.

## Auditing and troubleshooting

`enso workspace audit NAME` checks the home and that workspace against Enso's expected
layout, and adds one `policy` check for the workspace's **chat provider only**. It does
not parse policy rules, test a denied action, or validate every provider used by jobs and
beats. `--fix` does not write a policy for you.

The command exits `1` while any error remains and `0` otherwise; warnings never fail it.
Add `--json` to check it from a script. [`enso doctor`](/docs/cli/#operating) reports the
same audit beside configuration, provider, transport, and service checks. `enso serve`
logs a startup warning for a failing workspace that is bound or named by a job, and then
keeps running: that warning is a notice, not a refusal.

For example, a Claude chat workspace can pass its audit while a Codex job in the same
workspace fails because `.codex/config.toml` is missing. Review each job's agent and
each beat's saved agent, then verify the provider they actually use.

| Symptom | What to check |
| --- | --- |
| Missing policy error | Put a file at the exact provider path inside the workspace. A directory at that path does not count. |
| Codex bypass error | Replace the workspace's entire Codex argument list; check for both bypass spellings. |
| Trusted-root error | Use the resolved home path in the provider's own trust configuration, not the workspace path. |
| Audit passes but actions are allowed | Inspect policy syntax, loaded configuration, other flags, and the actual tool used. A file-presence check cannot establish enforcement. |
| Chat works but a job or beat fails | Check its saved provider and workspace; chat defaults do not replace its agent. |
| Agent cannot change its own settings | Make the intended change from your own terminal after active work finishes. |

## Limits and shared access

**Policy files are not protected by Enso.** A provider with sufficient file access
can edit its policy or `config.json` directly. A command deny rule covers matching
commands, not every way to produce the same effect. The `enso config` guard also
depends on the caller's `ENSO_WORKSPACE` environment variable, which a process can
change. These measures help prevent accidental changes; hostile-process isolation
requires separately enforced OS/provider controls and protected controller state.

**Chat access grants access to the agent's capabilities.** Telegram requires a
private chat, an allowed user, and a binding. Slack channel bindings accept human
participants subject to mention/thread rules; Slack has no Enso per-user allowlist
for a bound channel. A mention requirement does not identify an authorized person.
For personal use, bind your DM; for shared use, choose channel membership and provider
permissions accordingly. See [chat access](/docs/configuration/#bindings).

**Credentials and connected accounts are shared resources.** Values loaded from
[`secrets/*.env`](/docs/configuration/#secrets) are inherited by agents across
workspaces. A workspace does not create a separate OS account. Browser profiles
contain [signed-in sessions](/docs/browser/#private-data-and-boundaries), and
[Cloud teams](/docs/cloud/teams/#sharing-the-computer) share the instance.

**Enso's own commands are not scoped to one workspace.** Where the development
[Knowledge](/docs/knowledge/#availability) and [Memory](/docs/memory/#availability)
features are installed — neither is in v0.1.2 — an agent can list and print another
workspace's notes with `enso knowledge list|show --scope workspace:<name>`, and read
conversation summaries from every workspace with
`enso memory search "deploy" --all-workspaces`. The bundled memory-refinement job
also runs in the default workspace, on batches that can include exchanges captured in a
restricted one. Workspaces organize context; they are not an access-control boundary.

**Read-only viewing still exposes private data.** The [web viewer](/docs/web/#access)
has no built-in authentication and can show files, prompts, and logs. Keep it on
loopback or behind authenticated access.

**Instructions are guidance.** Treat fetched pages, attachments, and tool output as
untrusted data. Instructions and skills can tell an agent to ignore embedded commands,
but do not guarantee resistance to prompt injection or grant new action approvals.
