Skip to main content
Glama
amscotti

ClickDown

by amscotti

ClickDown

A fast, lightweight, multi-team Kanban task manager built on Bun and SQLite. ClickDown uses a hypermedia-driven architecture — server-side rendering with HTMX and Alpine.js — to deliver a responsive, app-like experience without a heavy client-side framework or build step.

It is local-first: a single Bun process provides the database (embedded SQLite), file storage (data/uploads/), and real-time updates (in-process WebSockets). No CDN, cloud service, or external broker is required — the app runs fully offline or on a LAN.

✨ Features

Tasks

  • Title, description (with Markdown rendering + Write/Preview toggle), priority (low / medium / high / urgent), assignee, and due date

  • Inline editing of every field from a task detail modal

  • Checklists, comments, labels, and file attachments (stored under data/uploads/)

  • Duplicate a task, or bulk-select and delete many at once

  • Archive tasks (soft-hide) with a recycle-bin style archive view; permanent purge (single task or empty archive)

  • Activity log per task tracking who changed what

  • Shareable deep links — every task has a /tasks/:id URL that opens its board with the task modal expanded

  • Priority color-coding and overdue / due-soon badges

  • Dashboard summary of assigned tasks with team/board context

Boards

  • Boards created with a default Backlog → To Do → In Progress → Done flow

  • Customizable columns (add, edit, delete, reorder, color, WIP limits) — admin/owner only

  • Drag-and-drop tasks within a column, across columns; column reorder for admins

  • Live task search across the board, "My tasks only" filter, and "Show archived"

  • Keyboard shortcuts (/ focuses search, n adds a task, Esc closes modals)

Teams & collaboration

  • Create teams, or join via a tokenized invite link (slug alone is not enough; owners/admins can regenerate the link)

  • Owner / admin / member roles, with a member-management UI to change roles and remove members

  • Isolated boards and tasks per team with membership-based access control

  • Board/column structure mutations require admin or owner; task work is open to all members

Real-time (WebSocket)

  • Live presence — avatars appear/disappear as team members open or close the board; new viewers receive a roster of who's already there

  • Live task updates — create, edit, move, delete, and archive operations broadcast to all viewers instantly

  • Refresh safety — remote changes reload the board with your search/filters preserved, and are deferred (with a notice) while you have a dialog open so unsaved edits are never lost

  • Built on Bun's native server.publish() + HTMX WS Extension + Alpine.js; no external message broker required

Accounts & UX

  • Registration / login / logout with bcrypt-hashed passwords (cost 12) and strength validation (8+ chars with upper, lower, digit, and special character)

  • Profile settings to update display name, email, and password (email or password change requires your current password; other sessions are revoked)

  • API tokens for AI agents — create/revoke personal tokens from the profile page (see MCP below)

  • Auto-generated initials avatars

  • Light / dark theme toggle (persisted to localStorage)

  • Toast notifications for all actions — visible even above open modals, and on network failures

  • Responsive layout and a custom design system over a modernized Pico CSS base

  • Structured logging via pino; health probes at /health and /ready

AI agent API (MCP)

  • Model Context Protocol endpoint at POST /api/mcp (Streamable HTTP, JSON-RPC 2.0)

  • Personal bearer tokens (SHA-256 hashed at rest) created from the profile page, with a one-step claude mcp add command shown on creation

  • 16 tools covering teams, boards, tasks, checklists, comments, and search — mutations broadcast live to open boards

Related MCP server: Planka MCP Server

🛠 Tech Stack

Layer

Technology

Runtime & server

Bun (Bun.serve)

Database

SQLite via bun:sqlite

Templating

@kitajs/html (server-side JSX → HTML)

Interactivity

HTMX + Alpine.js

Styling

Pico CSS + a custom design system

Auth

bcrypt password hashing, cookie sessions, SHA-256-hashed API tokens

Agent API

MCP Streamable HTTP (JSON-RPC 2.0, no SDK dependency)

IDs

nanoid

Logging

pino

Lint / Format

Biome

Testing

bun test (unit + integration) + Playwright (E2E, Chromium + Firefox)

🚀 Getting Started

Prerequisites

Bun 1.3.14 is the only requirement. Install or upgrade it if needed:

curl -fsSL https://bun.sh/install | bash
bun upgrade

If you use mise, the pinned mise.toml provides the same version automatically (mise install).

Installation

git clone <repository-url>
cd ClickDown
bun install

Database Setup

bun run dev and bun run start apply pending SQLite migrations automatically. You can also run them explicitly:

bun run db:migrate

By default the database lives at data/clickdown.db (override with DB_PATH).

Running the App

bun run dev     # development, with hot reload (recommended)
bun run start   # production

The app is then available at http://localhost:3000.

Health checks: GET /health (liveness) and GET /ready (schema and writable local upload storage).

All browser runtime assets are served locally from installed packages. Running ClickDown does not require CDN or cloud-service access.

Docker

A production-ready Dockerfile is included:

docker build -t clickdown .
docker run -p 3000:3000 -v clickdown-data:/app/data -e NODE_ENV=production clickdown

The container:

  • Applies pending migrations before accepting traffic, and ships a HEALTHCHECK on /ready

  • Installs production dependencies only in the runtime stage (no Playwright/TypeScript/Biome)

  • Runs as the non-root bun user with the base image pinned by digest

  • Persists SQLite data (and uploads) in a named volume (clickdown-data)

  • Sets NODE_ENV=production (strict CSRF Origin checks)

  • Declares STOPSIGNAL SIGTERM — the app shuts down gracefully (stops accepting requests, closes WebSocket connections, closes the database)

  • Exposes port 3000

The SQLite, in-memory rate limit, and WebSocket design supports one application process. Do not run multiple replicas against the same database volume.

.dockerignore excludes test files, local DBs, and dev tooling for a minimal image.

Optional smoke: bun run smoke:docker — builds the image, polls /ready, checks /health, and always cleans up (requires Docker).

⚙️ Configuration

ClickDown is configured through environment variables (Bun auto-loads .env):

Variable

Default

Description

PORT

3000

Port the HTTP server listens on

DB_PATH

data/clickdown.db

SQLite database file path

NODE_ENV

Set to production for CSRF Origin requirements and quieter logs

APP_URL

request origin

Browser-facing base URL used for invite links and Origin checks; set this behind a reverse proxy

TRUST_PROXY

false

Trust X-Forwarded-For for rate-limit keys; enable only behind a controlled proxy

COOKIE_SECURE

inferred from APP_URL

Force (true) or disable (false) the Secure cookie flag

E2E_TEST

Test-only flag that disables rate limiting; set automatically by Playwright, never in production

UPLOAD_DIR

data/uploads

Local directory for task attachments

LOG_LEVEL

debug / info

Pino log level (debug in dev, info in production by default)

🤖 MCP (AI agent) endpoint

ClickDown exposes a Model Context Protocol endpoint so AI agents (Claude Code, Cursor, custom scripts, etc.) can find and work on tasks directly.

Endpoint: POST /api/mcp (Streamable HTTP transport, JSON-RPC 2.0)

Setup

  1. Open Profile → API Tokens (MCP) and create a token. It is shown once — copy it.

  2. Register ClickDown with Claude Code (the profile page shows this command pre-filled with your token and URL):

    claude mcp add --transport http clickdown http://localhost:3000/api/mcp \
      --header "Authorization: Bearer cd_your_token_here"
  3. In Claude Code, run /mcp to verify the connection.

For config-file based clients (Cursor, etc.):

{
  "mcpServers": {
    "clickdown": {
      "type": "http",
      "url": "http://localhost:3000/api/mcp",
      "headers": { "Authorization": "Bearer cd_your_token_here" }
    }
  }
}

Tokens act as your user (full team access) and can be revoked at any time from the profile page. Only the SHA-256 hash is stored; session cookies are not accepted on the endpoint.

Available tools

Tool

Purpose

list_teams / list_boards / get_board

Navigate teams → boards → columns

list_tasks

Tasks on a board (filter by column, assignee, archived)

get_task

Full detail: description, checklist, comments, labels, attachments

create_task / update_task / move_task / archive_task / delete_task

Task lifecycle

add_comment

Leave progress notes on a task

add_checklist_item / toggle_checklist_item / delete_checklist_item

Checklist management

search_tasks

Text search across all accessible boards

get_my_tasks

Everything assigned to the token's user

All mutations broadcast over the existing WebSocket layer, so open boards update live when an agent makes a change. A typical agent flow: list_teamslist_boardslist_tasksget_taskmove_task + add_comment.

📁 Project Structure

src/
├── index.tsx             # Entry point: handleRequest() + Bun.serve()
├── config/               # Database + migrations runner
├── middleware/           # Session auth
├── mcp/                  # MCP protocol (JSON-RPC) + agent tool definitions
├── models/               # TypeScript types
├── realtime/             # WebSocket pub/sub + presence
├── routes/               # HTTP route handlers
├── services/             # Business logic
├── schemas/              # Zod validation
├── utils/                # access helpers, logger, password, HTML safety
└── components/           # Kita JSX pages, fragments, modals
public/                   # Static assets (custom.css, app.js, favicon)
migrations/               # SQL migrations (001–018, checksummed + auto-applied)
scripts/                  # migrate.ts, e2e-reset.ts
tests/                    # unit, integration, e2e
.github/workflows/        # CI: static gates, E2E, Docker smoke
mise.toml                 # Bun 1.3.14 toolchain pin
data/                     # SQLite DB + uploads (gitignored)

Request flow: Browser → HTMX → handleRequest → auth/access → service → SQLite → Kita JSX → HTML fragment.

🔒 Security notes

  • XSS prevention is enforced in CI@kitajs/ts-html-plugin (bun run xss:scan) fails the build on unescaped JSX; user content is escaped at render, and Markdown is sanitized with http(s)-only links

  • Local-only CSP — all scripts/styles/fonts load from the same origin (vendored, no CDN), with default-src 'self'

  • CSRF — Origin/Referer must match on every mutating request (required in production; APP_URL defines the external origin behind a proxy). The MCP endpoint is exempt: bearer tokens are never sent ambiently by browsers, and session cookies are rejected there

  • Sessions — HttpOnly + SameSite=Lax cookies, 7-day expiry; password and email changes require the current password and revoke other sessions (including live WebSockets)

  • API tokens — shown once at creation, stored as SHA-256 hashes, revocable instantly, deleted with the owning user; a periodic sweep closes WebSockets whose sessions or team access have been revoked

  • Invites — secret, regenerable tokens; team slugs alone never grant access

  • Rate limits — in-memory, scoped per endpoint and per user for auth, join, profile, token creation, markdown preview, uploads (10 MB per file), and task/board/team mutations

  • Attachments — served only to team members, forced Content-Disposition: attachment

💾 SQLite backups

The database is a single file (default data/clickdown.db, plus -wal/-shm in WAL mode).

# Safe online backup while the app is running (SQLite CLI)
sqlite3 data/clickdown.db ".backup 'data/clickdown-backup.db'"

# Or stop the app and copy the files
cp data/clickdown.db data/clickdown.db-wal data/clickdown.db-shm /path/to/backup/

Also back up data/uploads/ if you use attachments. Prefer volume snapshots when running under Docker.

🧪 Testing

  • Unit (tests/unit) — services/utils against in-memory SQLite

  • Integration (tests/integration) — real handleRequest in-process, including the MCP endpoint

  • E2E (tests/e2e) — Playwright against a server on port 3001, including security/hardening regressions (XSS, offline, realtime) and the MCP token flow. Runs on both Chromium and Firefox (two suites skip on Firefox: JS coverage and clipboard permission, which are Chromium-only Playwright APIs).

Install the browser binaries once before running E2E tests:

bunx playwright install chromium firefox
bun run test              # unit + integration
bun run test:coverage
bun run test:e2e          # full Playwright suite
bun run test:e2e:smoke    # smaller auth + security subset
bun run xss:scan          # JSX escaping audit (@kitajs/ts-html-plugin)
bun run ci                # typecheck + XSS scan + lint + unit/integration
bun run ci:full           # ci + full e2e

GitHub Actions (.github/workflows/ci.yml) runs the full gate set on push/PR: static checks + unit/integration, the Playwright suite, and a Docker build with a readiness-probed smoke test.

📋 Available Commands

Command

Description

bun run dev

Start with hot reload

bun run start

Start server (applies migrations first)

bun run build

Bundle entry to dist/ (Bun target)

bun run db:migrate

Run database migrations

bun run test

Unit + integration tests

bun run test:coverage

Tests with coverage report

bun run test:e2e

Playwright E2E

bun run test:e2e:smoke

Fast E2E subset

bun run lint / lint:fix

Biome

bun run typecheck

tsc --noEmit

bun run xss:scan

JSX escaping audit

bun run ci

typecheck + XSS scan + lint + unit/integration

bun run ci:full

ci + full E2E

bun run smoke:docker

Build image, poll /ready, check /health

📄 License

MIT © 2026 amscotti — free to use, modify, and self-host, with attribution.

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Task manager your agent can fully operate: boards, tasks, sprints, roles, worklogs, day planner.

  • Manage projects, tasks, time tracking, and team collaboration through natural language.

  • Persistent memory and knowledge management for AI agents with semantic search and 50+ tools.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/amscotti/ClickDown'

If you have feedback or need assistance with the MCP directory API, please join our Discord server