# 0003: Convex-first EXO control plane

https://exo.now/docs/adr/0003-convex-first-control-plane

> Status: accepted · Date: 2026-09-14



# ADR 0003: Convex-first EXO control plane [#adr-0003-convex-first-exo-control-plane]

* Status: accepted
* Date: 2026-09-14
* Historical work reference: IV-173 (retired)
* Supersedes: [ADR 0001](0001-reversible-content-backend.md)

## Decision [#decision]

EXO is the application platform and control plane. It owns projects, organizations and access,
environments, provider provisioning, deploy/release/rollback state, runtime-dependency metadata,
policy and security controls, audit evidence, orchestration, health, and bounded usage and cost
aggregates.

Convex is the default persistence, transaction, function, and reactive-state layer for every state
EXO owns. An exception requires a concrete workload constraint, a named authority, reconciliation
and rollback rules, and an exit path. An inherited Payload or PostgreSQL implementation is not a
constraint.

Construct owns Page and website editing, Puck, publishing, Page authorization, and authenticated
Surfaces such as Interspace/ION. Construct persists those records against its own Convex-native
model. Payload is not moved into Construct by default; a future external-CMS adapter requires a
demonstrated integration need and remains optional rather than authoritative.

Independent Apps own their domain records. EXO provisions and observes their resources and accepts
only bounded operational observations. It does not centralize their prompts, content, user data,
scientific datasets, financial ledgers, or other domain records.

## Legacy freeze [#legacy-freeze]

Payload, PostgreSQL, and Puck in this repository are migration sources and rollback material only.
No new EXO capability may add a Payload collection, Payload/Puck dependency, or Puck implementation
file. `pnpm check:legacy-backend-freeze` enforces the checked-in baseline. Removing items from that
baseline is allowed; extending it requires changing this ADR and explicit architecture review.

Bug fixes needed to export, reconcile, secure, or retire the legacy path are allowed when they do
not increase its product scope. The normal approval gates still apply to data writes, provider
changes, Release Candidate, Production, domains, billing, and resource deletion.

## Authority rules [#authority-rules]

The collection-by-collection disposition is maintained in the
[EXO legacy authority and migration map](../exo-legacy-authority-map.md). A record has exactly one
authority during every cutover phase:

1. External providers remain authoritative for live deployment, billing, source, and health facts.
2. EXO owns portfolio and release coordination; Entire owns source and execution provenance.
3. Clerk authenticates identities; EXO Convex owns resource grants and authorization state.
4. EXO Convex owns control-plane intent, orchestration state, bounded observations, and audit
   evidence.
5. Construct Convex owns Pages, content, revisions, Puck documents, publishing, and Page/Surface
   access.
6. Independent Apps own domain data and expose only versioned contracts and bounded observations to
   EXO.

## SQL, analytical index, and object-storage exceptions [#sql-analytical-index-and-object-storage-exceptions]

SQL is not retained merely because a legacy schema exists. A SQL exception must identify a workload
Convex cannot reasonably satisfy, such as a required external protocol or specialized query shape,
and must document synchronization and failure behavior. No current EXO control-plane collection has
such an approved exception.

Large binary objects do not belong in Convex documents. Blob bytes may stay in Convex file storage
or an external object store when size, transformation, retention, egress, or interoperability
requirements justify it. Convex remains authoritative for ownership, checksum, metadata, lifecycle,
and the opaque object reference.

Specialized analytical or search indexes may remain external when their workload requires them.
They are derived and rebuildable, never the sole authority. EXO stores only bounded aggregate state
and freshness/reconciliation evidence.

## Cutover shape [#cutover-shape]

The migration is staged and reversible:

1. freeze the legacy feature surface and inventory every record family;
2. widen Convex schemas, validators, indexes, auth checks, receipts, and bounded audit logs;
3. export deterministic, secret-free, versioned source bundles with counts and hashes;
4. dry-run transforms and reconcile identifiers, relationships, constraints, and totals;
5. import through idempotent receipts into isolated Staging after the normal data-write approval;
6. shadow-compare reads without making two systems authoritative;
7. cut Staging reads and writes to Convex, then prove auth, isolation, index, idempotency, audit,
   retention, rollback, and recovery parity;
8. separately approve any Production migration and retain the frozen source for the rollback window;
9. remove EXO Payload, PostgreSQL, and Puck code and dependencies; then retire provider resources
   only through their explicit approval gates.

Rollback restores the last single authority and replays only receipt-backed operations. Ad hoc
dual writes are prohibited.
