Kōan has two branches you can track:
main(default) — bleeding edge. Every merged change lands here.stable— contains only tagged releases, fast-forwarded at each release. Recommended for a predictable, vetted experience.
Track stable:
git clone -b stable https://github.com/Anantys-oss/koan.git
cd koan
# update later with:
git pull origin stableSwitch an existing checkout from main to stable:
git fetch origin
git checkout stable
git pull origin stableSee docs/maint.md for the release procedure and cadence philosophy.
The easiest way to set up Kōan is with the interactive wizard:
git clone https://github.com/Anantys-oss/koan.git
cd koan
make setup
make installThis launches the terminal onboarding wizard. It creates your private
instance/, configures a CLI provider and messaging, clones Kōan into
workspace/koan, and validates the setup. If you prefer manual setup,
continue below.
Prefer to run Koan in a container (server deployment or local isolation)? The easiest path is the prebuilt image on GitHub Container Registry — no local build:
git clone https://github.com/Anantys-oss/koan.git && cd koan
cp -r instance.example instance && cp env.example .env # fill in messaging + auth creds
./setup-docker.sh # detect host paths, generate volume mounts
make docker-auth # subscription users: extract a Claude OAuth token from the host
make docker-gh-auth # inject your gh token for the container
make docker-pull-up # pull ghcr.io/anantys-oss/koan and start (or: make docker-up to build)The image lives at ghcr.io/anantys-oss/koan
with latest, stable, and per-version tags. Building from source remains a
first-class fallback. See the Docker Setup Guide for the
full walkthrough — authentication, GHCR access, image pinning, and troubleshooting.
- At least one supported CLI provider installed and authenticated:
- Claude Code CLI
- OpenAI Codex CLI
- GitHub Copilot CLI
- Local provider dependencies (see docs/provider-local.md)
- Python 3.8+
- A Telegram account or a Slack workspace (for messaging)
- GitHub CLI
ghinstalled and authenticated — see Dedicated GitHub Identity for setting up a bot account
Koan supports multiple LLM providers. Claude Code CLI is the default and most capable option. You can also use OpenAI Codex, GitHub Copilot, or a local LLM server.
| Provider | Setup Guide | Best For |
|---|---|---|
| Claude Code (default) | docs/provider-claude.md | Full-featured agent with best reasoning |
| OpenAI Codex | docs/provider-codex.md | ChatGPT users who want Codex models |
| GitHub Copilot | docs/provider-copilot.md | Teams with existing Copilot subscriptions |
| Local LLM | docs/provider-local.md | Offline use, privacy, zero API cost |
You can mix providers per project — see the individual guides for
per-project configuration via projects.yaml.
git clone https://github.com/Anantys-oss/koan.git
cd koan
cp -r instance.example instanceThe instance/ directory is your private data — it's gitignored and never pushed to the Kōan repo. You can version it in a separate private repo if you want persistence.
Kōan supports Telegram (default), Slack, and Matrix for communication. Follow the setup guide for your preferred platform:
| Platform | Setup Guide | Best For |
|---|---|---|
| Telegram (default) | docs/messaging-telegram.md | Quick setup, works from any network |
| Slack | docs/messaging-slack.md | Team collaboration, workspace integration |
| Matrix | docs/messaging-matrix.md | Self-hosted / federated, open protocol |
All three platforms expose the same feature set. Telegram is the simplest for personal use, Slack is best for team environments, and Matrix is ideal if you want a self-hosted or federated option.
cp env.example .envEdit .env and fill in the credentials for your chosen messaging provider.
For Telegram:
KOAN_TELEGRAM_TOKEN=123456789:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
KOAN_TELEGRAM_CHAT_ID=987654321For Slack:
KOAN_MESSAGING_PROVIDER=slack
KOAN_SLACK_BOT_TOKEN=xoxb-your-bot-token
KOAN_SLACK_APP_TOKEN=xapp-your-app-token
KOAN_SLACK_CHANNEL_ID=C01234ABCDFor Matrix: Matrix can be configured via .env or via instance/config.yaml (recommended — see docs/messaging-matrix.md for the full guide):
# instance/config.yaml (recommended)
messaging:
provider: "matrix"
matrix:
homeserver: "https://matrix.org"
user_id: "@koan:matrix.org"
room_id: "!abcdefghijk:matrix.org"
access_token: "syt_your_token_here"Or the legacy .env form (env vars override config.yaml when set):
KOAN_MESSAGING_PROVIDER=matrix
KOAN_MATRIX_HOMESERVER=https://matrix.org
KOAN_MATRIX_ACCESS_TOKEN=syt_your_token_here
KOAN_MATRIX_USER_ID=@koan:matrix.org
KOAN_MATRIX_ROOM_ID=!abcdefghijk:matrix.orgThe .env file is gitignored — your secrets stay local. See the provider-specific setup guides above for detailed instructions on obtaining these credentials.
Recommended: Use projects.yaml at your koan root:
cp projects.example.yaml projects.yamlEdit projects.yaml:
defaults:
git_auto_merge:
enabled: false
base_branch: "main"
strategy: "squash"
projects:
myapp:
path: "/Users/yourname/myapp"
backend:
path: "/Users/yourname/backend"Each project only needs a path. All other fields are optional and inherit from defaults.
Fallback: You can also use the KOAN_PROJECTS env var in .env:
KOAN_PROJECTS=myapp:/Users/yourname/myapp;backend:/Users/yourname/backendIf projects.yaml exists, the env var is ignored. On first startup, Kōan will auto-migrate env vars to projects.yaml.
If you're using multi-project mode, create a memory folder for each project:
# Copy the template for each project
cp -r instance/memory/projects/_template instance/memory/projects/myappEdit the files in instance/memory/projects/myapp/ to describe your project's architecture. This helps Kōan understand your codebase faster.
$EDITOR instance/soul.md # Write your agent's personalityKoan can email you session digests when budget is exhausted. This uses standard SMTP — no extra dependencies.
Step 1: Enable email in instance/config.yaml:
email:
enabled: true # Turn on email notifications
max_per_day: 5 # Rate limit (rolling 24h window)Step 2: Add SMTP credentials to .env:
# SMTP server (e.g., Gmail with an app password)
KOAN_SMTP_HOST=smtp.gmail.com
KOAN_SMTP_PORT=587
KOAN_SMTP_USER=koan-bot@gmail.com
KOAN_SMTP_PASSWORD=your-app-password-here
# Your email address (single recipient)
EMAIL_KOAN_OWNER=you@example.comGmail users: You need an App Password, not your regular password. Enable 2FA first, then generate an app password under Security → App Passwords.
Step 3: Test with the /email test command in Telegram.
Use /email (or /email status) to check configuration and sending stats at any time.
make setupThis creates a .venv/ and installs Python dependencies.
# Terminal 1: Telegram bridge
make awake
# Terminal 2: Agent loop
make runLinux users: See Running as a systemd service below for automatic startup, restart-on-failure, and proper service management.
For full autonomy, Kōan should have its own GitHub account. This gives it a distinct identity for PRs, commits, and @mention commands — clearly separated from your personal account.
- Go to github.com/signup and create a new account (e.g.,
yourname-koan) - Use a dedicated email address (or a Gmail alias like
yourname+koan@gmail.com) - Pick a short, recognizable username — this will appear in @mentions and commit history
Important: Use a classic token, not a fine-grained token. Fine-grained tokens do not support the
notificationsscope, which is required for GitHub @mention commands.
On the bot's GitHub account:
- Settings → Developer settings → Personal access tokens → Tokens (classic)
- Click "Generate new token (classic)"
- Set an expiration (or "No expiration" for convenience)
- Select these scopes:
| Scope | Required | Purpose |
|---|---|---|
repo |
Yes | Push branches, create PRs and issues |
notifications |
Yes | Poll @mention notifications |
workflow |
Optional | Trigger GitHub Actions on push |
- Click Generate token and copy the
ghp_...token
echo "ghp_YOUR_TOKEN_HERE" | gh auth login --user yourname-koan --with-tokenNo output means success. Verify both accounts are registered:
gh auth statusYou should see both your personal account and the bot account:
github.com
✓ Logged in to github.com account yourname-koan (keyring)
- Active account: true
- Token: ghp_****
- Token scopes: 'notifications', 'repo'
✓ Logged in to github.com account yourname (keyring)
- Active account: false
- Token: gho_****
On each repository Kōan should work on:
- Settings → Collaborators → Add people
- Invite the bot account with Write access (or Maintain if you want it to merge PRs)
- Accept the invitation from the bot account
In .env:
GITHUB_USER=yourname-koan
KOAN_EMAIL=yourname-koan@users.noreply.github.comGITHUB_USERtells Kōan whichghidentity to use for API callsKOAN_EMAILsets the git author/committer on the bot's commits
In instance/config.yaml:
github:
nickname: "yourname-koan" # The @mention name (must match the GitHub username)
commands_enabled: true # Enable @mention commands
authorized_users: ["yourname"] # Your personal account (who can command the bot)In projects.yaml (optional per-project override):
projects:
sensitive-repo:
path: "/path/to/sensitive-repo"
github:
authorized_users: ["alice", "bob"] # Restrict who can command the bot on this repo# Confirm the bot identity resolves
GH_TOKEN=$(gh auth token --user yourname-koan) gh api user --jq '.login'
# → yourname-koan
# Confirm notifications access (should return [] not 403)
GH_TOKEN=$(gh auth token --user yourname-koan) gh api notifications
# → []
# Confirm repo access
GH_TOKEN=$(gh auth token --user yourname-koan) gh api repos/OWNER/REPO/collaborators/yourname-koan/permission --jq '.permission'
# → write (or admin/maintain)make startYou should see in the logs:
[init] GitHub CLI authenticated as yourname-koan
At startup, Kōan calls gh auth token --user <GITHUB_USER> to retrieve the bot's token and sets it as GH_TOKEN in the environment. All subsequent gh API calls use this token, so the bot operates under its own identity regardless of which gh account is "active" on your machine.
During the sleep cycle between missions, Kōan polls the bot's GitHub notifications. When someone posts @yourname-koan rebase on a PR, the bot detects the mention, verifies the user's permissions, and queues a mission.
See docs/github-commands.md for the full list of supported @mention commands and the security model.
On Linux systems with systemd, Kōan can run as a native system service with automatic restart on failure, journal integration, and boot-time startup.
Note: macOS users should use launchd instead — see Running as a launchd Service (macOS) below. systemd is Linux-only.
make install-systemctl-service # One-time: install + enable services
make start # Start via systemctlOr simply run make start — on Linux with systemd, it auto-installs the service on first run.
The install creates two systemd services:
| Service | Process | Description |
|---|---|---|
koan.service |
run.py |
Agent loop (missions, execution, reflection) |
koan-awake.service |
awake.py |
Messaging bridge (Telegram/Slack) |
The services are linked: stopping one stops both (BindsTo= relationship).
On Linux with systemd available, the Makefile automatically delegates to systemctl:
| Command | Without systemd | With systemd |
|---|---|---|
make start |
Python PID manager | systemctl start koan |
make stop |
Python PID manager | systemctl stop koan |
make status |
Python PID manager | systemctl status koan koan-awake |
The detection is automatic — no configuration needed. On macOS or Linux without systemd, the original PID-manager behavior is preserved.
Logs are written to both the systemd journal and the logs/ directory:
# systemd journal (quick checks)
sudo journalctl -u koan -f
sudo journalctl -u koan-awake -f
# File-based logs (same as non-systemd)
make logs# Standard systemctl commands work
sudo systemctl restart koan # Restart both services
sudo systemctl enable koan # Start on boot (done by install)
sudo systemctl disable koan # Don't start on boot
# Uninstall completely (reverts to PID-manager mode)
make uninstall-systemctl-servicemake uninstall-systemctl-serviceThis stops the services, disables them, removes the unit files, and reloads systemd. After uninstalling, make start will use the Python PID manager again.
On macOS, Kōan can run as a native launchd user agent with automatic restart on failure and login-time startup — no sudo required.
Tip: To keep your Mac awake while Kōan runs, see Preventing macOS sleep below.
make install-launchd-service # One-time: render plists + load services
make start # Start via launchctlOr simply run make start — on macOS, it auto-installs the launchd service on first run.
The install creates two launchd user agents in ~/Library/LaunchAgents/:
| Plist | Process | Description |
|---|---|---|
com.koan.run.plist |
run.py |
Agent loop (missions, execution, reflection) |
com.koan.awake.plist |
awake.py |
Messaging bridge (Telegram/Slack) |
Both services are configured with:
RunAtLoad— start automatically at loginKeepAlive(on non-zero exit) — restart on failureThrottleInterval(10s) — prevent rapid restart loops
A wrapper script (koan/launchd/koan-wrapper.sh) sources your .env file and sets up the environment (KOAN_ROOT, PYTHONPATH, SSH_AUTH_SOCK) before launching Python.
On macOS with launchctl available, the Makefile automatically delegates to launchd:
| Command | Without launchd | With launchd |
|---|---|---|
make start |
Python PID manager | launchctl kickstart |
make stop |
Python PID manager | launchctl kill SIGTERM |
make status |
Python PID manager | launchctl print |
The detection is automatic — no configuration needed. On Linux or systems without launchctl, the original PID-manager behavior is preserved.
Logs are written to the logs/ directory:
# Watch live logs
make logs
# Or tail directly
tail -f logs/run.log
tail -f logs/awake.logIf you use SSH-based git remotes, make start automatically forwards your SSH agent socket so launchd-managed processes can access it:
# This is done automatically by `make start`, but you can also do it manually:
ln -sf "$SSH_AUTH_SOCK" /path/to/koan/.ssh-agent-sockmake uninstall-launchd-serviceThis stops the services, removes the plist files from ~/Library/LaunchAgents/, and unloads them from launchd. After uninstalling, make start will use the Python PID manager again.
This happens if you run Python scripts directly instead of using make commands.
Fix: Always use the Makefile targets (make run, make awake, make test). They set KOAN_ROOT automatically.
If you need to run scripts directly:
KOAN_ROOT=/path/to/koan python3 koan/app/awake.pyYour missions.md file references a project name that doesn't match your configuration.
Fix: Either:
- Remove project tags from missions:
- My taskinstead of- [project:example] My task - Or ensure the project name matches your
projects.yamlconfig
For Telegram:
- Verify your token:
curl "https://api.telegram.org/botYOUR_TOKEN/getMe"should return your bot info - Verify your chat ID: Make sure
KOAN_TELEGRAM_CHAT_IDmatches the ID from thegetUpdatescall - Check
make awakeis running without errors
For Slack:
- Verify Socket Mode is enabled and the app token starts with
xapp- - Check that the bot is invited to the channel (
/invite @koan) - Review the logs for connection errors (
make logs)
Make sure your configured provider CLI is installed and authenticated.
Claude Code:
claude --version # Should show version
claude # Should start interactive mode (exit with /exit)OpenAI Codex:
codex --version # Should show version
codex login --device-authFor Copilot and local setups, see:
Kōan runs in the background — if your Mac goes to sleep, everything stops. You need to prevent sleep while keeping the screen off.
The simplest approach — run this before launching Kōan:
# Prevent system sleep indefinitely (Ctrl+C to stop)
caffeinate -s &Or wrap your entire session:
caffeinate -s make runThe -s flag prevents system sleep even when the display is off. The display will still turn off normally (saving power), but the CPU keeps running.
To make the setting survive reboots:
# Disable sleep on AC power
sudo pmset -c sleep 0
sudo pmset -c disablesleep 1To revert:
sudo pmset -c sleep 10
sudo pmset -c disablesleep 0Alternatively: System Settings → Energy → Prevent automatic sleeping when the display is off — toggle ON.
Tip:
caffeinate -sis the safest option — it only affects the current session and stops automatically when you kill it.
For Telegram (default):
| Variable | Description |
|---|---|
KOAN_TELEGRAM_TOKEN |
Telegram bot token from @BotFather |
KOAN_TELEGRAM_CHAT_ID |
Your Telegram chat ID |
For Slack:
| Variable | Description |
|---|---|
KOAN_MESSAGING_PROVIDER |
Must be set to slack |
KOAN_SLACK_BOT_TOKEN |
Bot User OAuth Token (starts with xoxb-) |
KOAN_SLACK_APP_TOKEN |
App-Level Token (starts with xapp-) |
KOAN_SLACK_CHANNEL_ID |
Channel ID where Kōan operates |
Note: Project paths are configured in
projects.yaml(see step 4 above). TheKOAN_PROJECTSenv var is supported as a fallback.
| Variable | Default | Description |
|---|---|---|
KOAN_ROOT |
(auto-detected) | Path to koan repo (set by Makefile) |
KOAN_EMAIL |
— | Git author email for koan's commits |
KOAN_BRIDGE_INTERVAL |
3 | Telegram poll interval (seconds) |
KOAN_CHAT_TIMEOUT |
180 | Claude CLI timeout for chat responses (seconds) |
KOAN_GIT_SYNC_INTERVAL |
5 | Runs between git sync checks |
KOAN_SMTP_HOST |
— | SMTP server hostname (e.g., smtp.gmail.com) |
KOAN_SMTP_PORT |
587 | SMTP server port |
KOAN_SMTP_USER |
— | SMTP login username |
KOAN_SMTP_PASSWORD |
— | SMTP login password |
EMAIL_KOAN_OWNER |
— | Recipient email for notifications |
Note:
max_runs_per_dayandinterval_secondsare now configured inconfig.yaml, not.env. The env varsKOAN_MAX_RUNSandKOAN_INTERVALare deprecated and ignored.
Kōan can work on up to 50 projects, rotating between them.
Configure in projects.yaml:
projects:
myapp:
path: "/Users/yourname/myapp"
backend:
path: "/Users/yourname/backend"
git_auto_merge:
base_branch: "staging"Per-project git_auto_merge overrides are defined directly in projects.yaml. See projects.example.yaml for the full schema.
For each project, create a memory folder:
cp -r instance/memory/projects/_template instance/memory/projects/myapp
cp -r instance/memory/projects/_template instance/memory/projects/backendTag missions with the target project:
- [project:myapp] Add user authentication
- [project:backend] Fix CSS bug on homepage