Easy contributor license agreements for your GitHub projects.
See the codeCLAHub provides a low-friction way to have a Contributor License Agreement for your open source project on GitHub. Contributors digitally sign your CLA by signing in with GitHub, and pull requests are automatically marked with a status check based on whether all commit authors have signed.
Running at cla-hub.io
For project owners:
clahub_-prefixed Bearer tokens)/api/badge/{owner}/{repo})X-RateLimit-* headersdocker compose up -d with auto-migration, health checks, and SQLite volume persistenceAPP_NAME, APP_LOGO_URL, APP_PRIMARY_COLORGET /api/health for uptime monitoring and container health checksFor contributors:
CLAHub provides a REST API at /api/v1/ for programmatic access. Authenticate with a Bearer token (generate one under Settings > API Keys).
# List signatures for a repo agreement
curl -H "Authorization: Bearer clahub_xxxx" \
https://cla-hub.io/api/v1/agreements/my-org/my-repo/signatures
# Check if a user has signed
curl https://cla-hub.io/api/v1/agreements/my-org/my-repo/check/username
# Export signatures as CSV
curl -H "Authorization: Bearer clahub_xxxx" \
https://cla-hub.io/api/v1/agreements/my-org/my-repo/export/csv
Full API documentation is available in the OpenAPI spec.
Add a CLA status badge to your README:
[](https://cla-hub.io/agreements/my-org/my-repo)
Options: ?style=flat-square, ?label=License, ?color=4c1.
@sentry/nextjs for error tracking (optional)Full walkthrough: docs/getting-started.md
You need Node.js 20+ and npm. You also need a GitHub App and two OAuth Apps (owner + contributor). See the Getting Started guide for step-by-step setup.
git clone https://github.com/DamageLabs/clahub.git
cd clahub
npm ci
cp .env.local.example .env.local # fill in your values
npm run db:push
npm run dev
Open http://localhost:3000.
Required env vars: DATABASE_URL, NEXTAUTH_SECRET, GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, GITHUB_WEBHOOK_SECRET, GITHUB_OWNER_CLIENT_ID/SECRET, GITHUB_CONTRIBUTOR_CLIENT_ID/SECRET, APP_URL.
Optional: SENTRY_DSN, RESEND_API_KEY, LOG_LEVEL, branding vars (APP_NAME, APP_LOGO_URL, APP_PRIMARY_COLOR).
See docs/configuration.md for the full environment variable reference.
GitHub can't reach localhost, so use ngrok (ngrok http 3000) and update APP_URL in .env.local.
| Command | Description |
|---|---|
npm run dev | Start dev server |
npm run build | Production build |
npm run start | Start production server |
npm run lint | Run ESLint |
npm run format | Format with Prettier |
npm test | Run unit tests (Vitest) |
npm run test:watch | Run tests in watch mode |
npm run test:e2e | Run E2E tests (Playwright) |
npm run db:push | Push Prisma schema to database |
npm run db:seed | Seed database with sample data |
npm run db:studio | Open Prisma Studio |
The fastest way to self-host CLAHub is with Docker:
cp .env.docker.example .env # fill in your values
docker compose up -d
Also supports Vercel, Railway, Fly.io, and bare-metal Node.js (PM2 / systemd).
See Deployment Guide for all options and Upgrading Guide for version updates.
Unit tests live in tests/unit/ and cover pure functions, Zod schema validation, and business logic with mocked dependencies.
npm test # run all unit tests once
npm run test:watch # run in watch mode during development
Tests are organized by layer:
| Directory | What's tested |
|---|---|
tests/unit/schemas/ | Zod schemas — agreement, signing, exclusion |
tests/unit/lib/ | Utility modules — api-error, audit, logger, result, sentry, badge, rate-limit, export-csv, export-pdf |
tests/unit/cla-check.test.ts | extractPushAuthors() pure function |
tests/unit/cla-check-integration.test.ts | checkClaForCommitAuthors, createCheckRun, extractPRAuthors with mocked Prisma/Octokit |
tests/unit/lib/cla-check-corporate.test.ts | Corporate CLA domain-based coverage with mocked Prisma |
tests/unit/lib/api-sign-corporate.test.ts | Corporate signing schema validation |
tests/unit/actions/signing.test.ts | signAgreement server action with mocked auth, Prisma, and cla-check |
E2E tests live in tests/e2e/ and test the signing flow, dashboard, and webhook endpoint through a real browser.
npm run test:e2e # run all E2E tests
Local development: Playwright reuses your running dev server on port 3000. Make sure npm run dev is running and the database is seeded (npm run db:seed).
CI: Playwright starts its own dev server using .env.test and runs prisma db push --force-reset + prisma db seed against a disposable test.db via the global setup script.
Authentication in tests: E2E tests inject real JWT session cookies using @auth/core/jwt encode() with the same secret as the running server. See tests/e2e/auth-helpers.ts for the authenticateAs() helper.
First time setup:
npx playwright install chromium # download browser binary (one-time)
src/
app/
(marketing)/ Landing, privacy, terms, why-cla pages
agreements/ Dashboard, create, edit, public signing page
api/auth/ NextAuth endpoints
api/badge/ SVG badge endpoints (public, no auth)
api/health/ Health check endpoint
api/v1/ REST API (agreements, signatures, check, export)
api/webhooks/ GitHub App webhook handler
auth/signin/ Sign-in page
error.tsx Error boundary with Sentry reporting
global-error.tsx Global error boundary (outside root layout)
not-found.tsx Custom 404 page
components/
ui/ shadcn/ui primitives
agreements/ Agreement form, signing form, exclusion manager, signature manager, notification toggle, audit log viewer, recheck button, CONTRIBUTING.md section
lib/
actions/ Server actions (agreement, exclusion, signing, signature)
schemas/ Zod validation schemas
api-auth.ts API key + session authentication
api-error.ts Structured API error responses with error codes
api-rate-limit.ts Tiered rate limiting (API key / session / anon)
audit.ts Shared audit logging utility
auth.ts NextAuth configuration (dual providers)
access.ts Role-based access control (owner, org_admin)
badge.ts SVG badge rendering (shields.io-compatible)
branding.ts Custom instance branding from env vars
contributing.ts CONTRIBUTING.md snippet generation
cla-check.ts Core CLA verification + exclusion + corporate coverage
email.ts Resend email wrapper + notification templates
export-csv.ts CSV export with papaparse
export-pdf.ts PDF export with @react-pdf/renderer
github.ts GitHub App / Octokit setup + webhook handlers
logger.ts Structured JSON logger with level filtering
prisma.ts Prisma client singleton
rate-limit.ts In-memory sliding window rate limiter
sentry.ts Sensitive data scrubbing utility
templates.ts CLA templates (Apache ICLA, DCO)
instrumentation.ts Server-side Sentry init (conditional on SENTRY_DSN)
instrumentation-client.ts Client-side Sentry init
prisma/
schema.prisma Database schema (9 models)
seed.ts Sample data
tests/
unit/ Vitest unit tests (schemas, lib, cla-check, actions)
e2e/ Playwright E2E tests (signing flow, dashboard, webhook)
setup.ts Vitest setup (jest-dom matchers)
Dockerfile Multi-stage production image (node:20-alpine)
docker-compose.yml Self-hosted deployment with SQLite volume
docker-entrypoint.sh Auto-migration entrypoint script
docs/ Deployment and operations documentation
| Model | Purpose |
|---|---|
User | GitHub users (owners and contributors) |
Agreement | CLA definitions linked to a GitHub repo or org |
AgreementVersion | Versioned CLA text with changelogs |
AgreementField | Custom form fields on a CLA |
Signature | Individual or corporate signatures (with optional company name/domain/title) |
FieldEntry | Field values submitted with a signature |
Exclusion | Bot/user/team exclusions per agreement |
ApiKey | API keys for REST API authentication |
AuditLog | Complete change history |
Contributor License Agreements prove intellectual property provenance of contributions to an open-source project. They generally say:
- The code I'm contributing is mine, and I have the right to license it.
- I'm granting you a license to distribute said code under the terms of this agreement.
From Contributor License Agreements by Jacob Kaplan-Moss
More background:
None of the CLAHub documentation, functionality, or other communication constitutes legal advice. Consult your lawyer about contributor agreements for your project.
MIT — Copyright (c) 2013-2026 Jason Morrison, Tony Guntharp
TypeScript
98.5%
Easy contributor license agreements for your GitHub projects.
See the codeCLAHub provides a low-friction way to have a Contributor License Agreement for your open source project on GitHub. Contributors digitally sign your CLA by signing in with GitHub, and pull requests are automatically marked with a status check based on whether all commit authors have signed.
Running at cla-hub.io
For project owners:
clahub_-prefixed Bearer tokens)/api/badge/{owner}/{repo})X-RateLimit-* headersdocker compose up -d with auto-migration, health checks, and SQLite volume persistenceAPP_NAME, APP_LOGO_URL, APP_PRIMARY_COLORGET /api/health for uptime monitoring and container health checksFor contributors:
CLAHub provides a REST API at /api/v1/ for programmatic access. Authenticate with a Bearer token (generate one under Settings > API Keys).
# List signatures for a repo agreement
curl -H "Authorization: Bearer clahub_xxxx" \
https://cla-hub.io/api/v1/agreements/my-org/my-repo/signatures
# Check if a user has signed
curl https://cla-hub.io/api/v1/agreements/my-org/my-repo/check/username
# Export signatures as CSV
curl -H "Authorization: Bearer clahub_xxxx" \
https://cla-hub.io/api/v1/agreements/my-org/my-repo/export/csv
Full API documentation is available in the OpenAPI spec.
Add a CLA status badge to your README:
[](https://cla-hub.io/agreements/my-org/my-repo)
Options: ?style=flat-square, ?label=License, ?color=4c1.
@sentry/nextjs for error tracking (optional)Full walkthrough: docs/getting-started.md
You need Node.js 20+ and npm. You also need a GitHub App and two OAuth Apps (owner + contributor). See the Getting Started guide for step-by-step setup.
git clone https://github.com/DamageLabs/clahub.git
cd clahub
npm ci
cp .env.local.example .env.local # fill in your values
npm run db:push
npm run dev
Open http://localhost:3000.
Required env vars: DATABASE_URL, NEXTAUTH_SECRET, GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, GITHUB_WEBHOOK_SECRET, GITHUB_OWNER_CLIENT_ID/SECRET, GITHUB_CONTRIBUTOR_CLIENT_ID/SECRET, APP_URL.
Optional: SENTRY_DSN, RESEND_API_KEY, LOG_LEVEL, branding vars (APP_NAME, APP_LOGO_URL, APP_PRIMARY_COLOR).
See docs/configuration.md for the full environment variable reference.
GitHub can't reach localhost, so use ngrok (ngrok http 3000) and update APP_URL in .env.local.
| Command | Description |
|---|---|
npm run dev | Start dev server |
npm run build | Production build |
npm run start | Start production server |
npm run lint | Run ESLint |
npm run format | Format with Prettier |
npm test | Run unit tests (Vitest) |
npm run test:watch | Run tests in watch mode |
npm run test:e2e | Run E2E tests (Playwright) |
npm run db:push | Push Prisma schema to database |
npm run db:seed | Seed database with sample data |
npm run db:studio | Open Prisma Studio |
The fastest way to self-host CLAHub is with Docker:
cp .env.docker.example .env # fill in your values
docker compose up -d
Also supports Vercel, Railway, Fly.io, and bare-metal Node.js (PM2 / systemd).
See Deployment Guide for all options and Upgrading Guide for version updates.
Unit tests live in tests/unit/ and cover pure functions, Zod schema validation, and business logic with mocked dependencies.
npm test # run all unit tests once
npm run test:watch # run in watch mode during development
Tests are organized by layer:
| Directory | What's tested |
|---|---|
tests/unit/schemas/ | Zod schemas — agreement, signing, exclusion |
tests/unit/lib/ | Utility modules — api-error, audit, logger, result, sentry, badge, rate-limit, export-csv, export-pdf |
tests/unit/cla-check.test.ts | extractPushAuthors() pure function |
tests/unit/cla-check-integration.test.ts | checkClaForCommitAuthors, createCheckRun, extractPRAuthors with mocked Prisma/Octokit |
tests/unit/lib/cla-check-corporate.test.ts | Corporate CLA domain-based coverage with mocked Prisma |
tests/unit/lib/api-sign-corporate.test.ts | Corporate signing schema validation |
tests/unit/actions/signing.test.ts | signAgreement server action with mocked auth, Prisma, and cla-check |
E2E tests live in tests/e2e/ and test the signing flow, dashboard, and webhook endpoint through a real browser.
npm run test:e2e # run all E2E tests
Local development: Playwright reuses your running dev server on port 3000. Make sure npm run dev is running and the database is seeded (npm run db:seed).
CI: Playwright starts its own dev server using .env.test and runs prisma db push --force-reset + prisma db seed against a disposable test.db via the global setup script.
Authentication in tests: E2E tests inject real JWT session cookies using @auth/core/jwt encode() with the same secret as the running server. See tests/e2e/auth-helpers.ts for the authenticateAs() helper.
First time setup:
npx playwright install chromium # download browser binary (one-time)
src/
app/
(marketing)/ Landing, privacy, terms, why-cla pages
agreements/ Dashboard, create, edit, public signing page
api/auth/ NextAuth endpoints
api/badge/ SVG badge endpoints (public, no auth)
api/health/ Health check endpoint
api/v1/ REST API (agreements, signatures, check, export)
api/webhooks/ GitHub App webhook handler
auth/signin/ Sign-in page
error.tsx Error boundary with Sentry reporting
global-error.tsx Global error boundary (outside root layout)
not-found.tsx Custom 404 page
components/
ui/ shadcn/ui primitives
agreements/ Agreement form, signing form, exclusion manager, signature manager, notification toggle, audit log viewer, recheck button, CONTRIBUTING.md section
lib/
actions/ Server actions (agreement, exclusion, signing, signature)
schemas/ Zod validation schemas
api-auth.ts API key + session authentication
api-error.ts Structured API error responses with error codes
api-rate-limit.ts Tiered rate limiting (API key / session / anon)
audit.ts Shared audit logging utility
auth.ts NextAuth configuration (dual providers)
access.ts Role-based access control (owner, org_admin)
badge.ts SVG badge rendering (shields.io-compatible)
branding.ts Custom instance branding from env vars
contributing.ts CONTRIBUTING.md snippet generation
cla-check.ts Core CLA verification + exclusion + corporate coverage
email.ts Resend email wrapper + notification templates
export-csv.ts CSV export with papaparse
export-pdf.ts PDF export with @react-pdf/renderer
github.ts GitHub App / Octokit setup + webhook handlers
logger.ts Structured JSON logger with level filtering
prisma.ts Prisma client singleton
rate-limit.ts In-memory sliding window rate limiter
sentry.ts Sensitive data scrubbing utility
templates.ts CLA templates (Apache ICLA, DCO)
instrumentation.ts Server-side Sentry init (conditional on SENTRY_DSN)
instrumentation-client.ts Client-side Sentry init
prisma/
schema.prisma Database schema (9 models)
seed.ts Sample data
tests/
unit/ Vitest unit tests (schemas, lib, cla-check, actions)
e2e/ Playwright E2E tests (signing flow, dashboard, webhook)
setup.ts Vitest setup (jest-dom matchers)
Dockerfile Multi-stage production image (node:20-alpine)
docker-compose.yml Self-hosted deployment with SQLite volume
docker-entrypoint.sh Auto-migration entrypoint script
docs/ Deployment and operations documentation
| Model | Purpose |
|---|---|
User | GitHub users (owners and contributors) |
Agreement | CLA definitions linked to a GitHub repo or org |
AgreementVersion | Versioned CLA text with changelogs |
AgreementField | Custom form fields on a CLA |
Signature | Individual or corporate signatures (with optional company name/domain/title) |
FieldEntry | Field values submitted with a signature |
Exclusion | Bot/user/team exclusions per agreement |
ApiKey | API keys for REST API authentication |
AuditLog | Complete change history |
Contributor License Agreements prove intellectual property provenance of contributions to an open-source project. They generally say:
- The code I'm contributing is mine, and I have the right to license it.
- I'm granting you a license to distribute said code under the terms of this agreement.
From Contributor License Agreements by Jacob Kaplan-Moss
More background:
None of the CLAHub documentation, functionality, or other communication constitutes legal advice. Consult your lawyer about contributor agreements for your project.
MIT — Copyright (c) 2013-2026 Jason Morrison, Tony Guntharp
TypeScript
98.5%