CLI Reference
Zero to running: init · dev · add · validate · doctor
nucleus-core-ts ships a single, dependency-free CLI that takes you from an empty directory to a booting backend and then keeps that project healthy as it grows. It is exposed under three names once the package is installed — `nucleus`, `nucleus-core-ts` and `nucleus-generate` — all pointing at the same binary. Everything runs on Bun with nothing extra to install.
The happy path is three commands: `nucleus init` scaffolds a runnable backend from a preset, `nucleus dev up` makes sure Postgres and Redis are reachable, and `nucleus dev` runs it. From there `nucleus add` extends the project's config in place (regenerating the Drizzle schema for you), while `nucleus validate` and `nucleus doctor` keep the config and the local environment honest. A maintenance command, `nucleus audit:purge-noise [--execute]` (alias audit-purge-noise), dry-runs by default and, with --execute (or its alias --yes), deletes historical low-signal audit_logs rows; it also accepts --url <conn> (the Postgres connection string, default $DATABASE_URL) and --schema <name> (limit the purge to one schema — default: every schema that has an audit_logs table, so it is multi-tenant safe). Two meta commands round it out: `nucleus --version` (`-v`) prints the installed package version, and `nucleus help` (`--help`, `-h`, or any unrecognised/no argument) prints the command list. This page documents every command and flag exactly as the binary behaves.
Zero to running#
One preset, one install, one boot. `init` writes a complete backend, installs dependencies and generates the schema; `dev up` brings up the datastores (starting throwaway Docker containers only for whatever is not already running); `dev` runs the app on port 4000 with Swagger at /docs.
1# scaffold a runnable backend from a preset (non-interactive)2nucleus init --preset saas --app-id shop -o ./shop3 4cd ./shop5nucleus doctor # is bun / Postgres / Redis ready?6nucleus dev up # ensure datastores (Docker fallback for missing ones)7nucleus dev # runs `bun run dev` -> http://localhost:4000/docs8 9# later, grow the project10nucleus add entity # interactive; regenerates the Drizzle schema11nucleus add feature storage # enable a feature block12nucleus add oauth google # add an OAuth provider13nucleus validate # schema + cross-field checks (CI-friendly)init — scaffold a runnable backend#
Builds a config (from a preset, an existing file, or an interactive builder), validates it, writes a complete backend (package.json, src/index.ts, src/config.json, src/config.nucleus.json [the JSON-Schema copy that powers editor autocomplete via $schema, when the packaged schema is present], tsconfig, .gitignore, .env) and — unless you pass --no-install — runs `bun install` and generates the Drizzle schema. In config.json secrets are referenced by ENV-VAR NAME (never literals); those names' values live in .env, where init pre-fills a real DATABASE_URL and freshly-generated token secrets and leaves any other referenced secret as a blank placeholder to fill. With no flags it is fully interactive; with --preset or --yes it is non-interactive.
1nucleus init # interactive builder2nucleus init --preset api --app-id orders3nucleus init --config ./existing.json # scaffold around an existing config4nucleus init -y # non-interactive, defaults to the saas preset--preset <name>minimal | api | saas | marketplaceOptionalSeed the config from a preset (non-interactive). minimal: auth off, one example entity — the smallest runnable API. api: consumer-mode (resource server) + authorization + rate-limit + monitoring. saas: full auth (login/register/email-verify) + multi-tenant + email + storage/CDN + notifications + monitoring + audit + rate-limit. marketplace: the SaaS stack plus Stripe payments and the marketplace money layer (splits/reserves/payouts).
--config <path>stringOptionalScaffold around an existing config.json instead of building one — the file is used as-is (after validation) and its appId is honoured.
--app-id <id>stringOptionalThe app id for the new project. Slugified. When omitted it is prompted for (interactive) or defaults to my-app (with --yes/--preset).
--output, -o <dir>stringOptionalTarget directory. Defaults to ./<appId>. A non-empty existing directory is confirmed (and aborted if you decline) only in interactive mode; in non-interactive mode (--yes/--preset/--config) init prints a warning and writes into it anyway — so point -o at a fresh dir in CI to avoid clobbering.
./<appId>--yes, -yflagOptionalNo prompts. Implies the saas preset unless --preset or --config is given. Ideal for CI and scripted scaffolds.
--no-installflagOptionalWrite the files but skip `bun install` and the schema generation step. Useful when you only want the source, or in an offline sandbox — run `bun install && bun run generate` yourself afterwards.
dev — run locally#
The local run loop. `dev up` ensures Postgres + Redis are reachable, `dev` runs the app, and `dev down` tears down anything `dev up` started. It reads DATABASE_URL / REDIS_URL (or REDIS_HOST + REDIS_PORT) from the project .env to know where the datastores live.
1nucleus dev up # ensure Postgres + Redis (Docker fallback for missing ones)2nucleus dev # pre-flight the datastores, then run `bun run dev`3nucleus dev down # stop + remove the containers `dev up` starteddev upensure datastoresOptionalProbes Postgres and Redis at their configured host:port. Anything already reachable is left untouched — so your existing local Postgres/Redis are used as-is. For a missing datastore on a local host it starts a throwaway Docker container (nucleus-dev-postgres on postgres:16-alpine, nucleus-dev-redis on redis:7-alpine), reusing a stopped one if present, and waits until it accepts connections. A missing datastore on a remote host is reported, not started.
dev (run)run the appOptionalWarns if Postgres/Redis are not reachable, then runs the project's `dev` script (or `bun --watch src/index.ts` if there is none), streaming its output until Ctrl-C. The scaffolded backend listens on port 4000 with Swagger at /docs. Invokable as bare `nucleus dev` or, identically, as the explicit `nucleus dev run` — so the three subcommands are up | down | run (any other sub errors).
dev downstop datastoresOptionalRemoves the nucleus-dev-postgres and nucleus-dev-redis containers if they exist. It never touches datastores it did not start (a container it did not create is out of scope).
add — extend a project in place#
Grow an existing project by editing its config.json (schema-validated on every write) and, for schema-affecting changes, regenerating the Drizzle schema so the next boot picks it up. Any secrets introduced are written as ENV-VAR NAMES to fill in .env — never literals.
1nucleus add entity # interactive table + columns; regenerates the schema2nucleus add feature storage # enable a feature block3nucleus add oauth google # add an OAuth provider to authenticationadd entityinteractiveOptionalColumn-by-column entity builder (table name, columns with types, NOT NULL, public GET, bulk endpoints). Appends to config.entities (skipping any name that already exists), then runs `generate` to rewrite src/drizzle/schema.ts + relations.ts. (Alias: `add entities`.)
add feature <name>storage | chat | notification | verification | audit | captcha | pubsub | paymentOptionalEnables a feature block with schema-valid defaults. storage turns on local storage + CDN; payment prompts for iyzico or Stripe. No schema regeneration is needed (features don't change tables). Already-enabled features are left as-is.
add oauth <provider>google | microsoft | github | appleOptionalAdds an OAuth provider under authentication.oauth with CLIENT_ID / CLIENT_SECRET env-var placeholders. Requires authentication to be enabled (the api and saas presets have it on).
validate & doctor#
Two read-only checks. `validate` verifies a config; `doctor` verifies the machine can run it. Both exit non-zero on a blocking problem, so they drop straight into CI or a pre-commit hook.
1nucleus validate # finds src/config.json | config.json | src/config.nucleus.json2nucleus validate ./path/to/config.json3nucleus doctor # bun, Postgres, Redis, optional docker/dapr, config validityvalidate [config]schema + cross-field + env refsOptionalValidates the config against the generated JSON-Schema, then applies cross-field rules the schema can't express (appId and database.url are required; entities present; authentication.mode is required when authentication is enabled, and consumer mode combined with authorization requires authentication.idpUrl for /auth/check; payment.provider matches its block; notification channels have their credentials; chat attachments require storage + CDN). Missing 'required' schema fields that have runtime defaults surface as warnings, not errors — as do unknown or misspelled config keys not present in the schema (when additionalProperties is false), so typos surface non-blockingly. Also prints every env var the config references. Exit 1 on any error. (Alias: check.) Note: doctor runs the same schema check but reports only errors, not these warnings.
doctorenvironment checkOptionalGreen/red checklist: bun runtime; Postgres reachable (DATABASE_URL); Redis reachable (REDIS_URL / REDIS_HOST+REDIS_PORT); docker, dapr and createdb as optional tooling; and — if a config is present — its validity plus any referenced env vars that are unset. Exit 1 if a required check (bun / Postgres / Redis / a valid config) fails.
generate & scaffold#
The original two commands, still here. `generate` keeps the Drizzle schema in sync with the config; `scaffold` lays down an entire multi-artifact project.
generate <config.json> <outDir>config → DrizzleOptionalReads your config.json and writes the Drizzle schema.ts + relations.ts the plugin loads, merging your entities with the built-in system tables (users, user_sessions, roles, claims, audit_logs, …). Run it after every entity change — `add entity` does this for you. A leading path argument is auto-detected, so `nucleus src/config.json src/drizzle` also works. (Alias: gen.)
scaffoldfull projectOptionalInteractive scaffolding of a complete starting point — the Elysia API, a frontend, Kubernetes manifests and CI/CD pipelines — so a new service is deployable from day one. This is the heavier sibling of init, which produces just a lean, runnable backend. (Alias: new.)
Related sections