{"id":"1806d171-290b-4f3b-b74d-dfd913ce6fb2","slug":"adam-dangerfield-agent-kanban","kind":"skill","name":"Agent-Kanban","summary":"Self-hosted kanban / ticketing system for coordinating AI agents across projects via a cheap REST API.","owner":"Adam-Dangerfield","repoUrl":"https://github.com/Adam-Dangerfield/Agent-Kanban","license":"AGPL-3.0","licenseTier":"strong-copyleft","trust":"community","stars":4,"match":null,"fewStars":true,"format":"skill","createdAt":"2026-06-07T06:02:04.000Z","addedAt":"2026-10-21T23:14:05.522Z","logoUrl":"https://avatars.githubusercontent.com/u/52532991?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["databases","productivity","cloud-infra"],"tags":["agents","ai-agents","docker","express","kanban","postgresql","rest-api","self-hosted"],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-22T04:29:00.368Z","sample":false,"url":"https://conduits.dev/tools/adam-dangerfield-agent-kanban","language":"JavaScript","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# Kanban — Self-Hosted Agent Task Tracker\n\n[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)\n\n> **Status: working personal tool.** A self-hosted coordination layer between AI\n> agents. The frontend is fully interactive; the backend runs on PostgreSQL in\n> Docker with real auth (JWT + per-agent tokens) and per-project access control.\n> It targets a single manager plus many agents — not multi-tenant SaaS — so read\n> the Auth section before exposing it on the public internet.\n\nA self-hosted **kanban / ticketing system** designed for one specific workflow:\nAI agents across multiple projects record and update their tasks here via a\ncheap REST API; a human reviews the board in one place.\n\nAgents don't need a UI. They need four calls: create a task, claim it, post a\nstatus message, mark it done. This system is optimised for exactly that, while\nstill rendering a full interactive board for the human reviewer.\n\n---\n\n## Features\n\n- **Projects and boards** — separate project boards switched from a left\n  sidebar. Each project has epics, stories, and tasks.\n- **Task lifecycle** — Backlog → To Do → In Progress → Done, with drag-and-drop\n  column moves. Tasks carry title, description, notes, priority, assignee,\n  epic/story grouping, and git branch + merge state\n  (`none → dev → pr → merged`).\n- **Agent messages** — tasks have a chat-style message thread where agents post\n  what they actually did, separate from the description of the work.\n- **Activity log** — every state change is appended to an immutable audit trail\n  per task.\n- **Cross-team requests** — one project can raise a request against another.\n  Accepting a request spawns a linked card on the receiving team's board.\n- **Shared inbox** — incoming and outgoing cross-team requests in one view.\n- **Search and filters** — global text search; filter by assignee, priority,\n  epic; sort by priority.\n- **Auth & per-project RBAC** — password-protected manager session (JWT) plus\n  per-agent API tokens (bcrypt-hashed, shown once at mint time). **Every endpoint\n  requires a token.** Access is granted per `(agent, project)` as `read`/`write`;\n  agents see only the projects they're granted.\n- **Admin panel** — searchable in-app management of agents, per-project\n  permissions (pivot by agent or by project), and provisioning tokens.\n- **Scoped provisioning** — a root or admin-minted scoped token lets new agents\n  self-register and self-grant access up to a ceiling, so a fleet can onboard\n  without the manager hand-minting every token.\n- **First-run setup** — a fresh instance walks an admin through creating the\n  first project before minting agent tokens.\n- **File attachments** — attach files (≤ 20 MB) to tasks and requests; local\n  disk in dev, S3 with presigned downloads in production.\n- **Zero-build frontend** — React 18 via CDN with in-browser Babel. No build\n  step; open `Kanban.html` or serve the folder.\n- **Docker Compose stack** — nginx (static) + Express API + PostgreSQL in three\n  services, ready to run with one command.\n\n---\n\n## Architecture\n\n```\nBrowser\n  └─ nginx (:8080)\n       ├─ static files   ← Kanban.html + *.jsx + api.js + styles.css\n       └─ /api/* proxy ──► Express API (:4000)\n                                └─► PostgreSQL (:5432)\n```\n\n- **Frontend**: plain static files. React 18 via CDN; `.jsx` files are\n  transpiled by in-browser Babel at load time. No build step required.\n  `api.js` is the single file responsible for camelCase ↔ snake_case\n  translation between frontend and API — nothing else does translation.\n- **API**: Express (`server/`). Runs against an in-memory store when\n  `DATABASE_URL` is unset (useful for frontend development without Postgres),\n  or against PostgreSQL in production.\n- **Database**: PostgreSQL. Schema + incremental changes are applied by a small\n  migration runner (`server/scripts/migrate.js`) on startup — so updates are\n  non-destructive (no volume wipe). See [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md).\n\n---\n\n## Quick Start\n\n### Full stack (Docker Compose — recommended)\n\n```bash\n# 1. Build images and start all three services.\n#    The schema is applied automatically to a fresh Postgres volume.\ndocker compose up --build\n\n# 2. Seed sample data (run once; safe to re-run — TRUNCATEs first).\ndocker compose --profile seed run --rm seed\n\n# 3. Mint a per-agent API token (repeat for each agent).\n#    The raw token is printed once — copy it immediately.\ndocker compose exec api npm run mint-token <agent-id>\n#    e.g.  docker compose exec api npm run mint-token claude\n```\n\nURLs after `up`:\n- Web board: `http://localhost:8080`\n- API health: `http://localhost:4000/api/health`\n\nCopy `server/.env.example` to `server/.env` and set at minimum:\n\n```\nJWT_SECRET=<long random string>\nDATABASE_URL=postgres://kanban:kanban@db:5432/kanban\nWEB_ORIGIN=http://localhost:8080\n```\n\n### API-only dev (no Docker, in-memory store)\n\n```bash\ncd server && npm install && npm run dev\n# DATABASE_URL unset → MemoryStore (resets on restart, no Postgres needed)\ncurl http://localhost:4000/api/health\n```\n\n---\n\n## API\n\nAll endpoints are under `/api`. All request and response bodies are\nsnake_case JSON. See [`API_CONTRACT.md`](API_CONTRACT.md) for the full field\nmaps and auth contract.\n\n```\n# open (no token) ------------------------------------------------------\nPOST   /api/auth/login            { password }            → { ok, actor, token:<JWT> }\nPOST   /api/auth/token            { token }               → { ok, actor, token:<raw> }\nGET    /api/health                                        → { ok, ts }\n\n# identity, agents, permissions, provisioning --------------------------\nGET    /api/me                                            → { id, name, role, is_admin, permissions }\nGET    /api/agents\nPOST   /api/agents                { id, name, ... }       → 201 { agent, token }   [admin / provision token]\nPATCH  /api/agents/:id            { is_admin?, ... }      → Agent                  [admin]\nGET/PUT /api/agents/:id/permissions[/:projectId]                                   [admin / scoped]\nPOST   /api/provision-tokens      { label, scope:[] }     → 201 { ..., token }     [admin]\n\n# projects / epics / stories (filtered to readable) --------------------\nGET    /api/projects\nPOST   /api/projects              { id, name, key, ... }  → 201 Project            [admin]\nGET    /api/projects/:id/epics  ·  POST → 201 Epic        [write on project]\nGET    /api/epics/:id/stories   ·  POST → 201 Story       [write on project]\n\n# tasks ----------------------------------------------------------------\nGET    /api/projects/:id/tasks   ·  GET /api/tasks/:id\nPOST   /api/projects/:id/tasks    { title, ... }          → 201 Task\nPATCH  /api/tasks/:id             { ...fields, _log? }    → Task\nDELETE /api/tasks/:id                                     → 204\nPOST   /api/tasks/:id/comments    { body }                → 201 Comment\n\n# attachments (multipart, ≤ 20 MB) ------------------------------------\nPOST   /api/tasks/:id/attachments   ·  POST /api/requests/:id/attachments\nGET    /api/attachments/:id      ·  DELETE /api/attachments/:id\n\n# cross-team requests --------------------------------------------------\nGET    /api/projects/:id/requests\nPOST   /api/requests              { to_project_id, ... }  → 201 Request\nPOST   /api/requests/:id/actions  { action }              → { request, spawnedTask? }\n  action ∈ accept | decline | start | done | cancel\n```\n\n`_log` on `PATCH` is an optional human-readable string appended to the task's\nactivity feed (e.g. `\"merged feat/x into main\"`).\n\n**Every endpoint requires `Authorization: Bearer <token>` except the three open\nones above.** A missing/invalid token → `401`; a valid token without access to\nthe project → `403`.\n\n---\n\n## Auth\n\nTwo credential types, both sent as `Authorization: Bearer <value>`:\n\n- **Manager JWT** — `POST /api/auth/login { password }` → signed JWT (valid 7\n  days), used by the web UI. The password is bcrypt-checked; `JWT_SECRET`\n  configures signing.\n- **Agent raw token** — each agent gets its own token, so every change is\n  attributed by name. Minted via `npm run mint-token <agent-id>` (or the admin\n  panel). Stored as a bcrypt hash; only the prefix (e.g. `agt_live_9f3c`) is kept\n  in plaintext. The raw value is shown **once** at mint time.\n\n**Every endpoint except `/api/health` and the two auth endpoints requires a\nvalid token — reads included.** Access is then checked per project: `read` and\n`write` per `(agent, project)`; admins bypass project checks; new agents start\nwith no access; `GET /api/projects` is filtered to what the caller can read.\nMissing/invalid token → `401`; valid token without project access → `403`.\n\n**Provisioning** (creating agents, minting tokens, granting permissions) uses a\nseparate `X-Provision-Token` header — either a root token (env `PROVISION_TOKEN`)\nor an admin-minted scoped token whose grants are capped to a project ceiling.\nProvision tokens can never set `is_admin`. See\n[`docs/AGENT_GUIDE.md`](docs/AGENT_GUIDE.md) for the onboarding walkthrough.\n\n---\n\n## Using the Kanban skill in an agent project\n\nThe `skills/kanban/` directory contains a portable Claude Code skill that gives\nany AI agent a dependency-free CLI over the full API.\n\n**Install:** copy `skills/kanban/` into the agent project's `.claude/skills/kanban/`.\n\n**Set two env vars** in the agent's `.env`:\n\n```\nKANBAN_URL=http://localhost:4000/api\nKANBAN_TOKEN=agt_live_xxxxxxxxxxxx\n```\n\n**Use it:**\n\n```bash\nnode .claude/skills/kanban/scripts/kanban.mjs me\nnode .claude/skills/kanban/scripts/kanban.mjs tasks aws --status in_progress\nnode .claude/skills/kanban/scripts/kanban.mjs claim AWS-101\nnode .claude/skills/kanban/scripts/kanban.mjs comment AWS-101 \"PR merged, marking done\"\nnode .claude/skills/kanban/scripts/kanban.mjs status AWS-101 done\n```\n\nThe skill (`skills/kanban/SKILL.md`) is loaded into agent context automatically\nwhen Claude Code detects it. See `docs/AGENT_GUIDE.md` for the full curl reference\nand onboarding walkthrough.\n\n---\n\n## Documentation\n\n| Document | Purpose |\n|---|---|\n| [`docs/USER_GUIDE.md`](docs/USER_GUIDE.md) | Using the web board: sign-in, board, tickets, attachments, cross-team requests, filters |\n| [`docs/ADMIN_GUIDE.md`](docs/ADMIN_GUIDE.md) | Admin panel: first-run setup, projects, agents, permissions, provision tokens |\n| [`API_CONTRACT.md`](API_CONTRACT.md) | Field maps (camelCase ↔ snake_case), full endpoint list, auth contract — the source of truth for backend/frontend alignment |\n| [`docs/AGENT_GUIDE.md`](docs/AGENT_GUIDE.md) | Practical guide for agents using the REST API (curl recipes, onboarding, import walkthrough) |\n| [`skills/kanban/SKILL.md`](skills/kanban/SKILL.md) | Portable Claude Code skill — concise instructions + CLI for AI agents |\n| [`docs/SPEC-rbac-attachments.md`](docs/SPEC-rbac-attachments.md) | Feature spec: auth matrix, per-project RBAC, attachments, scoped provisioning |\n| [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md) | Self-hosting in production: env vars, first deploy, attachments, backups |\n| [`ARCHITECTURE.md`](ARCHITECTURE.md) | Engineering detail: frontend architecture, backend internals, data model |\n| [`docs/DESIGN-NOTES.md`](docs/DESIGN-NOTES.md) | The *why*: agent-identity principles and other transferable design choices |\n| [`TEST_PLAN.md`](TEST_PLAN.md) | Test-case matrix (API + UI) |\n| [`CONTRIBUTING.md`](CONTRIBUTING.md) | Dev setup, project conventions, how to add an endpoint, PR guidance |\n| [`SECURITY.md`](SECURITY.md) | Security model, operator hardening checklist, how to report a vulnerability |\n| [`CHANGELOG.md`](CHANGELOG.md) | Release history |\n\n---\n\n## Repo Layout\n\n```\nKanban/\n├── Kanban.html            # frontend entry point\n├── styles.css             # all component styles\n├── data.js                # prototype mock seed data (window.SEED)\n├── api.js                 # camelCase↔snake_case + fetch wrapper\n├── app.jsx                # app shell, state, auth, all event handlers\n├── board.jsx              # board, columns, drag-and-drop\n├── detail.jsx             # ticket detail panel (incl. attachments)\n├── requests.jsx           # cross-team inbox\n├── components.jsx         # shared UI components\n├── setup.jsx              # first-run wizard + project creation\n├── admin.jsx              # admin panel (agents, permissions, tokens)\n├── tweaks-panel.jsx       # in-app layout/theme tweaks\n│\n├── server/                # Express API\n│   ├── src/\n│   │   ├── index.js       # all routes\n│   │   ├── store.js       # MemoryStore + PgStore\n│   │   ├── storage.js     # attachment storage (local disk / S3)\n│   │   └── seed-data.js   # seed content\n│   ├── scripts/\n│   │   ├── seed.js        # load seed data into Postgres\n│   │   ├── mint-token.js  # generate a per-agent token\n│   │   └── init-prod.js   # clean schema + admin bootstrap (prod)\n│   ├── db/schema.sql      # PostgreSQL schema\n│   ├── package.json\n│   ├── .env.example\n│   └── Dockerfile\n│\n├── skills/kanban/         # portable Claude Code skill (SKILL.md + CLI)\n├── backup/                # pg_dump → S3 backup sidecar image\n├── tests/                 # api.mjs (node:test) + ui.spec.mjs (Playwright)\n├── docs/                  # AGENT_GUIDE, SPEC-rbac-attachments\n│\n├── web.Dockerfile         # nginx image for the frontend\n├── nginx.conf             # serves static + proxies /api → api:4000\n├── docker-compose.yml     # full stack: db + api + web (+ seed profile)\n├── API_CONTRACT.md        # field maps + endpoint list (source of truth)\n└── ARCHITECTURE.md        # engineering internals\n```\n\n---\n\n## License\n\nThis project is licensed under the **GNU Affero General Public License v3.0\n(AGPLv3)**. See [`LICENSE`](LICENSE) for the full text.\n\nKey obligation: if you run a **modified** version of this software over a\nnetwork, you must make the corresponding source code available to the users of\nthat network service.\n"}