- Sandbox (bubblewrap) for non-admin shell/git, MCP per-user rights, hard budgets and quotas, login rate limiting, audit log and backups - Ghidra headless decompilation (ARM64 natives built from source), binary_info tool, clean room / deep workflow per session - Recreate skill and commands for analysing and re-building old software - File checkpoints with rewind/restore, background shells (bash_output, kill_shell) - web_search tool (DuckDuckGo, SearXNG, Brave, Tavily) - In-app dialogs and toasts instead of browser dialogs - 15 base themes (trilium-themes palettes plus Nord, VS Code, GitHub, Catppuccin, One, Dracula, Rose Pine, Everforest, Kanagawa) with light/dark/auto switcher and license notes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
326 lines
14 KiB
TOML
326 lines
14 KiB
TOML
# =============================================================================
|
||
# CodeCraft – configuration
|
||
# =============================================================================
|
||
#
|
||
# This file is the single source of truth for the server. Everything here can
|
||
# also be edited in the web UI (Settings). The UI edits this file in place and
|
||
# keeps your comments and formatting intact.
|
||
#
|
||
# Location: $CODECRAFT_CONFIG, otherwise <data_dir>/config.toml (Docker: /data/config.toml)
|
||
#
|
||
# Secrets: any string value may reference environment variables with ${NAME},
|
||
# e.g. api_key = "${ANTHROPIC_API_KEY}". Prefer this over literal keys – keys
|
||
# entered in the UI are stored here in plain text.
|
||
# =============================================================================
|
||
|
||
[server]
|
||
# Address the HTTP server listens on.
|
||
bind = "0.0.0.0:8787"
|
||
|
||
# Root directory the agents may work in. Sessions pick a project folder below it.
|
||
# Docker: mount your projects here (see docker-compose.yml).
|
||
workspace = "/workspace"
|
||
|
||
# Where sessions (SQLite) and runtime state are stored.
|
||
data_dir = "/data"
|
||
|
||
# Default model for new sessions, as "<provider>/<model>".
|
||
default_model = "anthropic/claude-sonnet-5-5"
|
||
|
||
# Cheap/fast model for titles and history compaction. Falls back to default_model.
|
||
small_model = "anthropic/claude-haiku-5-5"
|
||
|
||
# Compact the conversation automatically when the context is this full (0.0–1.0).
|
||
auto_compact_threshold = 0.85
|
||
|
||
# Max. subagents running at the same time (all sessions). Additionally, each
|
||
# subagent can be limited with max_parallel, and each provider/model with
|
||
# max_concurrency (see below).
|
||
max_parallel_subagents = 4
|
||
|
||
# MCP servers with more tools than this are loaded on demand (see [mcp.*] load).
|
||
mcp_direct_limit = 15
|
||
|
||
# Currency label for the cost statistics (prices below are in this currency).
|
||
currency = "USD"
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Providers
|
||
# -----------------------------------------------------------------------------
|
||
# kind = "anthropic" → Anthropic Messages API (and compatible endpoints)
|
||
# kind = "openai" → OpenAI-compatible /chat/completions (Gemini, opencode Go,
|
||
# fal, OpenRouter, Ollama, vLLM, llama.cpp, LM Studio, ...)
|
||
#
|
||
# models: optional list. If empty, the model list is fetched from <base_url>/models.
|
||
# Per-model options: context (tokens), max_output (tokens), reasoning (bool),
|
||
# vision (bool, can read images), effort ("low" | "medium" | "high" | "xhigh" | "max").
|
||
#
|
||
# Prices (per 1M tokens, used for the statistics):
|
||
# input_price, output_price, cache_read_price, cache_write_price
|
||
# Cache prices default to input_price when omitted. Costs are stored per request,
|
||
# so changing a price only affects future requests.
|
||
#
|
||
# Concurrency: max_concurrency limits simultaneous requests – on the provider
|
||
# (= the host) and/or per model. Requests beyond the limit wait in a queue,
|
||
# e.g. for self-hosted models that can only process one request at a time.
|
||
#
|
||
# options: extra request body fields sent as-is, e.g.
|
||
# options = { temperature = 0.5, reasoning_budget_tokens = 5000 }
|
||
#
|
||
# Authentication: by default "Authorization: Bearer <api_key>" (openai) or
|
||
# "x-api-key: <api_key>" (anthropic). Override with auth_header / auth_prefix.
|
||
# Extra static headers: headers = { "X-Foo" = "bar" }
|
||
# -----------------------------------------------------------------------------
|
||
|
||
[providers.anthropic]
|
||
kind = "anthropic"
|
||
base_url = "https://api.anthropic.com/v1"
|
||
api_key = "${ANTHROPIC_API_KEY}"
|
||
# Current Claude models think adaptively; "effort" controls depth and cost.
|
||
# Prices: Anthropic list prices in USD per 1M tokens (Nov 2026) – adjust if needed.
|
||
models = [
|
||
{ id = "claude-opus-5-5", context = 1000000, max_output = 64000, reasoning = true, vision = true, effort = "high",
|
||
input_price = 4.0, output_price = 20.0, cache_read_price = 0.20, cache_write_price = 5.0 },
|
||
{ id = "claude-sonnet-5-5", context = 1000000, max_output = 64000, reasoning = true, vision = true, effort = "medium",
|
||
input_price = 2.0, output_price = 10.0, cache_read_price = 0.20, cache_write_price = 2.5 },
|
||
{ id = "claude-haiku-5-5", context = 1000000, max_output = 32000, reasoning = true, vision = true, effort = "low",
|
||
input_price = 0.10, output_price = 0.50, cache_read_price = 0.01, cache_write_price = 0.125 },
|
||
]
|
||
|
||
# Google Gemini through its OpenAI-compatible endpoint.
|
||
[providers.gemini]
|
||
kind = "openai"
|
||
base_url = "https://generativelanguage.googleapis.com/v1beta/openai"
|
||
api_key = "${GEMINI_API_KEY}"
|
||
models = [] # discovered automatically
|
||
enabled = false # set to true once GEMINI_API_KEY is available
|
||
|
||
# opencode Go subscription (OpenAI-compatible endpoint for most models).
|
||
# Current model list: https://opencode.ai/inference/go/v1/models
|
||
[providers.opencode-go]
|
||
kind = "openai"
|
||
base_url = "https://opencode.ai/inference/go/openai/v1"
|
||
api_key = "${OPENCODE_API_KEY}"
|
||
models = []
|
||
enabled = false
|
||
|
||
# fal.ai – OpenRouter-backed LLM router. fal expects "Authorization: Key <FAL_KEY>".
|
||
# NOTE: verify base_url against the fal docs for your account.
|
||
[providers.fal]
|
||
kind = "openai"
|
||
base_url = "https://fal.run/openrouter/router/openai/v1"
|
||
api_key = "${FAL_KEY}"
|
||
auth_prefix = "Key "
|
||
models = [
|
||
{ id = "google/gemini-2.5-flash", context = 1000000, max_output = 32000, vision = true },
|
||
]
|
||
enabled = false
|
||
|
||
# Self-hosted (Ollama shown; vLLM/llama.cpp/LM Studio work the same way).
|
||
# From inside Docker, the host is reachable as host.docker.internal.
|
||
[providers.local]
|
||
kind = "openai"
|
||
base_url = "http://host.docker.internal:11434/v1"
|
||
api_key = ""
|
||
max_concurrency = 1 # one request at a time on this host
|
||
models = []
|
||
enabled = false
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# MCP servers
|
||
# -----------------------------------------------------------------------------
|
||
# type = "stdio" → command + args (+ env), started as a child process
|
||
# type = "http" → Streamable HTTP endpoint (url + optional headers)
|
||
# Tools appear to the model as mcp__<server>__<tool>.
|
||
# users = [...]: who may use the server. Default: admins only.
|
||
# Use ["*"] for all users or list usernames, e.g. ["anna", "ben"].
|
||
# load = "auto" (default) | "always" | "on_demand": servers with many tools
|
||
# (more than server.mcp_direct_limit) are not sent with every request; the
|
||
# model finds them with mcp_search and calls them with mcp_call. This keeps
|
||
# requests small – important for self-hosted models.
|
||
# MCP servers may ask you questions (elicitation); they show up in the UI.
|
||
# -----------------------------------------------------------------------------
|
||
|
||
# [mcp.trilium]
|
||
# type = "http"
|
||
# url = "https://notes.example.org/mcp"
|
||
# headers = { Authorization = "Bearer ${TRILIUM_TOKEN}" }
|
||
|
||
# [mcp.filesystem]
|
||
# type = "stdio"
|
||
# command = "npx"
|
||
# args = ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
|
||
# env = {}
|
||
# enabled = false
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Permissions
|
||
# -----------------------------------------------------------------------------
|
||
# For each tool: "allow" | "ask" | "deny". "ask" forwards the request to the UI.
|
||
# MCP tools can be addressed as "mcp__<server>__<tool>" or "mcp__<server>__*".
|
||
# Session permission modes in the UI override this:
|
||
# default → these rules
|
||
# accept_edits → file edits allowed without asking
|
||
# bypass → everything allowed (use with care)
|
||
# -----------------------------------------------------------------------------
|
||
|
||
[permissions]
|
||
read = "allow"
|
||
glob = "allow"
|
||
grep = "allow"
|
||
list = "allow"
|
||
todo_write = "allow"
|
||
ask_user = "allow"
|
||
task = "allow"
|
||
write = "ask"
|
||
edit = "ask"
|
||
bash = "ask"
|
||
bash_output = "allow"
|
||
kill_shell = "allow"
|
||
web_fetch = "ask"
|
||
web_search = "allow"
|
||
browser_snapshot = "allow"
|
||
image_compare = "allow"
|
||
generate_image = "ask" # costs money per image
|
||
"mcp__*" = "ask"
|
||
|
||
# Shell command patterns (glob, matched against the full command). First match wins.
|
||
[permissions.bash_rules]
|
||
"git status*" = "allow"
|
||
"git diff*" = "allow"
|
||
"git log*" = "allow"
|
||
"ls*" = "allow"
|
||
"pwd" = "allow"
|
||
"rm -rf /*" = "deny"
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Agents (modes)
|
||
# -----------------------------------------------------------------------------
|
||
# Built-in: build (full access), plan (read-only, proposes a plan),
|
||
# craft (study → build → check → ready), and subagents
|
||
# general + explore used by the "task" tool.
|
||
# Define your own or override built-ins here. mode = "primary" shows up in the
|
||
# mode switcher, mode = "subagent" is only available through the task tool.
|
||
# -----------------------------------------------------------------------------
|
||
|
||
# Subagents can run on a different model (and host) than the chat, and in the
|
||
# background: the main agent calls task(background = true), keeps working and
|
||
# receives the result as a message when the subagent is done.
|
||
|
||
# [agents.writer]
|
||
# mode = "subagent"
|
||
# description = "Writes documentation, summaries, guides and translations."
|
||
# model = "local/qwen3-4b" # optional, otherwise the session model
|
||
# tools = ["read", "glob", "grep"] # optional, default: all tools
|
||
# max_parallel = 1 # optional, instances at the same time
|
||
# prompt = '''
|
||
# You are a technical writer. Write clear, well-structured text.
|
||
# '''
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Image generation (fal.ai)
|
||
# -----------------------------------------------------------------------------
|
||
# Agents get the tool generate_image; the "Images" page creates images manually.
|
||
# Images are saved in the project (output_dir) and listed in a gallery.
|
||
# Model ids: https://fal.ai/models – price is per image (for the statistics).
|
||
# size_param: "image_size" (FLUX-style presets, default) or "aspect_ratio" ("16:9").
|
||
# -----------------------------------------------------------------------------
|
||
|
||
[images]
|
||
enabled = false
|
||
base_url = "https://fal.run"
|
||
api_key = "${FAL_KEY}"
|
||
default_model = "fal-ai/flux/schnell"
|
||
output_dir = "generated"
|
||
models = [
|
||
{ id = "fal-ai/flux/schnell", name = "FLUX.1 schnell – fast", price = 0.003 },
|
||
{ id = "fal-ai/flux-2-pro", name = "FLUX.2 pro" },
|
||
{ id = "fal-ai/ideogram/v3", name = "Ideogram 3 – text in images", size_param = "aspect_ratio" },
|
||
{ id = "fal-ai/recraft/v4/pro/text-to-image", name = "Recraft V4 pro – design" },
|
||
]
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Users & sign-in
|
||
# -----------------------------------------------------------------------------
|
||
# Users are managed in the web UI (Settings → Users). On first start without
|
||
# users, the server prints a one-time setup code to its log; use it in the UI
|
||
# to create the first admin – or set CODECRAFT_ADMIN_USER and
|
||
# CODECRAFT_ADMIN_PASSWORD in the environment.
|
||
#
|
||
# Every user works in their own directory <workspace>/<username>; admins can
|
||
# use the whole workspace. Note: file tools are confined to that directory,
|
||
# but shell commands run in the shared container and are not sandboxed.
|
||
# -----------------------------------------------------------------------------
|
||
|
||
[auth]
|
||
# Lifetime of a login in days.
|
||
session_days = 30
|
||
# External URL of this server – required for single sign-on (OIDC redirects).
|
||
public_url = ""
|
||
# Secure cookies: "auto" (when served via https), "true" or "false".
|
||
cookie_secure = "auto"
|
||
|
||
# Single sign-on via OpenID Connect (Authentik, Authelia, Keycloak, ...).
|
||
# Redirect URI to register at the IdP: <public_url>/api/auth/oidc/callback
|
||
[auth.oidc]
|
||
enabled = false
|
||
issuer = "https://auth.example.org/application/o/codecraft/"
|
||
client_id = "codecraft"
|
||
client_secret = "${OIDC_CLIENT_SECRET}"
|
||
scopes = ["openid", "profile", "email", "groups"]
|
||
username_claim = "preferred_username"
|
||
groups_claim = "groups"
|
||
# Members of this group are admins; empty = manage roles in CodeCraft.
|
||
admin_group = ""
|
||
# Only members of this group may sign in; empty = no restriction.
|
||
allowed_group = ""
|
||
# Create users automatically on their first sign-in.
|
||
auto_create = true
|
||
# Link a sign-in to an existing local user with the same username.
|
||
# Only enable this if you trust the usernames of your IdP.
|
||
link_by_username = false
|
||
button_label = "Single sign-on"
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Web search (web_search tool)
|
||
# -----------------------------------------------------------------------------
|
||
[search]
|
||
# "duckduckgo" (no key, may be rate-limited), "searxng" (self-hosted, set url),
|
||
# "brave" or "tavily" (api_key, e.g. "${BRAVE_API_KEY}")
|
||
provider = "duckduckgo"
|
||
url = ""
|
||
api_key = ""
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Sandbox
|
||
# -----------------------------------------------------------------------------
|
||
# Shell commands and git of a user run in a bubblewrap sandbox: system
|
||
# read-only, only the user's folders writable, own home, own processes, no
|
||
# server environment. The network is shared (package managers keep working).
|
||
# Needs the security options in docker-compose.yml. Without a working
|
||
# sandbox, non-admin users cannot run shell commands.
|
||
[sandbox]
|
||
# "auto" = non-admin users, "always" = everybody (admins too), "off" = nobody
|
||
mode = "auto"
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Craft mode
|
||
# -----------------------------------------------------------------------------
|
||
[craft]
|
||
# Headless Chromium binary used to study websites and check builds.
|
||
chromium = "chromium"
|
||
# Screen widths used for screenshots and comparisons.
|
||
widths = [390, 768, 1440]
|
||
# Clean room: only what a visitor sees (screenshots, visible text, layout).
|
||
# Set to false for "deep" mode (page source and scripts may be read).
|
||
clean_room = true
|