Browse docsConfiguration

Configuration

Enso reads machine-level settings from $ENSO_HOME/config.json. If ENSO_HOME is unset, the home is ~/.enso.

The configuration controls:

  • which Slack and Telegram accounts Enso serves;
  • which conversations map to which workspaces;
  • which provider CLIs, models, and effort levels may run;
  • interactive timeouts, log rotation, run-history retention, and web-viewer defaults.

config.json is loaded when the service starts. After changing it, validate it and restart the service. Job definitions are different: JOB.md files are reloaded by the scheduler every minute. See Jobs.

Before editing

config.json is strict JSON, not JSONC. Comments, trailing commas, single-quoted strings, and unquoted keys are invalid.

enso config check
enso config show
enso doctor
  • enso config check reports all configuration problems it can find at once.
  • enso config show prints the stored document and redacts object keys ending in token. It does not print a normalized or fully defaulted configuration.
  • enso doctor also checks provider executables, workspace health, and service status.

Enso refuses to serve chat when the configuration has an error. Warnings, such as a provider executable not currently being found, do not make the JSON invalid, but they usually indicate the machine is not ready to run.

Protect the file because it contains transport credentials:

chmod 600 "$ENSO_HOME/config.json"

If ENSO_HOME is not set in your shell, use ~/.enso/config.json instead.

A minimal configuration

This is valid strict JSON after replacing the example values. The default workspace must already exist.

{
  "version": 1,
  "transports": {
    "telegram": {
      "bot_token": "REPLACE_WITH_TELEGRAM_BOT_TOKEN",
      "allowed_users": ["123456789"],
      "notify": "123456789"
    }
  },
  "bindings": {
    "telegram:123456789": "default"
  },
  "defaults": {
    "provider": "claude",
    "model": "sonnet",
    "effort": "high"
  },
  "providers": {
    "claude": {
      "path": "claude",
      "models": ["opus", "sonnet", "haiku"],
      "args": ["--dangerously-skip-permissions"]
    }
  }
}

Create the workspace if needed, then validate:

enso workspace create default
enso config check

Complete example

This example shows every top-level section. It is strict JSON and contains no comments; replace tokens, IDs, paths, model choices, and permission flags for your machine.

{
  "version": 1,
  "transports": {
    "slack": {
      "bot_token": "xoxb-REPLACE_ME",
      "app_token": "xapp-REPLACE_ME",
      "notify": "C0123456789",
      "mention_required": false,
      "thread_mention_required": false
    },
    "telegram": {
      "bot_token": "REPLACE_ME",
      "allowed_users": ["123456789"],
      "notify": "123456789"
    }
  },
  "bindings": {
    "slack:dm:U0123456789": "default",
    "slack:C0123456789": "default",
    "slack:C9876543210": "research",
    "telegram:123456789": "default"
  },
  "defaults": {
    "provider": "claude",
    "model": "sonnet",
    "effort": "high"
  },
  "workspaces": {
    "research": {
      "agent": {
        "provider": "codex",
        "model": "sol",
        "effort": "xhigh"
      }
    },
    "default": {
      "providers": {
        "claude": {
          "args": ["--permission-mode", "dontAsk"]
        }
      }
    }
  },
  "providers": {
    "claude": {
      "path": "claude",
      "models": ["opus", "sonnet", "haiku"],
      "args": ["--dangerously-skip-permissions"]
    },
    "codex": {
      "path": "codex",
      "models": ["sol", "terra", "luna"],
      "args": ["--dangerously-bypass-approvals-and-sandbox"]
    },
    "grok": {
      "path": "grok",
      "models": ["grok-4.6", "grok-4.5"],
      "args": ["--always-approve"]
    }
  },
  "agent": {
    "timeout": 3600
  },
  "logging": {
    "level": "INFO",
    "max_bytes": 10485760,
    "backups": 5
  },
  "runs": {
    "keep": 500,
    "max_age_days": 30
  },
  "web": {
    "host": "127.0.0.1",
    "port": 8787
  }
}

Required and optional sections

Key Required Contract
version Yes Must currently be the integer 1.
transports Yes Configure Slack, Telegram, or both. At least one is required.
providers Yes A non-empty object containing supported provider names.
defaults Yes A complete provider/model/effort triple for interactive conversations.
bindings No Conversation-to-workspace mappings. An empty object is allowed.
workspaces No Agent or provider-argument overrides for selected workspaces.
agent No Interactive execution settings.
logging No Log level and rotation.
runs No Finished job-run retention.
web No Default bind host and port for the viewer.

Unknown nested behavior should not be assumed to exist merely because JSON accepts an extra key. Use the CLI reference and enso config check as the public contract.

Transports

Slack and Telegram can run together in the same Enso process.

Slack

{
  "transports": {
    "slack": {
      "bot_token": "xoxb-REPLACE_ME",
      "app_token": "xapp-REPLACE_ME",
      "notify": "C0123456789",
      "mention_required": false,
      "thread_mention_required": false
    }
  }
}

Slack requires bot_token and app_token. The app token is used for Socket Mode.

The optional notify value is a bare Slack conversation ID beginning with C, G, or D. It is the transport’s fallback target for job failure alerts and untargeted enso message send calls.

mention_required controls whether a top-level channel message must mention the bot. thread_mention_required applies separately to replies inside a thread. Both default to false and must be JSON booleans.

Invite the bot to every channel you bind. A valid binding does not add the bot to Slack for you.

Telegram

{
  "transports": {
    "telegram": {
      "bot_token": "REPLACE_ME",
      "allowed_users": ["123456789", 987654321],
      "notify": "123456789"
    }
  }
}

Telegram is limited to private chats. allowed_users is a list of exact, positive numeric Telegram user IDs; JSON integers and canonical decimal strings are accepted. Anyone outside the list is ignored silently. An empty or omitted list therefore authorizes nobody.

The optional notify target is one positive Telegram user ID. It may be an integer or a canonical decimal string, although strings are often easier to handle consistently.

Notification destination forms

Transport-level notify values are bare IDs. Job and CLI destinations may be:

Destination Example
Explicit Slack slack:C0123456789
Explicit Telegram telegram:123456789
Bare ID C0123456789 or 123456789, only when exactly one transport is configured

With both transports configured, always prefix job and CLI destinations so the target is unambiguous.

Conversation bindings

A binding maps a transport conversation to a workspace:

Binding key Conversation
slack:C… or slack:G… Slack channel; each top-level message starts a thread and conversation
slack:dm:U… Direct messages with one Slack user
telegram:<user-id> A Telegram private chat
{
  "bindings": {
    "slack:C0123456789": "research",
    "slack:dm:U0123456789": "default",
    "telegram:123456789": "default"
  }
}

Workspace names are lowercase kebab-case and at most 64 characters. Every bound workspace directory must exist at $ENSO_HOME/workspaces/<name>; a missing directory is a configuration error. Create it with:

enso workspace create research

A binding for an unconfigured transport is a warning. It is usually stale configuration and cannot receive messages until that transport is configured. Bindings take effect only after the service restarts.

See Workspaces for layout, instructions, uploads, and auditing.

Providers

Enso currently recognizes three provider keys: claude, codex, and grok.

Each configured provider has:

Field Meaning
path Executable path. A leading ~ is expanded; a bare command is resolved from PATH. If omitted, the provider name is used.
models Non-empty list of model names that configuration and jobs may select.
args String arguments appended verbatim to every invocation. Defaults to an empty list.

The initial setup choices use these model names and effort vocabularies:

Provider Initial model names Accepted effort levels
Claude opus, sonnet, haiku low, medium, high, xhigh, max
Codex sol, terra, luna low, medium, high, xhigh, max, ultra
Grok grok-4.6, grok-4.5 low, medium, high, xhigh

Codex’s setup aliases map sol, terra, and luna to their corresponding installed Codex model IDs. The models list controls what Enso accepts; it does not prove that the installed provider CLI can actually use a name. Use enso doctor and test the provider on the machine.

Enso clamps an effort downward when it exceeds the known maximum for a selected model, and logs the change. It does not silently find another model.

Permissions and authentication

Enso delegates authentication, sessions, filesystem access, and approval behavior to the provider CLI. Provider args are not interpreted or made safer by Enso. The unattended flags shown in setup and in the example grant broad authority:

  • Claude: –dangerously-skip-permissions
  • Codex: –dangerously-bypass-approvals-and-sandbox
  • Grok: –always-approve

Choose those flags deliberately. A workspace is a working directory and context boundary, not an operating-system sandbox. A provider may be able to read or change files and use credentials elsewhere on the machine. Treat every enabled provider, workspace skill, job script, and secret as part of one trusted automation system.

Defaults and workspace overrides

defaults is one complete interactive-agent selection:

{
  "defaults": {
    "provider": "claude",
    "model": "sonnet",
    "effort": "high"
  }
}

All three fields are required. The provider must be configured, the model must appear in that provider’s models list, and effort must be valid for that provider.

A workspace may replace the complete triple:

{
  "workspaces": {
    "research": {
      "agent": {
        "provider": "codex",
        "model": "sol",
        "effort": "xhigh"
      }
    }
  }
}

It may also replace one configured provider’s arguments:

{
  "workspaces": {
    "default": {
      "providers": {
        "claude": {
          "args": ["--permission-mode", "dontAsk"]
        }
      }
    }
  }
}

These are replacement rules:

  • a workspace agent block replaces the whole default triple and must include provider, model, and effort;
  • workspaces.<name>.providers.<provider>.args replaces that provider’s global argument list for the workspace; it is not appended to it.

Only workspaces that need overrides belong in the workspaces configuration object.

Jobs do not inherit this interactive default triple. Every JOB.md explicitly declares its provider, model, effort, and workspace. Workspace-specific provider arguments still apply when that provider runs in the named workspace.

Interactive timeout

{
  "agent": {
    "timeout": 3600
  }
}

agent.timeout is the interactive-turn timeout in seconds. It defaults to 3600 and must be a non-negative integer. A value of 0 disables the interactive timeout. Jobs use their own required/defaulted timeout field instead.

Secrets and environment

Provider credentials and values used by job scripts can live in:

$ENSO_HOME/secrets/*.env

Create and protect the directory:

mkdir -p "$ENSO_HOME/secrets"
chmod 700 "$ENSO_HOME/secrets"
chmod 600 "$ENSO_HOME/secrets/"*.env

Each file contains simple assignments:

GITHUB_TOKEN=replace-me
export ANOTHER_TOKEN="replace-me-too"

This parser is intentionally small:

  • blank lines and lines without = are ignored;
  • an optional leading export is removed;
  • lines whose parsed key begins with # are ignored;
  • a variable already present in the service environment wins;
  • surrounding whitespace is stripped, followed by any leading or trailing single- or double-quote characters;
  • the file is not a shell script: no variable expansion, command substitution, or shell escaping is performed.

Secrets are loaded when Enso starts and inherited by provider processes and job scripts. Restart after changing them. There is no per-workspace secret boundary: all agents and scripts launched by the service share the environment.

Transport bot tokens are required directly in config.json; the JSON loader does not substitute environment-variable references into those fields.

Logs and run retention

Defaults:

{
  "logging": {
    "level": "INFO",
    "max_bytes": 10485760,
    "backups": 5
  },
  "runs": {
    "keep": 500,
    "max_age_days": 30
  }
}

logging.level is DEBUG, INFO, WARNING, or ERROR; case is normalized. The byte and backup limits, along with both run-retention values, must be non-negative integers.

Finished run rows are pruned if they are older than max_age_days or outside the newest keep rows. Rows still marked running are not pruned by that rule.

Run history retains only the final 1 MiB of a job’s provider output in enso.db. That output is not automatically written into the workspace. If durable output belongs in drafts/ or knowledge/, instruct the agent to write it there or use a postrun script. See Jobs.

Use DEBUG logging carefully on a multi-user machine: verbose provider and routing details can reveal more operational context than normal logs.

Web viewer

{
  "web": {
    "host": "127.0.0.1",
    "port": 8787
  }
}

These values are defaults for enso web start; command-line flags override them. host must be a non-empty string. port must be an integer from 1 through 65535.

The viewer can still start with its safe defaults when config.json is missing or invalid, so its Health page can show the problem. It is read-only and has no built-in authentication. Keep it on 127.0.0.1 unless a trusted access layer protects it. See Web viewer.

Chat commands

Slack commands use !; Telegram commands use /.

Command Effect
stop Stops the process running for this conversation and drops its queued turns.
clear Forgets stored provider sessions for the conversation, so the next turn starts fresh.
status Shows the effective workspace and agent, session age, active process, and queue depth.
help Lists commands.
restart Replies, then restarts the service or re-executes enso serve.

Session state is keyed by conversation and provider and can only resume in the workspace where it began. Idle session database entries are pruned after 30 days when the service starts. Clearing or invalidating a session may also remove provider-owned local session artifacts for providers that expose them; it is more than a visual chat-history reset.

Safe edit procedure

  1. Make sure every workspace named by a binding exists.
  2. Edit $ENSO_HOME/config.json as strict JSON.
  3. Run enso config check.
  4. Run enso doctor if providers, paths, or workspaces changed.
  5. Restart Enso.
  6. Send status in the affected chat and confirm the effective workspace, provider, model, and effort.

Troubleshooting

JSON fails before schema validation

Remove comments and trailing commas, use double quotes, and validate the raw document:

python -m json.tool "$ENSO_HOME/config.json" >/dev/null
enso config check

A binding says its workspace is missing

The configuration deliberately fails closed when there is nowhere to run:

enso workspace create research
enso workspace audit research
enso config check

The provider path is not executable

Confirm the same environment that starts Enso can resolve the command:

command -v claude
enso doctor

A terminal’s PATH may differ from a background service’s. An absolute provider path avoids that ambiguity.

A workspace still uses global provider arguments

Check that the workspace name exactly matches the binding or job, and remember that the override must contain an args array:

{
  "workspaces": {
    "research": {
      "providers": {
        "codex": {
          "args": ["--dangerously-bypass-approvals-and-sandbox"]
        }
      }
    }
  }
}

Restart after changing config.json.

Alerts are not delivered

Check, in order:

  1. the job’s explicit notify value;
  2. the configured transports’ notify values;
  3. whether an explicitly prefixed destination names a configured transport;
  4. whether the Slack bot is in the target channel or the Telegram user has started the bot and can receive its outbound message.

Successful and no_work jobs are intentionally silent unless the prompt sends a message itself.

Machine-readable checklist

An agent editing Enso configuration should preserve these invariants:

  • emit strict JSON with version: 1;
  • configure at least one transport and at least one supported provider;
  • keep every agent selection a complete provider/model/effort triple;
  • use only models declared for that provider and valid provider effort strings;
  • create each bound workspace before referencing it;
  • treat workspace provider arguments as replacements;
  • restart for configuration or secret changes;
  • never describe a workspace as a security sandbox;
  • never assume retained job stdout was saved as a workspace file.

Continue with Workspaces, Jobs, or the CLI reference.