Crosscode architecture
human / coding agent
|
| CLI (`crosscode …`) MCP tools (stdio)
v v
per-worktree daemon --- SQLite events + outbox
|
| authenticated HTTP sync
v
coordination service --- Supabase-hosted PostgreSQL operations + audit log
|
v
other daemons receive reviewable proposals
Crosscode is CLI-first. Every coordination operation — status, tasks, claims,
proposal review, accept/reject, checkpoints, validation, publish — happens
against the local daemon through the CLI or MCP. The website
(apps/docs-site) is not part of this topology: it is a landing page, the auth
pages (sign-up, sign-in, password reset, and the crosscode login callback at
/auth/cli.html), and the documentation generated from the root docs/*.md.
Nothing else lives behind auth, and no browser page reads or writes workspace
state.
Daemon (apps/daemon)
One daemon runs per Git checkout/worktree. It watches filesystem and Git activity,
captures settled edits as immutable transactions (stable before/after content
hashes), and appends them to a local append-only SQLite event log alongside
projections, an offline outbox, and the download cursor. It creates safety
checkpoints under refs/crosscode/checkpoints/<replica-id>/... before any
materialization and never moves HEAD or changes the real index. It binds only to
loopback and exposes a mode-0600 connection descriptor under the worktree's Git
directory; the same descriptor is what apps/mcp-server and apps/cli connect to.
Coordination service (apps/service)
The service is a Supabase-hosted-PostgreSQL-backed record of workspace state:
operations, tasks, claims, handoffs, intents, and an audit log
(apps/service/migrations/001_initial.sql, 002_handoffs_intents.sql,
003_validations_cursor.sql, 004_supabase_auth.sql). Workspace members
authenticate directly against Supabase Auth — crosscode login (loopback
browser callback) or crosscode login --email/--password (headless), see
Sign-in below; the service verifies the resulting Supabase-issued JWTs
(fetched from SUPABASE_URL's JWKS endpoint, apps/service/src/auth.ts) rather than
signing its own. A replica (an individual daemon/device identity) is
self-registered by an authenticated member calling POST /v1/replicas
(apps/service/src/http.ts) instead of being minted through an admin-issued
enrollment token. Every authenticated request carries an
x-crosscode-workspace-id header naming which workspace it targets, since a
Supabase access token only carries the member's auth.users id, not a
workspace/replica scope the way Crosscode's own previously-issued tokens did.
Daemons upload operations idempotently and download them back in cursor order.
Live updates also fan out over WebSocket (presence, task, claim, handoff,
intent, operation), with a durable poll fallback when a replica is offline. The
service enforces workspace membership and role on every request (including
Postgres Row Level Security as defense-in-depth alongside the service's own
resolveMembership checks); it does not execute anything a replica sends it
beyond storing and relaying it. Workspace and member provisioning
(pnpm service:provision) remains an administrator-side operation, now backed
by the Supabase admin API (SUPABASE_SERVICE_ROLE_KEY) to create or invite
Supabase Auth users instead of writing one-time enrollment tokens.
The service is multi-tenant and keeps the whole team surface — workspaces,
memberships, roles, invites (/v1/invites), one-time pairing codes
(/v1/pairing-codes), projects (/v1/projects), presence, and billing. All of
it is reached from the CLI or over HTTP; none of it has a web UI.
Sign-in (crosscode login)
crosscode login has two paths to the same Supabase session, and the daemon
cannot tell them apart afterwards.
Browser (default, needs a TTY). The CLI starts a short-lived HTTP server
bound to 127.0.0.1 on an ephemeral port with a single /callback route, and
generates a 32-character random state. It opens
${WEB_URL}/auth/cli.html?port=<port>&state=<state> — WEB_URL from --web,
else CROSSCODE_WEB_URL, else the deprecated CROSSCODE_DASHBOARD_URL. There
is no production default: no site is deployed yet, so with none of the three
set this fails with WEB_URL_REQUIRED rather than guessing a domain. That page signs the
visitor in against Supabase (rendering the ordinary sign-in form if they aren't
already), then POSTs the session back to http://127.0.0.1:<port>/callback as
{ state, access_token, refresh_token, expires_at, user: { id, email } } and
tells them to return to the terminal. The loopback server answers the CORS
preflight (OPTIONS /callback → Access-Control-Allow-Origin: *,
Access-Control-Allow-Methods: POST, OPTIONS,
Access-Control-Allow-Headers: content-type) so that fetch succeeds. A
mismatched or missing state fails with LOGIN_STATE_MISMATCH; no callback
within 300 seconds fails with LOGIN_TIMEOUT. --no-browser prints the URL
instead of opening it.
Headless. crosscode login --email <e> --password <p> signs in directly
against Supabase with no browser, no loopback server, and no TTY. This is the
path for coding agents and CI.
Either way the session is persisted by the same daemon config writer into the
mode-0600 <git-dir>/crosscode/config.json (refresh token to the OS keychain
where one is available). Tokens are never printed and never appear in --json
output; crosscode login --json emits only
{"value":{"userId":"…","email":"…"}}. See
security.md for why.
Thin clients (apps/cli, apps/mcp-server)
The CLI and the MCP server hold no sync state of their own. They talk to the local
daemon's HTTP API and render or forward its state: status, tasks, claims, proposal
inspection, accept/reject, checkpoints, and validation runs. apps/mcp-server
additionally bootstraps the daemon on first connection if one isn't already running
for the worktree. These two, plus the daemon itself, are the entire supported
product surface: every editor and agent, including VS Code and Cursor,
integrates via MCP (mcp-clients.md). There is no editor
extension. This CLI/MCP-first contract — humans and agents alike get direct
access to every routine operation (status, claims, checkpoints, accept/reject,
publish) with no website required — is formalized in
AGENTS.md.
Safety invariants
- The local filesystem remains authoritative for local work.
- Remote operations arrive as proposals and are never automatically applied.
- Every materialization checks the local base again and creates a checkpoint first.
- Excluded paths, common secret files, symlink traversal, and payloads that are malformed or whose content does not match its recorded hash are rejected. (Binary files themselves are supported — they travel base64-encoded and are materialized byte-exactly — but any conflict involving one requires human approval, since hunk-level merge analysis is text-only.)
If Crosscode is stopped or removed, the repository remains an ordinary Git
repository — Git is the durable history and publishing layer, and checkpoints live
under refs/crosscode/checkpoints/... without polluting normal branch history.
See README.md for setup and current capabilities, and BUILD_INSTRUCTIONS.md for the milestone-by-milestone implementation status.
View raw markdown · generated from
docs/architecture.md at build time, do not hand-edit this page.