A self-hosted task board for AI agent teams. Model-agnostic REST API. Any agent framework, any model, any scale.
Pin your tasks, track your agents.
Tack is a lightweight Kanban board designed for human-AI collaboration. Your AI agents create, update, and move cards through a simple REST API. You see everything in a drag-and-drop web interface. Drag a card to "Approved" and your agents pick it up. Need a decision? Agents queue structured options with clickable buttons. You click, they resume.
No vendor lock-in. No cloud dependency. SQLite backend. Self-host in 30 seconds.
Status: alpha. Developed and tested internally on sandboxed development machines. If you deploy this: inspect the code, run in a VM or isolated environment, and back up your data before upgrading. This has not been independently security audited. See SECURITY.md for details.
Auth note: If you set TACK_API_KEY, the server will require the key for all write operations via the API. However, the web UI does not currently send the API key with its requests. This means drag-and-drop, card creation, and other UI write actions will be rejected by the server when a key is set. Agent API calls (which include the key in headers) will work correctly. Web UI auth support is planned.
pip install fastapi uvicorn
python server.pyOpen http://localhost:8795 in your browser.
docker compose up -dOr build manually:
docker build -t tack .
docker run -d -p 8795:8795 -v tack-data:/data tackGive your agent the contents of TOOL.md as context. It contains:
- REST API reference with all endpoints
- Column and priority definitions
- OpenAI function-calling tool definitions (works with any compatible framework)
- Usage guidelines (when to create cards, when to move them)
The tool spec is model-agnostic. It works with OpenAI, Anthropic, Ollama, llama.cpp, or any system that supports function calling or HTTP tool use.
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/board |
Get all cards organized by column |
GET |
/api/cards |
List cards (filter by ?column=, ?assignee=, ?fields=) |
POST |
/api/cards |
Create a card |
GET |
/api/cards/{id} |
Get a single card |
PATCH |
/api/cards/{id} |
Update card fields |
POST |
/api/cards/{id}/move |
Move card to a column |
POST |
/api/cards/{id}/claim |
Claim card (prevents other agents from grabbing it) |
POST |
/api/cards/{id}/release |
Release a claim |
POST |
/api/cards/{id}/notes |
Add a note to the card's trace log |
GET |
/api/cards/{id}/notes |
Read card notes |
POST |
/api/cards/{id}/decision |
Request a human decision (with structured options) |
POST |
/api/cards/{id}/decide |
Submit a decision |
GET |
/api/decisions |
List all pending decisions |
POST |
/api/cards/{id}/approval |
Request approval for a plan (yes/no) |
POST |
/api/cards/{id}/approve |
Submit an approval or denial |
GET |
/api/approvals |
List all pending approvals |
POST |
/api/batch |
Execute multiple operations in one call |
DELETE |
/api/cards/{id} |
Delete a card |
GET |
/api/activity |
Recent activity log |
GET |
/api/changes |
Poll for changes since a timestamp |
GET |
/health |
Health check |
- Sparse-field reads —
?fields=id,title,column_namereturns only the fields you ask for. Cuts response size ~5x, saves tokens on every poll. - Atomic claiming with TTL —
POST /claimlocks a card so two agents don't work on the same task. Claims auto-expire after a configurable TTL. 409 conflict with recovery hint if already claimed. - Per-card trace log — Append-only notes on each card. Agents document reasoning, findings, and handoff context. Makes agent-to-agent handoffs possible without losing context.
- Human-in-the-loop decisions — Agents post structured questions with options. Humans see clickable buttons in the UI. Click to decide, card moves forward.
GET /api/decisionsgives you the decision queue. - Plan approvals — Agents propose a plan, humans approve or deny. Simpler than decisions — no options to choose from, just yes/no with optional comments. Denied cards move to blocked with the reason logged.
GET /api/approvalsgives you the approval queue. - Batch operations —
POST /api/batchwith an array of actions. Create, move, update, claim, release, and note in a single call. - Webhooks — Planned but not yet implemented. See Spur for webhook event relay in the meantime.
| Column | Purpose |
|---|---|
| Inbox | New tasks, not yet reviewed |
| Approved | Human approved — agents pick these up |
| In Progress | Actively being worked on |
| Awaiting Decision | Needs human input (with structured options) |
| Awaiting Approval | Agent proposed a plan, needs yes/no |
| Deferred | Postponed — auto-returns to Awaiting Decision after TACK_DEFER_DAYS |
| Done | Completed |
| Blocked | Cannot proceed |
Environment variables:
| Variable | Default | Description |
|---|---|---|
TACK_HOST |
127.0.0.1 |
Bind address |
TACK_PORT |
8795 |
Port number |
TACK_DB |
./data/board.db |
SQLite database path |
TACK_DONE_ARCHIVE_DAYS |
7 |
Auto-hide done cards older than N days from board view |
TACK_DEFER_DAYS |
7 |
Days before deferred cards auto-return to Awaiting Decision |
TACK_API_KEY |
(none) | Optional API key for write operations (reads remain open) |
n— New card/— Focus searchEsc— Close modal / clear search- Double-click card to edit
- Web UI does not support API key auth. If
TACK_API_KEYis set, the web UI cannot perform write operations (create cards, drag-and-drop, edit). API clients that send the key in headers work fine. Fix planned for next release. - Webhooks not yet implemented. See Spur for webhook event relay.
- No built-in HTTPS or rate limiting. Use a reverse proxy for production deployments. See SECURITY.md.
- Python 3.10+
- FastAPI
- Uvicorn
- SortableJS (loaded from CDN in the frontend)
MIT