Open-source tracking plans that behave like code.
What it is | Screenshots | Quick start | Backend install | API
Trackboard is a self-hostable workspace for analytics contracts.
Use it to import a tracking plan, edit events and properties, review schema changes in branches, publish immutable versions, validate incoming events with API keys, and triage invalid payloads in a DLQ.
It is meant for teams that are tired of tracking plans living in spreadsheets, Notion pages, or half-forgotten SDK types.
The serious v1 surface is intentionally small:
- Import a plan from structured JSON.
- Edit main workspace events, local properties, and shared global properties.
- Create branches and merge requests with diff summaries.
- Publish main workspace versions with compatibility reports.
- Restore a published version back into draft, then publish again when ready.
- Validate single or batch event payloads through API keys.
- Inspect invalid payloads grouped by event, version, first seen, last seen, and count.
- Generate TypeScript types and JSON Schema from the latest plan.
- Configure an optional OpenAI-compatible provider for schema assist.
Not in the v1 release bar: warehouse codegen, webhook integrations, GitHub PR automation, and live streaming validation. Some old code may still exist in the repository, but the primary product path is the contract workflow above.
| Source-of-truth editor | Merge review |
|---|---|
![]() |
![]() |
| Published versions | DLQ triage |
|---|---|
![]() |
![]() |
| Workspace settings |
|---|
![]() |
The fastest path is Docker Compose.
git clone https://github.com/Astoriel/trackboard.git
cd trackboard
cp .env.example .env
docker compose up --buildOpen:
- Web app:
http://localhost:3000 - API docs:
http://localhost:8000/docs - API health:
http://localhost:8000/api/v1/health/ready
For local development, replace the placeholder secrets in .env before doing anything public. The default values are only for a local throwaway environment.
- Register a user and organization.
- Create a tracking plan.
- Import a JSON plan or add events manually.
- Publish v1 from the main workspace.
- Create an API key in Settings.
- Validate production payloads with
/api/v1/validateor/api/v1/validate/batch. - Open DLQ when payloads fail validation.
- Use a branch when the schema change needs review before touching main.
Example import shape:
{
"global_properties": [
{
"name": "user_id",
"type": "string",
"required": true,
"description": "Stable application user id"
}
],
"events": [
{
"event_name": "signup_completed",
"description": "A user completes signup",
"category": "activation",
"global_properties": ["user_id"],
"properties": [
{
"name": "signup_method",
"type": "string",
"required": true,
"constraints": { "enum": ["email", "google", "github"] }
}
]
}
]
}Use Python 3.12 and PostgreSQL. SQLite is not the serious v1 path.
cd apps/api
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -U pip
pip install -e ".[dev]"
$env:DATABASE_URL="postgresql+asyncpg://trackboard:trackboard_dev@localhost:5432/trackboard"
$env:REDIS_URL="memory://"
$env:JWT_SECRET="replace-with-a-long-random-secret"
$env:SECRET_ENCRYPTION_KEY="replace-with-a-different-long-random-secret"
$env:CORS_ORIGINS="http://localhost:3000"
alembic upgrade head
uvicorn app.main:app --reload --port 8000Useful backend checks:
python -m pytest
python -m bandit -r app -q
python -m pip_audit . -f jsoncd apps/web
npm install
$env:NEXT_PUBLIC_API_URL="http://localhost:8000"
npm run devUseful frontend checks:
npx tsc --noEmit
npm run lint
npm run build
node scripts/stress-frontend-coverage.mjsThe stress script drives the UI through register, plan creation, import, branch, merge request, publish, validation, DLQ, settings, AI provider settings, and code generation. It also compares visited frontend calls against the backend OpenAPI routes.
Base path: /api/v1
Important routes:
POST /auth/register,POST /auth/login,POST /auth/refreshGET /plans,POST /plans,GET /plans/{plan_id}POST /plans/{plan_id}/importPOST /plans/{plan_id}/branchGET /plans/{plan_id}/merge-requestsPOST /merge-requests/{mr_id}/mergePOST /plans/{plan_id}/publishGET /plans/{plan_id}/versionsPOST /versions/{version_id}/restorePOST /plans/{plan_id}/keysPOST /keys/{key_id}/rotateDELETE /keys/{key_id}POST /validatePOST /validate/batchGET /plans/{plan_id}/dlqGET /health/live,GET /health/ready,GET /health/version
Validation example:
curl -X POST http://localhost:8000/api/v1/validate \
-H "Content-Type: application/json" \
-H "X-API-Key: tb_live_xxx" \
-d '{
"event": "signup_completed",
"mode": "block",
"source": "web",
"properties": {
"user_id": "usr_123",
"signup_method": "google"
}
}'Every validation response follows the same shape:
{
"valid": true,
"mode": "block",
"version_id": "00000000-0000-0000-0000-000000000000",
"event": "signup_completed",
"violations": [],
"validated_at": "2026-04-14T12:00:00Z"
}Minimum backend variables:
| Variable | Purpose |
|---|---|
DATABASE_URL |
PostgreSQL connection string. Use postgresql+asyncpg://... locally. Plain postgresql://... is normalized by the app. |
JWT_SECRET |
Signs access and refresh tokens. Use a long random value. |
SECRET_ENCRYPTION_KEY |
Encrypts stored provider secrets, such as optional AI keys. Use a different long random value. |
CORS_ORIGINS |
Comma-separated allowed frontend origins. |
REDIS_URL |
Redis URL. memory:// is acceptable for local/dev only. |
ENVIRONMENT |
Use production in real deployments. Production rejects debug mode and weak secrets. |
ALLOW_PRIVATE_AI_ENDPOINTS |
Keep false unless you intentionally allow localhost/private AI endpoints in a trusted dev setup. |
Backend on Render:
- Root directory:
apps/api - Build command:
pip install -e . - Start command:
alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port $PORT - Health check:
/api/v1/health/ready - Required env:
DATABASE_URL,JWT_SECRET,SECRET_ENCRYPTION_KEY,ENVIRONMENT=production,DEBUG=false,CORS_ORIGINS=<your frontend url>
Frontend on Vercel:
- Root directory:
apps/web - Env:
NEXT_PUBLIC_API_URL=<your backend url> - Build command:
npm run build
For Neon, use the normal database host for migrations and the pooler host for high-concurrency app traffic if you need it. Do not commit database URLs.
trackboard/
apps/
api/ FastAPI, SQLAlchemy 2, Alembic, Pydantic v2
web/ Next.js App Router, TypeScript, Tailwind
docs/
assets/
serious-v1-implementation-plan.md
docker-compose.yml
render.yaml
Trackboard is not magic security dust. Treat it like an internal schema control plane:
- Keep JWT and encryption secrets out of git.
- Rotate API keys when they are exposed.
- Run Alembic migrations before serving traffic.
- Pin CORS to your real frontend domains.
- Keep private AI endpoints disabled in production.
- Put the API behind HTTPS.
- Back up Postgres.
MIT. See LICENSE if present in your checkout.




