* feat(api): add webhook CRUD API router and tests Add tRPC router for managing workspace webhooks: - list, create, update, delete endpoints (admin role required) - test endpoint to send a synthetic payload to a webhook URL - URL validation, event subscription filtering - Unit tests for all router procedures - Integration tests with PGlite test database - Add vitest config and test infrastructure for API package Depends on #391 (DB schema & repository). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(api): use assertPermission instead of assertUserInWorkspace Replace assertUserInWorkspace with assertPermission("workspace:manage") per project conventions. The permissions system is the preferred authorization approach for new code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(api): use @kan/db alias instead of relative imports in tests Replace relative path imports (../../db/src/...) with the @kan/db alias configured in vitest.config.ts for consistency and robustness. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(api): use webhookUrlSchema in router input validation Cherry-pick router-related changes from b2cc9ac: - Use extracted webhookUrlSchema zod validator in create/update input schemas for consistent SSRF checks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(api): replace dynamic import with static import for webhook utility Add packages/api/src/utils/webhook.ts with sendWebhookToUrl, createCardWebhookPayload, and webhookUrlSchema. Replace the dynamic import() in the test endpoint with a static import at the top of the file for better tree-shaking, type-checking, and readability. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(api): align sendWebhooksForWorkspace tests with merged PR #392 The merged delivery utility uses client-side event filtering (getActiveByWorkspaceId takes 2 args, not 3). Update test assertions to match the actual implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(web): add webhook management UI Add settings page for managing workspace webhooks: - Add webhooks page route and settings navigation link - Add webhook list view with status toggles and action menus - Add create/edit modal with URL validation and event selection - Add delete confirmation dialog - Add WEBHOOKS_ENABLED env flag for feature gating Depends on #393 (CRUD API router). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(web): remove dead env vars, extract TableRow, import webhookEvents - Remove unused WEBHOOK_URL and WEBHOOK_SECRET env vars (leftovers from earlier env-var-based design) - Move TableRow component outside WebhookList to avoid re-creation on every render - Import webhookEvents from @kan/db/schema instead of hardcoding - Simplify formatDate to only handle Date objects (strings are not returned by tRPC/Superjson) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(web): gate webhooks settings tab to admin role The webhook API requires admin role, but the settings tab was visible to all users (condition: true). Now matches the API's authorization requirement, addressing reviewer feedback on PR #394. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(web): use webhookEvents constant for form defaults Replace hardcoded event arrays with [...webhookEvents] in NewWebhookModal so default values stay in sync if new events are added to the schema. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(web): use date-fns with locale for webhook date formatting Replace hardcoded toLocaleDateString('en-US') with date-fns format() using the useLocalisation() hook's dateLocale, matching the pattern used throughout the codebase (ActivityList, DateSelector, etc.). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Kan
The open-source project management alternative to Trello.
Roadmap · Website · Docs · Discord
Features 💫
- 👁️ Board Visibility: Control who can view and edit your boards
- 🤝 Workspace Members: Invite members and collaborate with your team
- 🚀 Trello Imports: Easily import your Trello boards
- 🔍 Labels & Filters: Organise and find cards quickly
- 💬 Comments: Discuss and collaborate with your team
- 📝 Activity Log: Track all card changes with detailed activity history
- 🎨 Templates : Save time with reusable custom board templates
- ⚡️ Integrations (coming soon) : Connect your favourite tools
See our roadmap for upcoming features.
Screenshot 👁️
Made With 🛠️
Self Hosting 🐳
One-click Deployments
The easiest way to deploy Kan is through Railway. We've partnered with Railway to maintain an official template that supports the development of the project.
Docker Compose
Alternatively, you can self-host Kan with Docker Compose. This will set up everything for you including your postgres database and automatically run migrations.
-
Create a
.envfile with your environment variables (see Environment Variables section below) -
Use the provided
docker-compose.ymlfile or create your own with the following configuration:
services:
migrate:
image: ghcr.io/kanbn/kan-migrate:latest
container_name: kan-migrate
networks:
- kan-network
environment:
- POSTGRES_URL=${POSTGRES_URL}
depends_on:
postgres:
condition: service_healthy
restart: "no"
web:
image: ghcr.io/kanbn/kan:latest
container_name: kan-web
ports:
- "${WEB_PORT:-3000}:3000"
networks:
- kan-network
env_file:
- .env
environment:
- NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL}
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
- POSTGRES_URL=${POSTGRES_URL}
- NEXT_PUBLIC_ALLOW_CREDENTIALS=true
depends_on:
migrate:
condition: service_completed_successfully
restart: unless-stopped
postgres:
image: postgres:15
container_name: kan-db
environment:
- POSTGRES_DB=kan_db
- POSTGRES_USER=kan
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
ports:
- 5432:5432
volumes:
- kan_postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U kan -d kan_db"]
interval: 5s
timeout: 5s
retries: 10
restart: unless-stopped
networks:
- kan-network
networks:
kan-network:
volumes:
kan_postgres_data:
- Start the containers in detached mode:
docker compose up -d
The migrate service will automatically run database migrations before the web service starts. The application will be available at http://localhost:3000 (or the port specified in WEB_PORT).
Managing containers:
- To stop the containers:
docker compose down - To view logs:
docker compose logs -f - To view logs for a specific service:
docker compose logs -f webordocker compose logs -f migrate - To restart the containers:
docker compose restart - To rebuild after code changes:
docker compose up -d --build
For the complete Docker Compose configuration with all optional features, see docker-compose.yml in the repository.
Local Development 🧑💻
- Clone the repository (or fork)
git clone https://github.com/kanbn/kan.git
- Install dependencies
pnpm install
- Copy
.env.exampleto.envand configure your environment variables - Migrate database
pnpm db:migrate
- Start the development server
pnpm dev
Environment Variables 🔐
| Variable | Description | Required | Example |
|---|---|---|---|
POSTGRES_URL |
PostgreSQL connection URL | To use external database | postgres://user:pass@localhost:5432/db |
REDIS_URL |
Redis connection URL | For rate limiting (optional) | redis://localhost:6379 or redis://redis:6379 (Docker) |
EMAIL_FROM |
Sender email address | For Email | "Kan <hello@mail.kan.bn>" |
SMTP_HOST |
SMTP server hostname | For Email | smtp.resend.com |
SMTP_PORT |
SMTP server port | For Email | 465 |
SMTP_USER |
SMTP username/email | No | resend |
SMTP_PASSWORD |
SMTP password/token | No | re_xxxx |
SMTP_SECURE |
Use secure SMTP connection (defaults to true if not set) | For Email | true |
SMTP_REJECT_UNAUTHORIZED |
Reject invalid certificates (defaults to true if not set) | For Email | false |
NEXT_PUBLIC_DISABLE_EMAIL |
To disable all email features | For Email | true |
NEXT_PUBLIC_BASE_URL |
Base URL of your installation | Yes | http://localhost:3000 |
NEXT_API_BODY_SIZE_LIMIT |
Maximum API request body size (defaults to 1mb) | No | 50mb |
BETTER_AUTH_ALLOWED_DOMAINS |
Comma-separated list of allowed domains for OIDC logins | For OIDC/Social login | example.com,subsidiary.com |
BETTER_AUTH_SECRET |
Auth encryption secret | Yes | Random 32+ char string |
BETTER_AUTH_TRUSTED_ORIGINS |
Allowed callback origins | No | http://localhost:3000,http://localhost:3001 |
GOOGLE_CLIENT_ID |
Google OAuth client ID | For Google login | xxx.apps.googleusercontent.com |
GOOGLE_CLIENT_SECRET |
Google OAuth client secret | For Google login | xxx |
DISCORD_CLIENT_ID |
Discord OAuth client ID | For Discord login | xxx |
DISCORD_CLIENT_SECRET |
Discord OAuth client secret | For Discord login | xxx |
GITHUB_CLIENT_ID |
GitHub OAuth client ID | For GitHub login | xxx |
GITHUB_CLIENT_SECRET |
GitHub OAuth client secret | For GitHub login | xxx |
OIDC_CLIENT_ID |
Generic OIDC client ID | For OIDC login | xxx |
OIDC_CLIENT_SECRET |
Generic OIDC client secret | For OIDC login | xxx |
OIDC_DISCOVERY_URL |
OIDC discovery URL | For OIDC login | https://auth.example.com/.well-known/openid-configuration |
TRELLO_APP_API_KEY |
Trello app API key | For Trello import | xxx |
TRELLO_APP_API_SECRET |
Trello app API secret | For Trello import | xxx |
S3_REGION |
S3 storage region | For file uploads | WEUR |
S3_ENDPOINT |
S3 endpoint URL | For file uploads | https://xxx.r2.cloudflarestorage.com |
S3_ACCESS_KEY_ID |
S3 access key | For file uploads (optional with IRSA) | xxx |
S3_SECRET_ACCESS_KEY |
S3 secret key | For file uploads (optional with IRSA) | xxx |
S3_FORCE_PATH_STYLE |
Use path-style URLs for S3 | For file uploads | true |
S3_AVATAR_UPLOAD_LIMIT |
Maximum avatar file size in bytes | For file uploads | 2097152 (2MB) |
NEXT_PUBLIC_STORAGE_URL |
Storage service URL | For file uploads | https://storage.kanbn.com |
NEXT_PUBLIC_STORAGE_DOMAIN |
Storage domain name | For file uploads | kanbn.com |
NEXT_PUBLIC_USE_VIRTUAL_HOSTED_URLS |
Use virtual-hosted style URLs (bucket.domain.com) | For file uploads (optional) | true |
NEXT_PUBLIC_AVATAR_BUCKET_NAME |
S3 bucket name for avatars | For file uploads | avatars |
NEXT_PUBLIC_ATTACHMENTS_BUCKET_NAME |
S3 bucket name for attachments | For file uploads | attachments |
NEXT_PUBLIC_ALLOW_CREDENTIALS |
Allow email & password login | For authentication | true |
NEXT_PUBLIC_DISABLE_SIGN_UP |
Disable sign up | For authentication | false |
NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY |
Hide “Powered by kan.bn” on public boards (self-host) | For white labelling | true |
KAN_ADMIN_API_KEY |
Admin API key for stats and admin endpoints | For admin/monitoring | your-secret-admin-key |
See .env.example for a complete list of supported environment variables.
Contributing 🤝
We welcome contributions! Please read our contribution guidelines before submitting a pull request.
Contributors 👥
License 📝
Kan is licensed under the AGPLv3 license.
Contact 📧
For support or to get in touch, please email henry@kan.bn or join our Discord server.