Skip to content

Timothy

CI codecov Release Go React TypeScript PostgreSQL License: AGPL v3

Timothy

The open-source control plane for your personal AI workforce. Run your own AI agents. Your infrastructure. Your models. Your rules.

Timothy runs on your hardware and works for you around the clock. Shape agents with their own model, tools and knowledge, hand them real work, and let them chat, research, write code, read your inbox and calendar, and brief you about what matters. They remember who you are across every conversation and deliver results to your phone while you sleep. Every conversation, memory, document, and API key stays on infrastructure you control.

Use any model you want: Anthropic, OpenAI, Amazon Bedrock, GLM, a local Ollama, or any compatible endpoint. Route each kind of work to whichever model does it best, switch anytime from settings, no code changes, no lock-in.

Status: early, under active development.

Alpha releases with prebuilt images are available on the Releases page; expect rough edges and breaking changes between releases.

Features

Feature What you get
One assistant, every model Anthropic, OpenAI, Amazon Bedrock, local models via Ollama, or any compatible provider, all behind one interface. Pick which model handles chat, coding, research, or briefings, and let Timothy fail over to a backup when a provider has a bad day.
Give it real work Hand Timothy a task (research a topic, write a report, fix a bug) and it works unattended: plans, executes, verifies its own output, and shows you the result with a full timeline of what it did. Quick tasks skip the ceremony and just get done.
Results find you Any task or schedule can deliver its result to Telegram, email, a webhook, or GitHub (a pushed branch or an opened pull request) the moment it finishes, files attached. No checking a dashboard: the answer lands where you already are.
It writes code safely Coding tasks run in isolated per-language sandboxes (Go, Node, Python, Java, PHP), on their own git branch, with the work verified before you see it. It can also hand the coding work to a CLI agent you already use, Claude Code, Codex, Cursor, opencode, or pi, while keeping review and budgets in your hands.
Your daily briefings Wake up to a digest of your inbox, calendar, and spending, delivered to Telegram or email in your timezone, saying only what actually needs your attention. Schedule any task to run on your clock.
Connected to your life Gmail, Google Calendar, Docs, Drive, GitHub, Outlook, IMAP, CalDAV, and any MCP server. Timothy reads them when a task needs it, and asks before doing anything destructive.
Shape your own assistants Create named agents with their own personality, favorite model, and exactly the tools and knowledge they need, nothing more. A briefing agent that reads only your mail and calendar can never touch your code or send a message on your behalf.
It remembers you Preferences, projects, and facts you share carry across conversations, and recurring patterns become insights over time. You approve what becomes a standing instruction; noise gets filtered before it ever reaches you.
Your documents, searchable Drop in files or URLs; Timothy files them into topic collections and uses them to answer your questions. Your own knowledge base, on your own disk.
Nothing gets lost Conversations survive restarts, crashes, and upgrades. Pick up any session exactly where it left off.
You control the spend Every model call is priced and logged honestly. Set budgets with alerts, see exactly where the money goes, and route routine work to cheap or free models.
Private by design Runs entirely on your hardware. Sensitive content like email can be pinned to a local model so it never leaves your network, and API keys live in an encrypted store (or your own Vault / AWS Secrets Manager), never in logs, never in the UI.
Talk to it Optional voice input with fully local speech-to-text. Audio never leaves your machine.

A day with Timothy

Things Timothy's own operator actually runs it for:

  • 07:00, your phone buzzes. "Two things need you today: the client call at 14:00 has an unanswered thread from yesterday, and your card was charged twice by the same vendor. The other 14 emails were newsletters." A scheduled briefing read your inbox and calendar, cross-referenced them, and messaged you on Telegram.
  • "Find every receipt from my Portugal trip and total it per currency." Timothy searches your Gmail, opens each receipt (never trusting a snippet), and reports an itemized breakdown with per-currency totals it computed with a calculator, not vibes.
  • "Research the current EU AI Act timeline and write me a cited summary." It searches the web, reads primary sources, writes the report to a file, and a verification step checks the artifact exists and cites real URLs before you ever see "done".
  • "Fix the flaky test in my repo." A coding mission clones the repo into a sandbox, works on its own branch, runs the tests, and opens the result for your review. Your laptop stays untouched.
  • "Remember that Ana owes me EUR 200 from dinner, she'll pay in September." Weeks later: "Who owes me money?" answers correctly, because facts you tell it persist and stay retrievable.
  • Drop a PDF into the knowledge base. It lands in the right topic collection automatically, and next week "what did that scaling article say about probabilistic counting?" quotes it back.
  • Every evening at 20:00, an expense digest lists the day's spending from your inbox; every Monday at 07:00, a week-prep note cross-references your calendar with recent email threads and flags meetings that need preparation.

Each of these is a schedule, a chat message, or a one-line task. No plugins to write, no pipelines to build.

Architecture

Go microservices behind a single public API, one PostgreSQL database, React web UI. All run via Docker Compose.

Service Role
brain Public API: chat orchestration, agent loop, missions, event-sourced sessions, SSE streaming
gateway Internal LLM gateway: multi-provider routing, cost ledger
memoryd Internal memory service: pgvector-backed recall
sandboxd Internal service holding the Docker socket: per-mission sandbox containers
web React + Tailwind interface: chat, missions, usage, settings
searxng Internal metasearch backend for the search_web tool
markitdown Internal Python sidecar: file→markdown conversion
whisper Internal Python sidecar: local speech-to-text for the web mic button (opt-in, off by default)
pdfgen Internal Python sidecar: markdown→PDF via Typst, powers mission PDF export

Plus Postgres (18 + pgvector), internal only, no host port. Migrations are embedded in each Go binary and applied automatically at startup; there's no separate migrate command. Every Go service exposes GET /health and GET /metrics; brain's /metrics, the only one on a published port, requires Authorization: Bearer $TIMOTHY_METRICS_TOKEN.

Sessions are an append-only event log: every turn, tool run, and compaction is an immutable event, so conversations survive crashes mid-stream and replay exactly as they happened.

Published ports (everything else is compose-internal):

Port What
3300 Web UI
8300 Brain (public API)
3301 Vite dev server (make dev)

Both published ports serve plain HTTP and are meant for a trusted LAN. For any exposure beyond that, put a reverse proxy in front that terminates TLS: the API token travels in an Authorization header on every request and is stored in the browser's localStorage, so over plain HTTP anyone on the path can read it. The web UI ships a Content-Security-Policy, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, and Referrer-Policy: strict-origin-when-cross-origin; a proxy that rewrites response headers should preserve them.

Quick start (prebuilt images)

The fastest way to run Timothy: no Go/Node toolchain, no build step, just Docker and the released images.

curl -fsSL https://raw.githubusercontent.com/timothy-agent/timothy/main/deploy/release/install.sh | sh

The installer resolves the newest release, installs into ~/timothy (override with TIMOTHY_HOME=/some/dir), generates a .env with fresh secrets (POSTGRES_PASSWORD, TIMOTHY_MASTER_KEY, TIMOTHY_API_TOKEN), pulls the images, starts the stack, and prints a magic sign-in link once the web UI is up. Open the link: the web UI signs in automatically.

Prefer to inspect scripts before running them? Every release also ships install.sh as an asset: download it from the releases page, read it, then sh install.sh.

Upgrading

Run the exact same command again:

curl -fsSL https://raw.githubusercontent.com/timothy-agent/timothy/main/deploy/release/install.sh | sh

The installer finds your existing install (~/timothy, TIMOTHY_HOME, or the directory you run it from), keeps all your secrets, bumps TIMOTHY_VERSION to the newest release, refreshes docker-compose.yml, pulls the new images (including the mission sandbox), and restarts the stack. Your data lives in Docker volumes and your secrets in .env; neither is touched. Database migrations run automatically when the new version starts. Downgrading is not supported once a newer version's migrations have run.

The rest of this README covers building and running from source instead.

Build from source

Prerequisites:

  • Docker (Desktop, or engine + compose plugin).
  1. Copy the env file and fill in the required values:

    cp deploy/env.example deploy/.env

    Open deploy/.env and set:

    • POSTGRES_PASSWORD: compose refuses to start without it.
    • TIMOTHY_MASTER_KEY: generate with openssl rand -base64 32. This is the root of trust for the encrypted secret store (provider API keys, OAuth tokens all live behind it). Compose hard-fails if it's blank. Back this up: losing it makes every stored secret unrecoverable.
    • TIMOTHY_API_TOKEN: generate with openssl rand -hex 32. Bearer token for the API; if it's blank, every request 401s.
  2. Missions sandbox. sandboxd is a required service: docker-compose.yml fixes its MISSION_SANDBOX_IMAGE at timothy-sandbox:latest, and it refuses to start without that image built. make up builds it for you, but you can build it ahead of time:

    make sandbox-image

    This builds the base image plus the per-language variants (timothy-sandbox-{go,node,python,java,php}:latest) that coding missions run in.

  3. (Linux only) Set DOCKER_SOCK_GID so sandboxd can use the Docker socket:

    stat -c '%g' /var/run/docker.sock

    Put that number in .env. On Docker Desktop the default of 0 works as-is. Note: mounting docker.sock gives sandboxd root-equivalent access to the host. It's isolated on its own compose network, read-only, and runs with all capabilities dropped, but the socket itself is the trust boundary, so only run this on a host you control.

  4. Start the stack:

    make up

    Web UI: http://localhost:3300. API: http://localhost:8300.

  5. First login. There's no login page: the web UI auto-opens a settings dialog asking for an API token the first time it can't find one. Paste the TIMOTHY_API_TOKEN value from deploy/.env. It's stored in your browser's localStorage.

  6. Add a provider. A fresh install has zero LLM providers and no routing configured, so Timothy can't answer anything until you do this. Go to Settings → Providers, pick a preset tile (OpenAI, OpenAI Responses, Anthropic, Cursor, Bedrock, GLM, Grok, Ollama, or a custom OpenAI-compatible endpoint), fill in the form, and run the connection test before adding it. The API key you enter is encrypted into the secret store (default backend db, encrypted with TIMOTHY_MASTER_KEY); the database only ever holds a reference to it, never the raw value, and it never appears in .env, logs, or API responses. Creating your first provider automatically bootstraps the 4 routes Timothy needs to work (default, summarize, embedding, vision); routes are otherwise fully user-managed (create, edit chain/strategy, delete) from Settings → Routing.

Operating the stack

make up      # start (builds images as needed)
make down    # stop
make logs    # follow logs for all services

Rebuild and restart a single service after a code change:

make brain      # or gateway, memoryd, web, markitdown, whisper, pdfgen, sandboxd

Backups

Postgres has no host port, so backups go through the container. scripts/backup-db.sh does that and writes a gzipped dump outside the pgdata volume:

scripts/backup-db.sh                 # writes ./backups/timothy-<UTC timestamp>.sql.gz
scripts/backup-db.sh /mnt/nas/timothy   # or point it somewhere off-host
Variable Default Meaning
BACKUP_DIR <repo>/backups Output directory; the first positional argument wins over it.
BACKUP_KEEP 14 How many dumps to keep. Rotation is by count, so a stack that was off for a month still keeps its last good dumps.

The script takes no input, prints no secret values, and exits non-zero on any failure, so it runs unattended from cron:

15 3 * * * /path/to/timothy/scripts/backup-db.sh >> /var/log/timothy-backup.log 2>&1

Each run dumps the whole database. Never narrow it to pg_dump -t <table> to save space: a table-scoped dump silently drops the secrets table, and the restore then comes up with every provider and connector credential missing. The script refuses to write a dump that has no secrets table for exactly this reason.

A complete backup is the dump plus TIMOTHY_MASTER_KEY from deploy/.env. The dump holds secrets only as ciphertext; without that key they are unrecoverable. Back the key up separately from the dumps, and never regenerate it: a new key orphans every secret already sealed with the old one.

Restoring onto a fresh host

  1. Put the repo and deploy/.env in place. The .env must carry the same TIMOTHY_MASTER_KEY as the instance the dump came from; POSTGRES_PASSWORD may be new.

  2. Start Postgres alone, so no service writes while the restore is running:

    docker compose -f deploy/docker-compose.yml up -d postgres
  3. Load the dump. ON_ERROR_STOP=1 makes a partial restore fail loudly instead of leaving a half-populated database:

    gunzip -c backups/timothy-<timestamp>.sql.gz \
      | docker compose -f deploy/docker-compose.yml exec -T postgres \
          sh -c 'PGPASSWORD="$POSTGRES_PASSWORD" psql -U timothy -d timothy -v ON_ERROR_STOP=1'

    The dump recreates the schema, so restore into an empty database. If this Postgres already ran the stack once, drop and recreate first: docker compose -f deploy/docker-compose.yml exec -T postgres sh -c 'PGPASSWORD="$POSTGRES_PASSWORD" psql -U timothy -d postgres -c "DROP DATABASE timothy" -c "CREATE DATABASE timothy"'.

  4. Check the data landed, including the secrets table:

    docker compose -f deploy/docker-compose.yml exec -T postgres \
      sh -c 'PGPASSWORD="$POSTGRES_PASSWORD" psql -U timothy -d timothy -c "select count(*) from secrets" -c "select count(*) from session_events"'
  5. Bring up the rest of the stack:

    make up
  6. Verify secrets actually decrypt, which is what proves the master key matches. Ask brain to run a provider connection test; it resolves the stored credential through the secret store:

    curl -s -H "Authorization: Bearer $TIMOTHY_API_TOKEN" http://localhost:8300/v1/admin/providers
    curl -s -X POST -H "Authorization: Bearer $TIMOTHY_API_TOKEN" http://localhost:8300/v1/admin/providers/<id>/test

    A successful test means the ciphertext decrypted. A decrypt failure in make logs (gateway or brain) means the TIMOTHY_MASTER_KEY in deploy/.env is not the one that sealed these rows; restore the correct key rather than re-entering credentials, or every historical secret stays unreadable.

Upgrading a source build

git pull
make up

Migrations are additive-only, never edited once applied, and run automatically at service startup; no separate migrate step.

Local development

The Go toolchain runs fully containerized; no host Go install required.

make build   # compile everything
make test    # unit tests
make vet     # go vet
make lint    # golangci-lint

Frontend development with hot reload:

make dev   # Vite dev server on :3301, proxies /v1 to brain

make test-integration, make canary, and make kb-eval (retrieval eval harness, scripts/kb-eval/) need the compose stack up (make up first).

Design decisions are documented as D-0XX markers in code comments next to the code they explain.

License

AGPL-3.0

About

The open-source control plane for your personal AI workforce. Run your own AI agents. Your infrastructure. Your models. Your rules.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

60 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages