Development
How to run Modular from a clone.
Requirements#
- Node
^22.19.0 || >=24.11.0 - pnpm
11.24.0(pinned inpackageManager) - macOS: Xcode Command Line Tools (
apps/desktopbuilds a native Sparkle bridge on install) - For the desktop app: Go (it builds
apps/relayd) and, on macOS, Swift (the mac bridge)
First run#
cp .env.example .env
# put a real secret in MODULAR_CREDENTIAL_SECRET:
node -e "console.log(require('node:crypto').randomBytes(32).toString('hex'))"
pnpm install
pnpm --filter @modular/plugin-sdk build # the web Vite config imports from its build output
pnpm --filter "./plugins/*" build # the server loads the built-in plugins at start
pnpm dev:fullstackThe web app is on http://localhost:5174, the server on http://127.0.0.1:21423.
The server reads .env.local, then .env, from the repository root; a variable already
set in the environment wins. MODULAR_DATA_DIR=./data is relative to the server's working
directory, so the data lands in apps/server/data/ (modular.db, server.log,
preferences/, runtimes/). Use an absolute path to put it elsewhere.
No model provider keys are needed to start; everything else in .env.example
(OpenTelemetry, Langfuse, PostHog) is off by default.
Commands#
| Command | Runs |
|---|---|
pnpm dev:fullstack | Server and web app |
pnpm dev:server, pnpm dev:web | One of them |
pnpm dev:desktop | The Electron app (builds relayd, the mac bridge and native helpers first) |
pnpm typecheck | TypeScript, every package |
pnpm lint | ESLint |
pnpm test | Unit tests (builds the SDK and plugins first) |
pnpm test:server | Server tests only |
pnpm exec cucumber-js --config e2e/cucumber.mjs | End-to-end scenarios (e2e/) |
pnpm e2e:check | Checks the e2e suite's own rules |
pnpm generate:web | Regenerates the web API client from the server's OpenAPI |
pnpm gen:cli | Regenerates the CLI commands from the server's OpenAPI |
pnpm brand:render | Renders the logo and icons (resources/brand) |
pnpm observability:up | Local OpenTelemetry collector and Prometheus (Docker) |
pnpm generate:web boots the server's route table, so it needs MODULAR_DATA_DIR set
(any empty folder).
Running inside a Modular terminal#
A terminal opened by an installed Modular exports that app's variables (its data folder,
its port, NODE_ENV=production). Unset them before running a dev server or tests, or
they point at your real data:
env -u NODE_ENV $(env | grep -E '^(MODULAR_|CRADLE_)' | cut -d= -f1 | sed 's/^/-u /') pnpm dev:fullstackNative module clash#
better-sqlite3 is used by the server under Node and by the desktop app under
Electron, and pnpm links one copy. Whichever rebuilt it last wins, and the other fails
with was compiled against a different Node.js version. To restore the Node build:
cd "$(node -e "console.log(require('node:path').dirname(require.resolve('better-sqlite3/package.json',{paths:['./apps/server']})))")"
npx prebuild-install -r node -t "$(node -p 'process.versions.node')"apps/server/scripts/rebuild-electron-runtime.mjs rebuilds a private copy for Electron
when packaging, so packaging does not break the Node build.
Rules for contributors#
AGENTS.md holds the project's rules: ownership of data, the View /
Container split in the web app, styling, testing. Architecture is the
map of the code.