Installation

Causl ships as private packages under the @causl scope, served by the Gitea npm registry at https://git.opsite.ca/api/packages/causl/npm/. The client package is @causl/causl-wasm-ts, and its engine is the Rust core compiled to WebAssembly. I put the registry configuration first because it is the step that fails quietly: without the scoped registry line, npm install @causl/core still succeeds, against a different package on public npmjs.

Read this before you copy any install command. @causl/core has zero versions on the Gitea registry, and it does resolve on public npmjs at 0.3.0 through 0.3.3 as a different package: the retired TypeScript engine. An install of it returns exit code 0 and prints no warning, then every import in your application resolves against the wrong code. The package you want is @causl/causl-wasm-ts. See the @causl/core hazard.

Registry configuration

The @causl scope is private and is not served by npmjs. Point the scope at the Gitea registry and supply a token before you install anything:

# ~/.npmrc, or an .npmrc at your repo root @causl:registry=https://git.opsite.ca/api/packages/causl/npm/ //git.opsite.ca/api/packages/causl/npm/:_authToken=${GITEA_PACKAGES_TOKEN} always-auth=true

GITEA_PACKAGES_TOKEN is a Gitea personal access token carrying the read:package scope, issued from git.opsite.ca → Settings → Applications → Generate New Token. Anonymous requests to the registry return 401, so a missing token is a loud failure. A missing registry line is the quiet one.

The old Verdaccio host iasbuilt-npm.opsite.ca is gone. Any .npmrc snippet that names it, in your repo or in an older copy of these docs, is stale and should be replaced with the block above.

The @causl/core hazard

This is the single most expensive mistake available on this page, because nothing about it looks like a mistake at the time:

The correct client package is @causl/causl-wasm-ts. If an older document, a lockfile, or a stale pin in your package.json still names @causl/core, that is the thing to fix, and a successful install is not evidence that it is fine.

One thing that is not the same mistake: the open-source TypeScript engine on public npmjs is @causlts/core, from causljs/causl-ts. That is a genuinely different product with its own release line, not a mirror of the private client. Do not treat the two as interchangeable, and do not substitute one for the other to work around a registry problem.

@causl/wasm and @causl/checker are not published anywhere. If you find an install command for either, it is out of date.

Supported environments

Causl ships ESM-only modules. Because the engine is WebAssembly, the host floor is set by what the shipped artefact needs rather than by the JavaScript language level:

The governing browser requirement is typed function references, established by feature-bisecting the shipped artefact. The artefact declares zero WasmGC struct, array, or rec-group types, so "WasmGC heap types" is not the requirement and an older floor quoting it was wrong.

Treat the floor as a release-note claim, not as our measurement. Nobody has run the shipped artefact on a real boundary host at Safari 18.2, Chromium 119, or Firefox 120. If your deployment targets sit on that edge, measure before you commit.

There is no CommonJS build. If you need to require the client from a CJS module, use the dynamic import() form or a small wrapper.

Engine

There is one engine. @causl/causl-wasm-ts runs the Rust core compiled to WebAssembly (rust-ssot) unconditionally, and there is no TypeScript fallback in the package: createCauslTs is deleted from source, and createCausl() is construct-or-throw. The implicit capability fallback that earlier revisions of these docs described was withdrawn at 0.5.0, both because the fallback engine and the primary disagreed on a surface the SPEC requires to be identical, and because the implicit path was the accident path.

Booting it is one await at application init, and nothing after that:

import { createCausl } from '@causl/causl-wasm-ts'; import { preloadCauslWasm } from '@causl/causl-wasm-ts/wasm'; await preloadCauslWasm(); // once, at app init const graph = createCausl(); // synchronous from here on const a = graph.input('a', 1); const b = graph.input('b', 2); const sum = graph.derived('sum', get => get(a) + get(b)); graph.commit('bump-a', tx => { tx.set(a, 10); }); console.log(graph.read(sum)); // 12

The wasm artefact sits behind the explicit @causl/causl-wasm-ts/wasm subpath, and since 0.5.0 importing it is required rather than optional. Importing the package root pulls in the public API plus a small loader stub and no engine; the engine bytes are paid for by the caller that imports the subpath and calls preloadCauslWasm(), which every application now does. createCauslWasmSync() is available for synchronous construction sites, and createCauslWasm() is retained as preload composed with the sync factory.

Two failure codes matter, and you should branch on error.code rather than instanceof:

One bridge ships, gc-classic. The gc-builtins bridge was deleted. Bridge ids are gc-classic and gc-builtins, never wasmgc-classic or wasmgc-builtins.

@causl/causl-wasm-ts

@causl/causl-wasm-ts is the client: the dependency graph, the transactional commit, the deterministic recompute pipeline, and the loader that installs the Rust engine behind them. Every other package peers on it. Start here; install nothing else until you need it.

# npm npm install @causl/causl-wasm-ts # pnpm pnpm add @causl/causl-wasm-ts # yarn yarn add @causl/causl-wasm-ts

The only declared peer dependency is fast-check, and it is marked optional: it is pulled in only by the @causl/causl-wasm-ts/testing subpath that exposes property-test helpers. You will never need to install it for production use.

The package exposes four entry points: the root, /wasm (the loader and preloadCauslWasm), /testing, and /internal.

@causl/react

@causl/react wires a causl graph into React's render lifecycle through useSyncExternalStore. The headline hook is useCauslNode, with useCauslShallow, useCauslFamily, useCauslSuspense, and a <Hydrate> SSR boundary alongside it.

# npm npm install @causl/causl-wasm-ts @causl/react react # pnpm pnpm add @causl/causl-wasm-ts @causl/react react # yarn yarn add @causl/causl-wasm-ts @causl/react react

@causl/react declares both react (at ^18.3.0 || ^19.0.0) and @causl/causl-wasm-ts as peer dependencies. Nothing is resolved for you transitively, so install the client explicitly alongside every companion package, exactly as the commands above do. The same holds for @causl/sync: if you want useCauslSuspense against async resources, install the sync package by hand.

@causl/sync

@causl/sync is the async resource lifecycle: fetchers, mutations, suspendable reads, and the race-row guarantees (S-1 stale-write, S-2 stale-read) that keep concurrent network calls from corrupting derived state. Install it when you have data that lives outside the graph, over HTTP, IndexedDB, WebSocket, or anything similar.

# npm npm install @causl/causl-wasm-ts @causl/sync # pnpm pnpm add @causl/causl-wasm-ts @causl/sync # yarn yarn add @causl/causl-wasm-ts @causl/sync

@causl/persistence

@causl/persistence ships the storage adapters (localStorage, sessionStorage, IndexedDB, custom StorageAdapter) that pair with graph.dehydrate() and graph.hydrate() in the client. The protocol ships in the client; the concrete adapters live here so the client stays small.

# npm npm install @causl/causl-wasm-ts @causl/persistence # pnpm pnpm add @causl/causl-wasm-ts @causl/persistence # yarn yarn add @causl/causl-wasm-ts @causl/persistence

@causl/checker

@causl/checker is the static analyser: a CLI plus library entry point that walks your causl graph definitions and surfaces race rows (R-1 through R-8), cycle hazards, missing capability scopes, and unreachable derivations.

There is nothing to install today. @causl/checker is not published, to the Gitea registry or anywhere else, so any npm install @causl/checker line you find is wrong. Consume the checker from its source repository until a published artefact exists, and do not add it to a package.json expecting it to resolve.

@causl/formula

@causl/formula brings spreadsheet formula patterns on top of the client: formulas, ranges, and cycles expressed as causl derivations. Install it when your application has spreadsheet-like cells with formula references or range dependencies that you want expressed declaratively rather than re-encoded as ad-hoc derivations.

# npm npm install @causl/causl-wasm-ts @causl/formula # pnpm pnpm add @causl/causl-wasm-ts @causl/formula # yarn yarn add @causl/causl-wasm-ts @causl/formula

@causl/devtools

@causl/devtools is the optional Historian and Editor surface: time-travel, live derived inspection, batched replaceMany edits during development. It pairs with the browser extension and is a dev-only dependency.

# npm npm install --save-dev @causl/devtools # pnpm pnpm add -D @causl/devtools # yarn yarn add --dev @causl/devtools

The wiring itself is in the client behind a one-line opt-in: createCausl({ devtools: true }) dead-code-eliminates in production builds. You only install @causl/devtools if you want the standalone REPL utilities outside the browser extension.

TypeScript setup

All @causl packages ship .d.ts files. The minimum tsconfig.json for an application consuming causl looks like this:

{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "jsx": "react-jsx", "lib": ["ES2022", "DOM", "DOM.Iterable"] } }

Module resolution. Causl is ESM-only and uses package exports subpaths (@causl/causl-wasm-ts/wasm, @causl/causl-wasm-ts/testing, @causl/causl-wasm-ts/internal). You need "moduleResolution": "Bundler" (or "NodeNext") so TypeScript reads the exports map. Older "moduleResolution": "Node" will silently miss the subpath types, which matters most for /wasm, the subpath every application has to import.

Strict mode. Causl's branded types (NodeId<T>, GraphTime) only catch the errors they are designed to catch under strict: true. A non-strict tsconfig erases the brand and a node from graph A passed to graph B will fail at runtime instead of at tsc. I treat strict: true as a precondition; do not try to use causl without it.

JSX. If you use @causl/react, set "jsx": "react-jsx" (or "react-jsxdev" in dev). The classic "react" setting still works but is no longer recommended.

Bundle sizes

Causl publishes a size-limit CI gate on every published surface so the bundle budget is enforceable, not aspirational. The ceilings, per SPEC §14.2 and §17.6, are:

BundleCeilingNotes
@causl/causl-wasm-ts (full import) 30 KB Brotli Includes source-mapped error chains, devtools opt-in wiring (eliminated in production), runtime IR validation in hydrate(), branded NodeId<T> and GraphTime, and the dehydrate() / hydrate() SSR pair. The /wasm subpath is externalised from this cell.
@causl/causl-wasm-ts (createCausl-only) 26 KB Brotli The minimum import surface. Adopters who only register inputs and read derivations sit near this floor.
@causl/causl-wasm-ts/wasm 32 KB Brotli The loader plus the backend wrapper. This is JavaScript only; the engine bytes are the row below.
wasm bridge gc-classic (raw) Enterprise 756 KiB raw The shipped causl_engine_bridge_bg.wasm measured 714,194 bytes on 2026-08-11 (sha256 ab4b62c1…), inside the 774,144-byte cap. Only this one bridge ships. See the Enterprise wasm-performance guide for what those bytes buy.
@causl/react 8 KB Brotli Absorbs the four §8.2 extension hooks plus the dev-mode "Update did not commit" warnings (eliminated in production).
@causl/sync (full import) 12 KB Brotli The resource-only entry point is gated separately and is smaller.
@causl/devtools-bridge (absent-extension path) 5 KB Brotli The bridge logic that the devtools: true opt-in imports. The present-extension path is larger.

The CI gate size fails any PR that crosses these ceilings, and a bump needs the §14.2.1 written team consensus in the PR body or it is rejected. The earlier 4.5 KB, 6 KB and 3 KB ceilings were retired once the team accepted that the small-bundle promise was costing more in adopter glue than it was earning.

Next steps

With the packages installed, the natural next step is the Tutorial, a walkthrough that builds a small graph, wires it into a React component, and adds an async fetch with @causl/sync. From there: