VOLT

Develop

The VOLT monorepo, how to build it from source, run its pieces in development, and the checks CI runs.

VOLT's application code lives in one pnpm monorepo, VoltLabs-Research/VOLT. The native libraries, every plugin and the plugin CLI live in their own repositories. This page covers the monorepo: what each package is, how to build it, how to run the server, the cluster daemon and the client while you work on them, and what CI checks on every push.

To deploy VOLT rather than develop it, see Self-hosting.

Repository layout

The workspace (pnpm-workspace.yaml) is every directory under packages/apps, packages/infra, packages/shared and packages/sdk.

DirectoryPackage nameWhat it is
packages/apps/clientvolt-webappThe React web app, built with Vite. It ships the web runtime, so it can run a whole VOLT stack in the browser tab.
packages/apps/landingvolt-landingThe public landing page, a separate Vite app deployed apart from VOLT. Its links point at the web app (VITE_VOLT_APP_URL), and its Run in this browser opens the app's /connect?run=browser.
packages/apps/desktopvolt-desktopThe Electron desktop app: a window onto the client's build that runs the stack in that window. It also holds the self-hosting deploy CLI (src/cli.ts, scripts/deploy.sh) and the Docker stack (stack/compose.yml, stack/compose.images.yml).
packages/infra/servervolt-serverThe API server: Express routes under /api and a Socket.IO gateway. It stores its data in PostgreSQL, or in SQLite in the browser and desktop runtimes.
packages/infra/clustercluster-daemonThe cluster daemon: runs analyses and plugins, stores trajectories and results, and connects out to the server.
packages/infra/web-runtime@volt/web-runtimeThe server and the daemon compiled to run unchanged in browser workers, on a Node platform implemented over browser APIs, with CPython from Pyodide as python3.
packages/sdk/voltclient@voltstack/voltclientThe TypeScript HTTP client, published to npm. The web client uses it too.
packages/sdk/pythonvoltsdkThe Python SDK, published to PyPI and bundled as a wheel in the web runtime.
packages/sdk/daemon-cluster-client@voltstack/daemon-cluster-clientThe protocol between a cluster daemon and the server, and a client for it, published to npm.
packages/shared/contracts@volt/contractsThe wire contracts the server, the client and the daemon share: routes, request and response types, enums, and the plugin workflow model with its rules. Raw TypeScript with no build step; each consumer compiles it from source.
packages/shared/local-stack@volt/local-stackProvisioning of the single-user local stack the browser and desktop runtimes run: each backend's environment, the bootstrap that creates the local account, team and cluster, and the default plugin seed. The deploy CLI reuses the bootstrap.
packages/shared/tooling@volt/toolingESLint and dependency-cruiser configuration for the server and the daemon, and the volt-layering command.

Prerequisites

ToolVersionNeeded for
Node.js22 (from .nvmrc)Everything.
pnpm10Installing and running the workspace. CI uses pnpm 10.
Emscripten3.1.74Building @volt/web-runtime, which compiles the backends' native addons to WebAssembly. The client build needs it, because the client ships the runtime.
A C/C++ toolchain and the libraries of canvasInstalling cluster-daemon, whose native modules (DuckDB, canvas) build on install. On Debian or Ubuntu, CI installs build-essential pkg-config libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev zstd.
PostgreSQLRunning the server outside Docker. The stack's compose file uses postgres:17-alpine.
DockerRunning the stack with Docker Compose.
Google Chrome or ChromiumThe web runtime's smoke tests (CHROME_PATH, /usr/bin/google-chrome by default).
Python3.10 or laterThe Python SDK.

Install and build

git clone https://github.com/VoltLabs-Research/VOLT.git
cd VOLT
pnpm install
pnpm build          # pnpm -r build: every package that has a build script

To build one package, build its workspace dependencies first and then the package. The SDKs' package.json types point at their dist/, so a consumer's typecheck needs them built:

pnpm --filter "volt-server^..." build   # the dependencies of volt-server
pnpm --filter volt-server build

The web runtime bundles the backends' built dist/, so build them before it, with Emscripten on your PATH:

source ~/emsdk/emsdk_env.sh
pnpm --filter "@volt/web-runtime^..." build
pnpm --filter @volt/web-runtime build

The client's Vite plugin copies the built runtime into the client's build and fails with @volt/web-runtime: … is not built (run its build first) when it is missing. pnpm --filter "volt-webapp^..." build builds the runtime along with the client's other dependencies.

Package scripts

PackageScripts
volt-webappdev, build (tsc -b, then vite build), lint, preview
volt-desktopdev, build, typecheck, lint, package (builds the client, then the installers with electron-builder), package:only, cli:build (bundles the deploy CLI to dist/cli.cjs), deploy (runs the CLI from source), smoke
volt-serverstart (ts-node from src/), start:dist, build, typecheck, lint, layering, layering:update, test
cluster-daemondev (tsx from src/), start (from dist/), build, fetch:tools, typecheck, lint, layering, layering:update
@volt/web-runtimebuild, typecheck, smoke <page>
@voltstack/voltclient, @voltstack/daemon-cluster-clientbuild, dev (tsup in watch mode), typecheck, test

The root package.json runs build, typecheck and lint across the workspace (pnpm -r).

Running in development

Server

The server reads packages/infra/server/.env. Copy .env.example and point DATABASE_URL at a PostgreSQL database; the example value is postgres://volt:volt@localhost:5432/volt. One way to get that database:

docker run -d --name volt-dev-postgres -p 5432:5432 \
  -e POSTGRES_USER=volt -e POSTGRES_PASSWORD=volt -e POSTGRES_DB=volt \
  postgres:17-alpine

Then start the server from source. It listens on SERVER_PORT (8000 unless set):

cd packages/infra/server
cp .env.example .env
pnpm --filter volt-server start

GET /healthz answers {"status":"ok", ...} once it is up. Configuration lists the variables.

Cluster daemon

The daemon reads its configuration from the environment; it does not load a .env file. packages/infra/cluster/.env.example lists the variables. It needs at least TEAM_CLUSTER_ID, TEAM_CLUSTER_DAEMON_PASSWORD and VOLT_CLOUD_URL (the server's origin), which you get by creating a cluster in the app (see Clusters).

cd packages/infra/cluster
set -a; source .env; set +a
pnpm dev

dev first fetches the daemon's vendor tools (volt-dump-transform and zstd, from the CoreToolkit release pinned in package.json under coreToolkitTools), then runs src/daemon.ts with tsx. scripts/start.js sets the V8 heap to 80% of the machine's memory, or of its cgroup limit; set DAEMON_HEAP_MB to override it.

Client

VITE_PROXY_API_URL=http://localhost:8000 pnpm --filter volt-webapp dev

The dev server listens on port 5170 over HTTPS and proxies /api and /socket.io (WebSockets included) to VITE_PROXY_API_URL, else VITE_SERVER_ENDPOINT, else http://127.0.0.1:8000. Without certificates it serves a self-signed one. Service workers reject self-signed certificates, so to run the in-browser stack from the dev server, generate a trusted local CA and certificate first:

packages/apps/client/scripts/dev-certs.sh            # localhost, 127.0.0.1 and this host's LAN IPs
packages/apps/client/scripts/dev-certs.sh my-host    # extra names or IPs

The script writes packages/apps/client/.certs/ and trusts the CA in the user's NSS database (Chrome and Brave on Linux). It needs openssl and certutil. Import .certs/volt-dev-ca.pem by hand in Firefox and on other devices.

Desktop app

The desktop window loads the client's build from packages/apps/client/dist, so build the client first:

pnpm --filter "volt-webapp..." build
pnpm --filter volt-desktop dev

The Docker stack

packages/apps/desktop/stack/compose.yml describes the stack: volt-postgres, volt-server, volt-client and, under the enrolled profile, cluster-daemon. It builds each image from the checkout that VOLT_SOURCE_DIR points at. compose.images.yml is an overlay that swaps every build for a published image. The deploy CLI drives both; see Self-hosting.

Checks and tests

CheckCommand
Typecheckpnpm --filter <package> typecheck. The client has no typecheck script; its build runs tsc -b.
Lintpnpm --filter <package> lint
Layeringpnpm --filter volt-server layering and pnpm --filter cluster-daemon layering run dependency-cruiser. They fail on any rule error and on growth of the debt pinned in each package's depcruise-baseline.json; layering:update lowers the baseline.
Server testspnpm --filter volt-server test runs src/**/*.test.ts with node --test on an in-memory SQLite database.
SDK testspnpm --filter @voltstack/voltclient test, pnpm --filter @voltstack/daemon-cluster-client test, and pytest in packages/sdk/python.
Web runtime smoke testspnpm --filter @volt/web-runtime smoke <page>, with page, programs, stack, python or notebook. Each runs a page of smoke/ in headless Chrome. The Python pages download packages from Pyodide's CDN.

CI

.github/workflows/verify.yml runs on every push to main, on pull requests and on demand. A newer push cancels the run in progress on the same ref. Each job installs only its package and the workspace packages it depends on (pnpm install --frozen-lockfile --filter <name>...), then builds those dependencies.

JobSteps
ServerTypecheck, lint, layering, build (tspc applies typia's transform, so the build proves it still runs).
Cluster daemonNative toolchain, then typecheck, lint, layering, build.
ClientNative toolchain and Emscripten 3.1.74, then lint, typecheck of the web runtime, the five web runtime smoke pages, and the client build.
DesktopTypecheck, lint, build, and the deploy CLI bundle (cli:build).
LandingLint and build of volt-landing, which has no workspace dependencies.
Stack composedocker compose config on compose.yml and on compose.yml with compose.images.yml, both with the enrolled profile, and an assertion that the overlay leaves no build: block.

The other workflows publish:

WorkflowPublishes
publish-stack-images.yml, publish-cluster-daemon.yml, publish-cluster-stack-image.yml, publish-jupyter-scripting.ymlContainer images to ghcr.io/voltlabs-research, on pushes to main that touch their paths.
desktop-build.ymlDesktop installers, on v* tags.
desktop-cli-release.ymlThe deploy CLI bundle and stack/compose.yml as assets of a v* release.
publish-sdk-npm.yml@voltstack/voltclient and @voltstack/daemon-cluster-client to npm, on voltsdk-node-* releases or on demand.
pypi-voltsdk.ymlvoltsdk platform wheels to PyPI, on voltsdk-py-* releases or on demand.

Other repositories

Everything outside the monorepo is under github.com/VoltLabs-Research, one repository each:

RepositoryWhat it is
CoreToolkitThe C++ library plugins build on: the LAMMPS frame, neighbor search, the plugin entry point and the Parquet writers. It also holds the reusable workflow that builds and publishes plugin binaries, and the daemon's volt-dump-transform tool.
LammpsIOTrajectory reading, used by CoreToolkit.
SpatialAssemblerAssembles GLB models from exporter payloads.
HeadlessRasterizerRenders trajectory previews without a display.
vpmThe plugin registry CLI. See VPM.
One repository per pluginFor example coordination-analysis and OpenDXA. See the plugin catalog and Writing plugins.

Native libraries describes the libraries in more depth.

On this page