Content
# CentralCTRL
> # This is a PREVIEW RELEASE — work in progress.
>
> **Please read this before you `git clone`:**
>
> CentralCTRL is **under active development**. This is an **early preview cut**,
> not a stable 1.0. APIs, database schemas, and MCP tool surfaces are still
> moving between cuts. Migrations may not be reversible.
>
> **What it's great for right now:**
> Solo work with multiple AI agents — running Claude, Codex, Cursor, or any
> mix against the same workspace, with the proof-of-work loop keeping the
> audit trail honest. If that's your use case, you'll get real value out of
> this preview today.
>
> **What still needs work:**
> Refinement, polish, and broader testing. Edges are rough in places. The
> admin UI is bare in spots. Multi-human team flows work but aren't yet
> battle-tested at scale. **Don't make CentralCTRL your only system of
> record yet** — run it alongside something you trust, or against
> non-critical work, until 1.0.
>
> **Updates are landing continuously.** I'm working hard toward an actual
> stable release. See **[STATUS.md](STATUS.md)** for what works / what's
> experimental / what's planned today, and **[ROADMAP.md](ROADMAP.md)** for
> where it's headed.
>
> **A note on licensing:** while CentralCTRL is in preview, it is
> **source-available, not open source** — free for personal use and teams
> of 10 or fewer (see [LICENSE](LICENSE) and the [License](#license)
> section below). **The intention is to move toward a fully open-source
> license** once the project stabilizes; the source-available terms during
> preview are about giving the project room to grow sustainably, not about
> staying closed long-term.
>
> Feedback, bug reports, and feature requests are very welcome — open an
> issue at
> [github.com/Roenel/CentralCTRL/issues](https://github.com/Roenel/CentralCTRL/issues).
> If you want to support the work, see [Sponsoring](#sponsoring).
---
CentralCTRL is an **MCP-first project, cycle, and issue tracking workspace
built for multi-agent teams.** Both the web UI and the MCP server share the
same data layer — agents and humans work against one workspace, one truth.
A **[Teck Media Group LLC](https://centralctrl.com)** product ·
**[Roenel Teck](https://github.com/Roenel)** contributing member ·
Website: **[centralctrl.com](https://centralctrl.com)**
**New here? Start with the [Getting Started guide](docs/getting-started.md).**
**Lost? Check the [docs index](docs/README.md) or the [FAQ](docs/faq.md).**
## See what your agents are doing — and trust the code they ship
CentralCTRL is built around two ideas that most issue trackers don't have:
**you can always see what your agents are doing right now**, and **agents
can't silently close work that didn't actually happen.**
### Live agent visibility
When you've got Claude, Codex, Cursor, and a couple of background bots all
working in the same repo, "what's going on?" is the hardest question. The
answer is one MCP call away:
- **`what_is_everyone_doing`** — one round-trip, full picture. Active work
sessions across the workspace, recent activity (assignments, comments,
status transitions, completion receipts), pending handoffs left for you,
recently-touched paths from sessions that just wrapped, and any
overlapping sessions where two agents have claimed the same files.
- **Activity feed (`/inbox`)** — a chronological audit trail of every
meaningful state change in the workspace, visible in the web UI and via
`list_activity` from MCP.
- **Live push, no polling** — the `/api/agent-events` SSE stream pushes
events the moment they land. Subscribe by event pattern (`issue:*`,
`notification:created`) and scope (`mine`, `workspace`, a specific issue)
and react in real time without paying a poll-every-second token tax.
- **`peer_working_diff`** — when you want to know exactly what a peer
agent is changing right now, this returns their authoritative
server-side `git diff` for their active session. Decides whether to step
back, press forward, or coordinate.
### Verified code, not vibes
Marking an issue **Done** isn't just a status flip — it's an audited claim
that specific code was changed. CentralCTRL enforces this with a
proof-of-work gate that runs on every Done transition:
1. A **Work Session** must have been started against the issue with
`start_work_session` (which claims the file paths the agent intends to
touch).
2. The session must have been finished with `finish_work_session` — this
captures the **actual `git diff`** between the session's start commit
and HEAD, into `actualChangedPaths`.
3. A **Completion Receipt** must be on file with `verificationStatus:
PASSED | PARTIAL` and a `workSessionId` linking it back to that
session.
4. **The server then verifies the agent's claimed paths against the diff
it captured.** Mismatches surface as `FAILED` audit-override receipts.
The receipt also renders three artifacts you don't have to write by hand:
the PR description, the commit message, and the issue completion comment —
so the same prose isn't drafted four different ways.
The net effect: when an agent says "I closed WEB-42," you can see exactly
which files it touched, which session captured the diff, which PR carried
the work, and which receipt anchors the audit trail. No silent
status-flipping. No "I did the thing" without evidence.
For the full doctrine — including multi-agent path-overlap detection, the
schema-migration lock, and the pause/press/negotiate/escalate conflict
protocol — see **[architecture.md](docs/architecture.md)** and
**[AGENTS.md](AGENTS.md)**.
## Stack
- **Next.js 16** (App Router, Turbopack) · React 19 · Tailwind CSS 4
- **Prisma 7** + PostgreSQL
- **MCP server** (`npm run mcp`) — agents use it to manage issues, work sessions, and handoffs
## Architecture
```
Browser / UI Agent clients (Claude, Codex, …)
│ │
│ HTTP │ stdio (JSON-RPC)
▼ ▼
┌─────────────────────────────────────────────┐
│ Next.js app (port 3000) │
│ App Router pages · /api/* route handlers │
│ src/lib/workspace.ts │
└───────────────────┬─────────────────────────┘
│ Prisma 7
▼
PostgreSQL database
```
Both the web UI and the MCP server call the same `workspace.ts` functions — there is no separate API layer. See [docs/architecture.md](docs/architecture.md) for the full design rationale.
## Quick start
**Prerequisites:** Node 22 LTS, Docker 24+ with Compose v2 (`docker compose version`).
```bash
# 1. Configure environment (do this first — db:migrate needs DATABASE_URL)
cp .env.example .env
# 2. Start the dev database (Postgres on port 5433)
docker compose up -d
# 3. Install dependencies
npm install
# 4. Apply schema
npm run db:migrate
# 5. Start the web app
npm run dev # http://localhost:3000
```
On first start CentralCtrl supports two paths to bootstrap the first admin:
- **Interactive setup (default)** — open `http://localhost:3000/setup` and
fill out the form. After setup completes, `/setup` returns 404.
- **Headless setup (CI / IaC)** — set **all three** of `CCTRL_ADMIN_EMAIL`,
`CCTRL_ADMIN_PASSWORD`, and `CCTRL_SKIP_SETUP_WIZARD=1` before first
boot. The bootstrap creates the admin, stamps `setupCompletedAt`, and
logs a reminder to rotate the password after first login.
Setting only `CCTRL_ADMIN_PASSWORD` (without `CCTRL_SKIP_SETUP_WIZARD=1`)
is **deprecated**: the bootstrap logs a warning and falls through to the
`/setup` wizard so existing deployments aren't surprised.
> **Port note:** the dev compose maps Postgres to `5433` (not the default `5432`) to avoid clashing with any local Postgres installation. The default `DATABASE_URL` in `.env.example` already points to `5433`.
## Architecture docs
See **[docs/architecture.md](docs/architecture.md)** for a full explanation of the key design choices:
- Why MCP and the web UI share the same `workspace.ts` functions
- The proof-of-work gate that guards the Done transition
- Per-user-agent identity and the advisory file-lease model
- Data model, auth surfaces, and ADR pointers
## Connecting an agent
See **[docs/agents.md](docs/agents.md)** for:
- Claude Code MCP config example (`~/.claude/settings.json` or `.claude/settings.json`)
- Required env vars (`DATABASE_URL`, `CCTRL_ACTOR_HANDLE`, …)
- How multiple agents share the same MCP server while using distinct actor handles
- The agent protocol: `what_am_i_missing` → `get_my_next_task` → `start_work_session` → edit → `finish_work_session`
### Lost your context after a long chat?
When your AI agent's conversation gets compacted (Claude Code, Cursor, etc. dropping older turns to make room for new ones), the chat scrollback is gone but the work isn't. Three MCP calls — `whoami`, `assigned_work`, `get_issue_context_pack` — bring a fresh conversation back up to speed.
## MCP server
```bash
npm run mcp # starts the CentralCtrl MCP server on stdio
```
All MCP tools are defined in [`src/mcp/server.ts`](src/mcp/server.ts).
## Importing from Linear
Two supported paths — pick whichever fits your situation.
### Option A: Agent-driven via MCP (no API key, no code changes)
If your agent client has **both** the Linear MCP (e.g. `mcp.linear.app`)
**and** the CentralCTRL MCP connected, you can migrate from Linear without
touching `.env` or writing import scripts. The agent reads from Linear,
normalizes fields, and writes through CentralCTRL's import tools:
```
linear.list_projects → pick the project to migrate
linear.list_issues → fetch all issues (paginated)
cctrl.preview_linear_project_import → review the mapping
cctrl.import_linear_project → execute the import
```
Statuses and labels are created on demand; re-runs are idempotent (records
match on Linear external IDs). Full step-by-step:
**[docs/imports/linear-mcp-recipe.md](docs/imports/linear-mcp-recipe.md)**.
### Option B: Web UI picker (set `LINEAR_API_KEY`)
Set `LINEAR_API_KEY` in `.env` to enable the **Projects page Linear picker**
— a UI flow that lists your Linear projects and runs the same import behind
the scenes. Without the key, CentralCTRL keeps a developer JSON fallback for
testing the import API.
## Database
```bash
npm run db:generate # regenerate Prisma client after schema changes
npm run db:migrate # apply pending migrations
npm run db:deploy # production-safe migration deploy + client generation
```
## Production deployment
CentralCtrl ships a multistage Docker image and two compose recipes depending on
whether you have a public DNS name + TLS or are running on a LAN.
### LAN / self-hosted (no public DNS)
Use `docker/compose.prod.yml` with the LAN-friendly `docker/Caddyfile`
(`auto_https disable_redirects`). Plain HTTP on port 80 works out of the box;
`cctrl.local` resolves via mDNS on macOS and most Linux distros.
```bash
cp .env.example .env # set POSTGRES_PASSWORD at minimum
# Bring up postgres + web + caddy
docker compose -f docker/compose.prod.yml up -d
# First-run only: apply the database schema
docker compose -f docker/compose.prod.yml run --rm cctrl-web npm run db:deploy
```
Verify:
```bash
curl -s http://localhost/api/health | jq .
# {"status":"ok","uptimeSeconds":12,"version":"0.1.0",...}
# Deeper readiness probe (DB + migrations):
curl -s http://localhost/api/health/ready | jq .
```
> **Windows mDNS note**: `cctrl.local` requires Bonjour Print Services or a
> manual `hosts` entry (`<server-ip> cctrl.local`). macOS and Linux resolve
> it automatically.
See **[docs/deployment-lan.md](docs/deployment-lan.md)** for the full LAN
setup walkthrough — mDNS troubleshooting, first-login admin password
retrieval, logs, backups, and updates.
### Public / cloud (real DNS + HTTPS)
Use `docker-compose.prod.yml` at the repo root. Set `CCTRL_HOSTNAME` in
`.env` to your public domain; Caddy will auto-provision a Let's Encrypt cert.
```bash
cp .env.example .env
# Set POSTGRES_PASSWORD, CCTRL_HOSTNAME, CCTRL_PUBLIC_URL, etc.
docker compose -f docker-compose.prod.yml up -d
```
The `migrate` service runs `npm run db:deploy` before `web` starts. For agent
MCP access run the MCP server on-demand:
```bash
CCTRL_ACTOR_HANDLE=claude docker compose -f docker-compose.prod.yml run --rm mcp
```
### Deploying with Podman
`docker-compose.prod.yml` is portable between Docker Compose v2 and `podman-compose`. Two host-side notes for Podman operators:
- **SELinux-enforcing hosts (Fedora, RHEL, CentOS Stream)** — bind-mounted host volumes need a `:Z` (per-container relabel) or `:z` (shared relabel) suffix or the container can't read them. The only bind mount in `docker-compose.prod.yml` is the Caddyfile (`./Caddyfile:/etc/caddy/Caddyfile:ro`); change it to `./Caddyfile:/etc/caddy/Caddyfile:ro,Z` on SELinux hosts, or omit `--profile caddy` if you're terminating TLS elsewhere.
- **External Postgres** — set `DATABASE_URL` directly in `.env`. Compose passes it through and it overrides the built-from-components default. (Earlier versions wired this via a nested `${CCTRL_DATABASE_URL:-...}` substitution; that pattern tripped podman-compose's parser and has been flattened.)
Bring the stack up as you would with Docker:
```bash
podman-compose -f docker-compose.prod.yml up -d
```
### Verifying your deployment
CentralCtrl exposes three observability endpoints:
- **`/api/health`** — pure liveness, no DB call (~10 ms). Wire your container
healthcheck and uptime monitor here.
- **`/api/health/ready`** — readiness: confirms Postgres reachable and migrations applied.
Returns 503 with a per-check failure body when something's wrong.
- **`/api/health/backup`** — backup-freshness check (TKT-415 + TKT-416). Returns 503 if
the newest `.sql.gz` in `BACKUP_LOCAL_DIR` is older than `BACKUP_FRESHNESS_HOURS` (default 25h).
All three are unauthenticated and rate-limit exempt — safe targets for monitoring tools.
For the full step-by-step smoke-test checklist see **[docs/deployment.md](docs/deployment.md)**.
## Documentation
The full documentation index lives at **[docs/README.md](docs/README.md)**.
Highlights:
- **[Getting Started](docs/getting-started.md)** — install, first admin,
first agent, first issue.
- **[FAQ](docs/faq.md)** — common questions about MCP, identity,
multi-agent coordination, and licensing.
- **[Troubleshooting](docs/troubleshooting.md)** — dev-server errors, Prisma
client issues, MCP boot crashes, conflict-marker recovery.
- **[Glossary](docs/glossary.md)** — what is a Principal, Work Session,
Completion Receipt, etc.
- **[Architecture](docs/architecture.md)** — design rationale, ADR index.
- **[Agent integration](docs/agents.md)** — MCP config, identity, protocol.
- **[Deployment](docs/deployment.md)** + **[LAN deployment](docs/deployment-lan.md)** —
Docker compose, Caddy, backups.
- **[Threat model](docs/security/threat-model.md)** — security posture.
- **[AGENTS.md](AGENTS.md)** — exhaustive operator/agent reference. Long;
use it as a search target, not bedside reading.
## Status & Roadmap
- **[STATUS.md](STATUS.md)** — what works *today* in this preview cut:
stable / experimental / planned / known-broken.
- **[ROADMAP.md](ROADMAP.md)** — where CentralCTRL is going: near-term
polish, the host/client split, the Electron desktop app, and the long
view of where we'd love to end up.
## Sponsoring
CentralCTRL is a **[Teck Media Group LLC](https://centralctrl.com)**
product, with **[Roenel Teck](https://github.com/Roenel)** as the
contributing member driving development. It's released as a
source-available project today, with the intention of moving to OSS once
it stabilizes (see [License](#license)).
If it's useful to you and you'd like to support continued development, a
tip goes a long way:
- **Cash App:** [**$roenelteck**](https://cash.app/$roenelteck)
- **Website:** [centralctrl.com](https://centralctrl.com)
Every contribution funds time spent on the next item in
**[ROADMAP.md](ROADMAP.md)** — and directly shortens the timeline to a 1.0
+ open-source release. Even a coffee's worth helps. Thank you.
## Contributing
This is a preview release; contribution flow is still being defined. For now:
- **Bug reports / questions** — open an issue on
[github.com/Roenel/CentralCTRL/issues](https://github.com/Roenel/CentralCTRL/issues).
- **Pull requests** — small, focused PRs welcome; large refactors please open
an issue to discuss first so we don't conflict with in-flight work.
See **[CONTRIBUTING.md](CONTRIBUTING.md)** for the dev-loop conventions
(commit style, conflict-marker guard, destructive-Prisma-command ban).
## License
> **During preview, CentralCTRL is source-available — not open source.**
> The intention is to move to a fully open-source license once the project
> stabilizes. The current terms exist to give the project room to grow
> sustainably during the early phase, not to keep it closed long-term.
CentralCTRL is licensed under the **CentralCTRL Source-Available License**
(see [LICENSE](LICENSE)). Free for:
- **Personal Use** — individuals using it for non-revenue purposes.
- **Small Team Use** — Organizations with **10 or fewer total individuals**
(employees, contractors, founders, owners — everyone working for the org).
Use by larger organizations, or any hosted/managed-service offering, requires
a commercial license — open an issue or contact the copyright holder to
arrange one.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
servers
Model Context Protocol Servers
servers
Model Context Protocol Servers
Time
A Model Context Protocol server for time and timezone conversions.