Docs

How to configure telos, how its AI fits together, and what to check before it is on the internet. New here? Start with Get started. A word you do not know is in the Glossary.

Configuration

Everything is an environment variable on the telos container.

VariableDefaultMeaning
DATABASE_URL—Required. Postgres connection string. There is no embedded fallback.
BETTER_AUTH_SECRET—Required in production. Signs sessions. At least 32 characters: openssl rand -hex 32.
APP_URLhttp://localhost:3001Public URL. Sign-in links, and each todo's and project's link back, are built from it.
SERVER_URLAPP_URL in productionWhere a browser reaches the server, for the OIDC sign-in's redirects. Only differs in development.
PORT3001Port the server listens on.
SMTP_URL—How sign-in links are emailed: a nodemailer connection URL. Unset, no links are sent.
SMTP_FROMtelos@<APP_URL's host>The address sign-in email comes from.
SECURE_LOCAL_NETfalseTyping an address signs in, with no link. Private networks only; refused with a public APP_URL.
TRUST_PROXYfalseReverse proxies in front (1 behind one). Sign-in is throttled per client address.
AI_ENABLED—Set to false to remove AI entirely. Otherwise an admin turns it on in Settings.
RUNNER_CONCURRENCY2The most runs the runner works at once.
RUNNER_POLL_SECONDS5How long the runner waits when nothing is ready.
RUNNER_ALLOW_CHECKSfalseLet the runner run check lanes' shell commands. RUNNER_ALLOW_STDIO is gone: telos runs no stdio MCP servers.
RUNNER_KEYmade up at bootOnly for a second runner on another host: set the same value on both.

Telos has no passwords: people sign in with a link emailed through SMTP_URL, through an OpenID Connect provider, or with SECURE_LOCAL_NET on a private network. Upgrading from 5.x, AUTH_SECRET is now BETTER_AUTH_SECRET and AUTH_MAGIC_LINK=false is now SECURE_LOCAL_NET=true; the server refuses to start while an old name is set.

AI switches

Telos is a todo app for people first. Everything AI is an extra, and it is off unless you turn it on, at every level:

  1. The instance. Settings → AI, seen only by an admin. Off, /mcp is a 404 and the app shows no AI at all. AI_ENABLED=false removes the switch.
  2. The account. Settings → AI. Off, the account's API keys stop working and the runner leaves its rows alone.
  3. The project. Its AI switch, and under it auto-run: whether agents start on todos by themselves. Auto-run is off on a new project; until you turn it on, an agent works a todo only when you press Run now.
  4. The todo. "AI ignores this", and the runner leaves it alone.

Agents, lanes and runs

The runner is telos's own worker, part of the server, with nothing to set up. Three ideas make it up:

  • an agent is a model and its instructions;
  • a lane is a column; give it an agent and each ready todo there becomes a run, which follows the lane's success or failure route;
  • a run ends in success or failure, with a report and, if the agent chose, new todos.

Every reply is read the same way: one that opens with FAIL sends the todo along the failure route, anything else passes. A passing run can split its todo into new ones, which land in the project's lane for new todos, and the todo is done when they all are. A lane's success route can archive what passes instead of moving it.

Each run keeps a log of its tool calls, shown on the todo as it goes, and a list of what it made. A card shows Sent back when a reviewer rejected the work, Run failed when the run never finished, and how many attempts the lane's agent has used. Activity collects what needs you, 30 days of spend, and recent runs across every project.

Starters

A new agent can start from one of three (Settings → Agents → Start from), each with instructions and a temperature for its job:

  • Planner: splits work into todos.
  • Worker: does it.
  • Reviewer: rules PASS or FAIL.

Agents talk to any OpenAI-compatible endpoint, a local Ollama included. Type the base URL and its models are listed, with the context length each reports. A reasoning effort is sent as reasoning_effort to a model that deliberates.

MCP servers and hooks

MCP servers are kept once, under Settings → MCP servers, and each agent reaches all of them or only the ones you tick. Every server is a URL (streamable HTTP): telos does not run stdio servers. A server's headers are secrets: you see which are set, never their values, and only the runner is sent them.

A server can carry hooks, a memory lookup before each turn for example. The todo is the hooks' session:

HookFires
sessionStartOn the todo's first run.
beforeTurn, afterTurn, sessionEndOn every run, each run being one turn of the todo.
sessionDeleteAfter the todo is deleted for good, so a memory server can forget it.
beforeCompactNever: a run does not compact its transcript.

The /mcp door

For your own agents, Claude Code and the like. With an API key from Settings, sent as x-api-key, a client can read the board and what happened on it, and work it: add, edit, move, run and archive todos, set what they wait on, write notes, record artifacts, and apply board templates. Each key has a switch per tool, all on until you turn one off.

The AI switches, agents, keys, lanes and their agents, bulk changes and deleting a project stay yours. Nothing checks what a client records, so the board lists it as unverified, signed with the key that said it.

Keeping a copy in step

changes(since:) on /graphql, and the changes tool on /mcp, hand back what changed in your projects, todos and dependencies since a cursor: the rows as they stand now, tombstones for what went, and the next cursor. A cursor is good for 29 days; after that a client gets CURSOR_EXPIRED and starts again.

Before you expose it

Registration is open: any address that completes a sign-in gets an account. Right for an instance only you can reach, wrong for one on the public internet.

  • Put it behind something. A reverse proxy with TLS, and, if the instance is yours alone, an allowlist, VPN, or auth in front of it. Behind a proxy, set TRUST_PROXY so the sign-in throttle sees real client addresses.
  • Never set SECURE_LOCAL_NET on a reachable instance. An email address becomes the whole credential.
  • Set a real BETTER_AUTH_SECRET and keep it. The server refuses to boot in production without a strong one.
  • Mind what agents can reach. An agent's base URL and the MCP servers are fetched from the server's host, so keep it away from anything private and leave RUNNER_ALLOW_CHECKS off on a shared instance.