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.
| Variable | Default | Meaning |
|---|---|---|
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_URL | http://localhost:3001 | Public URL. Sign-in links, and each todo's and project's link back, are built from it. |
SERVER_URL | APP_URL in production | Where a browser reaches the server, for the OIDC sign-in's redirects. Only differs in development. |
PORT | 3001 | Port the server listens on. |
SMTP_URL | — | How sign-in links are emailed: a nodemailer connection URL. Unset, no links are sent. |
SMTP_FROM | telos@<APP_URL's host> | The address sign-in email comes from. |
SECURE_LOCAL_NET | false | Typing an address signs in, with no link. Private networks only; refused with a public APP_URL. |
TRUST_PROXY | false | Reverse 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_CONCURRENCY | 2 | The most runs the runner works at once. |
RUNNER_POLL_SECONDS | 5 | How long the runner waits when nothing is ready. |
RUNNER_ALLOW_CHECKS | false | Let the runner run check lanes' shell commands. RUNNER_ALLOW_STDIO is gone: telos runs no stdio MCP servers. |
RUNNER_KEY | made up at boot | Only 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:
- The instance. Settings → AI, seen only by an admin. Off,
/mcpis a 404 and the app shows no AI at all.AI_ENABLED=falseremoves the switch. - The account. Settings → AI. Off, the account's API keys stop working and the runner leaves its rows alone.
- 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.
- 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:
| Hook | Fires |
|---|---|
sessionStart | On the todo's first run. |
beforeTurn, afterTurn, sessionEnd | On every run, each run being one turn of the todo. |
sessionDelete | After the todo is deleted for good, so a memory server can forget it. |
beforeCompact | Never: 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_PROXYso the sign-in throttle sees real client addresses. - Never set
SECURE_LOCAL_NETon a reachable instance. An email address becomes the whole credential. - Set a real
BETTER_AUTH_SECRETand 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_CHECKSoff on a shared instance.
This page is a summary. The README has the full detail, and development notes.