A Kanban board with an agent-friendly API, built for vibe coding workflows.
When AI agents work on complex projects, tasks get lost — context windows fill up, conversations compress, and the agent forgets what it was supposed to do next. vibe-kanban gives agents (and humans) a shared Kanban board where every task, status change, and blocker is tracked via a simple REST API.
TL;DR — Your agent syncs its TODO list here. You watch progress in real-time. Nothing gets forgotten.
Built with: Python (FastAPI) · React (Vite + TypeScript) · Tailwind CSS · SQLite/PostgreSQL · JWT + API Key auth
Email/password + Google OAuth. Includes "Forgot password?" flow with email reset link.
Enter your email to receive a password reset link (15-minute expiry).
Each project gets a unique API key with description, timestamps (created/last used), and self-service revoke/regenerate actions.
Drag-and-drop across 5 columns. Priority color-coding: red = high, yellow = medium, green = low.
Timestamped comment thread with both human and agent entries. Status changes auto-generate audit trail comments.
Clickable avatar with profile edit, admin link (super_admin only), dark mode toggle, and logout.
Update display name and change password with "last changed" timestamp.
Toggle dark mode from the avatar dropdown. Preference persists across sessions via localStorage.
Super admin view with platform-wide stats, user suspend/unsuspend, and project revoke/regenerate actions.
- 5-Column Kanban — TODO, Doing, Pending Confirming, Testing, Done
- Dual Auth — Email/password registration + Google OAuth
- Email Verification — 6-digit code + click-to-verify link; unverified users limited to 1 project (trial)
- Forgot & Reset Password — Email-based password reset (Resend or SMTP)
- Welcome Email — Sent on new user registration (Google OAuth auto-verified)
- Change Password — Self-service password change in profile with last-updated timestamp
- API Keys — Up to 10 projects per verified account (1 for unverified), each with 1000 API actions
- Project Descriptions — Optional description field per project
- API Key Actions — Revoke, regenerate, and edit API keys (both user and admin)
- External Agent API —
X-API-Keyauthenticated endpoints for agents to create, move, and comment on tickets - Drag & Drop — Move tickets between columns in the web UI
- Audit Trail — Every status transition auto-generates a timestamped comment
- Comments — Both humans and agents can leave comments on tickets
- Quota Management — Track API usage per project with
last_used_attimestamps (GET requests are free) - User Profile — Edit display name, view role and member-since date
- User Avatar — Circular avatar from Google OAuth photo or generated initial letter
- Dark Mode — Toggle from avatar dropdown, persisted to localStorage, supports system preference
- Super Admin — First registered user gets
super_adminrole with full admin dashboard - Admin Actions — Suspend/unsuspend users, revoke/regenerate API keys
- Admin Dashboard — View all users, projects, and tickets across the platform
- PostgreSQL Support — Production-ready with
asyncpgdriver (SQLite for development) - Railway Deploy — One-click deployment with
railway.tomlconfiguration - Docker Ready — Single-container deployment with multi-stage build
graph TB
subgraph Clients
WebUI["Web UI<br/>(React + Vite)"]
Agent["AI Agent<br/>(any HTTP client)"]
end
subgraph Backend["FastAPI Backend :8004"]
Auth["Auth API<br/>/auth/*"]
WebAPI["Web Ticket API<br/>/api-keys/*/tickets"]
ExtAPI["External Agent API<br/>/external/*"]
AdminAPI["Admin API<br/>/admin/*"]
Health["Health Check<br/>/health"]
SMTP["SMTP Email<br/>(password reset)"]
end
subgraph Data
DB[(SQLite / PostgreSQL)]
end
WebUI -->|JWT Bearer| Auth
WebUI -->|JWT Bearer| WebAPI
WebUI -->|JWT Bearer<br/>super_admin| AdminAPI
Agent -->|X-API-Key| ExtAPI
Auth -->|reset email| SMTP
AdminAPI --> DB
WebAPI --> DB
ExtAPI --> DB
Auth --> DB
erDiagram
User ||--o{ ApiKey : "has (max 10)"
ApiKey ||--o{ Ticket : contains
Ticket ||--o{ Comment : has
User {
int id PK
string email UK
string hashed_password
string display_name
string google_id
string avatar_url
string role "user|super_admin"
bool is_active
bool email_verified
datetime password_changed_at
datetime created_at
}
ApiKey {
int id PK
int user_id FK
string name
string description
string key UK "64-char hex"
int usage_count
bool is_active
datetime last_used_at
datetime created_at
}
Ticket {
int id PK
int api_key_id FK
string title
string description
enum status "todo|doing|pending_confirming|testing|done"
enum priority "low|medium|high"
int order
string external_ref
}
Comment {
int id PK
int ticket_id FK
string author
string content
bool is_status_change
}
backend/
app/
core/ # config, database, security (JWT + API key auth), email (SMTP)
models/ # User, ApiKey, Ticket, Comment (SQLAlchemy async)
schemas/ # Pydantic request/response models
api/
auth.py # register, login, Google OAuth, forgot/reset/change password
api_keys.py # CRUD + revoke/regenerate for projects (max 10 per account)
tickets.py # CRUD + move (JWT auth, web UI)
external.py # Agent API (X-API-Key auth, quota-enforced)
admin.py # Admin API (super_admin only, suspend/unsuspend, revoke/regen)
frontend/
src/
components/ # KanbanBoard, KanbanColumn, TicketCard, TicketModal, UserAvatar, Layout
contexts/ # AuthContext (JWT state), ThemeContext (dark mode)
pages/ # Login, Register, ForgotPassword, ResetPassword, Settings, Board, Profile, Admin
api/ # Axios client with auth interceptor
e2e/
test_e2e.py # API-level E2E tests (2 scenarios)
playwright_e2e_steps.md # Playwright MCP browser test guide
git clone https://github.com/osisdie/vibe-kanban.git
cd vibe-kanban
# Create .env
cp .env.example .env
# IMPORTANT: Change JWT_SECRET_KEY to a random value
# openssl rand -hex 32
# Build and run
docker compose up --buildOpen http://localhost:8004 — both API and UI are served from a single container.
Prerequisites: Python 3.10+, Node.js 18+
git clone https://github.com/osisdie/vibe-kanban.git
cd vibe-kanban
# Create .env from example
cp .env.example .env
# Backend
python -m venv .venv
source .venv/bin/activate
pip install -r backend/requirements.txt
# Frontend
cd frontend && npm install && cd ..# Terminal 1 — Backend (auto-creates SQLite DB on first run)
source .venv/bin/activate
cd backend && uvicorn app.main:app --reload --port 8004
# Terminal 2 — Frontend
cd frontend && npm run devPorts
8004/5177are chosen to avoid conflicts with common dev servers. Change them in.env,vite.config.ts, andMakefileif needed.
- Create a project in the web UI → copy the API key
- Agent syncs TODO list at session start:
POST /api/v1/external/tickets {"title": "Implement auth", "external_ref": "task-001"} - Agent updates status as it works:
PATCH /api/v1/external/tickets/{id}/move {"status": "doing"} - Agent reports blockers (missing credentials, needs approval, etc.):
PATCH /api/v1/external/tickets/{id}/move {"status": "pending_confirming"} POST /api/v1/external/tickets/{id}/comments {"content": "Need DB credentials"} - Agent re-reads the board if it loses context:
GET /api/v1/external/tickets - Humans can also add/edit/move tickets directly in the web UI
Authenticate with X-API-Key header. Only mutating requests (POST/PUT/PATCH/DELETE) count toward the 1000-action quota.
curl -X POST http://localhost:8004/api/v1/external/tickets \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Implement auth module", "priority": "high", "external_ref": "task-001"}'curl -X PATCH http://localhost:8004/api/v1/external/tickets/1/move \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "doing"}'curl -X POST http://localhost:8004/api/v1/external/tickets/1/comments \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Blocked: waiting for DB credentials"}'curl http://localhost:8004/api/v1/external/tickets \
-H "X-API-Key: YOUR_KEY"curl http://localhost:8004/api/v1/external/usage \
-H "X-API-Key: YOUR_KEY"
# {"name": "My Project", "usage_count": 42, "remaining": 958}| Status | Description |
|---|---|
todo |
Not started |
doing |
Currently in progress |
pending_confirming |
Blocked — waiting for approval, credentials, plan confirmation, etc. |
testing |
Implementation complete, under test |
done |
Finished |
The Dockerfile uses a multi-stage build: Node builds the React frontend, then Python serves both the API and static files from a single container.
# Build
docker build -t vibe-kanban .
# Run
docker run -d \
-p 8004:8004 \
-e JWT_SECRET_KEY=$(openssl rand -hex 32) \
-e FRONTEND_URL=https://your-domain.com \
-v kanban-data:/app/backend/data \
vibe-kanbanOr with docker compose:
docker compose up -dImportant: Railway containers are ephemeral — SQLite data is lost on every redeploy. You must use Railway's managed PostgreSQL.
- Connect your GitHub repo in Railway dashboard
- Add Service > PostgreSQL — this creates a managed, persistent database
- Set the
DATABASE_URLenvironment variable on your app service:postgresql+asyncpg://${{Postgres.PGUSER}}:${{Postgres.PGPASSWORD}}@${{Postgres.PGHOST}}:${{Postgres.PGPORT}}/${{Postgres.PGDATABASE}} - Set
JWT_SECRET_KEYto a strong random value - Set
FRONTEND_URLto your Railway public URL (e.g.https://your-app.up.railway.app) - Set SMTP variables for password reset emails (see Environment Variables below)
- Deploy — the first registered user automatically becomes
super_admin
Data safety: PostgreSQL data lives in Railway's managed storage, completely independent of app container lifecycle. Redeploys, rollbacks, and scaling do not affect your data.
| Platform | Difficulty | Notes |
|---|---|---|
| Render | Easy | Connect GitHub repo, add managed PostgreSQL. |
| Fly.io | Medium | fly launch with Dockerfile. Global edge deployment. |
| Coolify (self-hosted) | Medium | Full control on your own VPS. Built-in auto-HTTPS. |
| VPS + Caddy | Advanced | Most flexible. Caddy provides automatic HTTPS via Let's Encrypt. |
- Change
JWT_SECRET_KEYto a strong random value (openssl rand -hex 32) - Set
FRONTEND_URLto your actual domain (for CORS) - Configure SMTP variables for password reset emails
- Consider PostgreSQL for multi-user usage (change
DATABASE_URL) - Set up HTTPS via reverse proxy (Caddy, nginx, or platform-managed)
- Set up backups for the database
| Variable | Default | Required | Description |
|---|---|---|---|
DATABASE_URL |
sqlite+aiosqlite:///./vibe_kanban.db |
No | Database connection string |
JWT_SECRET_KEY |
change-me-to-a-random-secret |
Yes | JWT signing secret — must change in production |
JWT_ALGORITHM |
HS256 |
No | JWT algorithm |
JWT_EXPIRE_MINUTES |
1440 |
No | Token expiry (default 24h) |
GOOGLE_CLIENT_ID |
(empty) | No | Google OAuth client ID (optional) |
GOOGLE_CLIENT_SECRET |
(empty) | No | Google OAuth client secret (optional) |
GOOGLE_REDIRECT_URI |
http://localhost:8004/api/v1/auth/google/callback |
No | OAuth redirect URL |
EMAIL_PROVIDER |
smtp |
No | Email provider: smtp or resend |
SMTP_HOST |
(empty) | No | SMTP server hostname (e.g. smtp.gmail.com) |
SMTP_PORT |
587 |
No | SMTP port (587 for TLS, 465 for SSL) |
SMTP_USER |
(empty) | No | SMTP username / sender email |
SMTP_APP_PASSWORD |
(empty) | No | SMTP app password (for Gmail: App Passwords) |
RESEND_API_KEY |
(empty) | No | Resend API key (recommended for PaaS like Railway) |
RESEND_FROM_EMAIL |
(empty) | No | Resend sender email (must be from a verified domain) |
FRONTEND_URL |
http://localhost:5177 |
No | Frontend URL for CORS and OAuth redirects |
API_V1_PREFIX |
/api/v1 |
No | API version prefix |
# Start the backend, then run tests
source .venv/bin/activate
cd backend && uvicorn app.main:app --port 8004 &
sleep 3 && python ../e2e/test_e2e.pyTests cover:
- Single task lifecycle — create → todo → doing → testing → done + audit trail verification
- Multi-task workflow — 3 tasks with interleaved transitions, comments, blockers, and final state assertion
pip install pre-commit
pre-commit install- Ruff — Python linting + formatting (backend)
- TypeScript — type checking (frontend)
- General — trailing whitespace, YAML/JSON validation, large file check
- GitHub Actions (
.github/workflows/ci.yml) — backend lint+test, frontend typecheck+build - GitLab CI (
.gitlab-ci.yml) — equivalentlint → test → buildstages
Contributions are welcome! Please open an issue first to discuss what you'd like to change.
# Development setup
pip install pre-commit && pre-commit installMIT







