Capxul Docs
Contributing

Architecture

The platform shape one layer up — public packages vs the private substrate, the ports-and-adapters seam, and the Convex backend.

The platform is a set of published npm packages over a private substrate, separated by a ports-and-adapters seam. The authoritative contract is canon/architecture.md; this page is the summary.

Public packages vs the private substrate

Five packages publish to npm. Everything else is "private": true and never publishes — private packages never appear in a consumer's node_modules; published artifacts inline the internal code they need at pack time.

Public (published)Private (never published)
@capxul/sdk@capxul/backend
@capxul/sdk-react@capxul/wire
@capxul/sdk-testing@capxul/types
@capxul/mcp@capxul/errors
@capxul/sandbox@capxul/config
@capxul/observability
@capxul/contracts

The layer stack

Consumers call method bundles and receive Promise<CapxulResult<T>>. Internally, ports are Effect Context.Tags, adapters are Effect Layers, and domain code is an Effect program that depends only on ports:

LayerOwns
Public SDK surfacecreateCapxulClient, method bundles, CapxulResult, CapxulError
Domain coreEffect programs for auth, provisioning, identity, credentials, smart accounts
PortsContext.Tag declarations and service interfaces
AdaptersLayer implementations of ports — external libraries and platform IO
BackendConvex functions, BetterAuth, Resend, AgentMail, tenancy guards

The load-bearing constraint: the domain core does not import Convex, BetterAuth, Resend, AgentMail, Openfort, browser storage, the Node filesystem, or network clients directly. All platform IO enters through an adapter behind a port.

The ports-and-adapters seam

A port is a Context.Tag plus a service interface; an adapter is a Layer that provides it. Each port has a spec in canon/ports/ and the full list lives in the port catalog. The catalog groups them into three families:

  • Domain portsIdentityPort, SmartAccountPort, AccountReadPort, SubAccountPort, SafeDeploymentPort, CredentialsPort, BootstrapPort.
  • Platform portsAuthClientPort, AuthCachePort, EvmSignerPort, ClockPort, EnvPort, TelemetryPort.
  • Primitive portsConvexCallPort, TransportPort.

Every port is proven by a single conformance matrix that runs the same tests against its hermetic and real adapters — see the test contract.

Where the backend runs

The backend is serverless Convex — there is no Docker image and no local database. @capxul/backend owns the Convex functions, BetterAuth (auth and OTP), Resend (email delivery), AgentMail (test inboxes), and the tenancy guards. Every public query or mutation that returns user data must call requireConsentedUser(userId, appId) before reading or returning it. The SDK reaches Convex only through the ConvexCallPort / TransportPort adapters — never by importing the generated Convex API.

The public-surface rule

Consumers import @capxul/sdk and @capxul/sdk-react — nothing else. The backend, @capxul/wire, and the Convex function APIs are private surface, and internal SDK subpaths (@capxul/sdk/production, _internal, adapter seams) are not part of the published contract.

Public SDK methods return Promise<CapxulResult<T>>:

export type CapxulResult<T> =
  | { readonly ok: true; readonly value: T }
  | { readonly ok: false; readonly error: CapxulError };

They do not return raw values, thrown exceptions, actors, state machines, or Effect programs. Internal Effect error variants are mapped to CapxulError before any value crosses the boundary. The rule itself is canon/rules/public-surface.md; the error shape is covered in Conventions.

On this page