Docs/Operate

Operate

Run local services, manage project state, connect orgs, and tune runtime behavior.

Commands

nimbus serve Pro

Start a local Salesforce-compatible API server. See the Local Server guide for full documentation.

bash
nimbus serve
FlagDefaultDescription
--addr127.0.0.1:8080Address to listen on
--api-version60.0Salesforce API version to expose
--usernameadmin@nimbus.localEmail for the default admin user
--passwordpasswordPassword for OAuth flow
--grpc-addr127.0.0.1:7443gRPC address for Pub/Sub API

nimbus app Pro

Feature overview: local app hosting.

Run a Salesforce Multi-Framework UI bundle (React + Vite app) against the local Nimbus runtime. Replaces sf ui-bundle dev for offline development: no scratch org, no sandbox, no internet required. The dev experience is identical — HMR, source maps, npm run dev all work — but every /services/data/* and /services/apexrest/* call from the React app hits Nimbus's Apex interpreter and embedded Postgres instead of a real org.

What it does:

  1. Discovers UI bundles under force-app/**/uiBundles/<name>/ (marker: *.uibundle-meta.xml)
  2. Spawns the bundle's dev server (default: npm run dev) — Vite, HMR, source maps, the lot
  3. Starts a proxy on 127.0.0.1:4545 (same default as sf ui-bundle dev) that injects window.SFDC_ENV (basePath, accessToken, instanceUrl, apiVersion, userId) into the served HTML
  4. Routes the React app's API calls to the embedded Salesforce-compatible API surface (REST sobjects, SOQL query, Apex REST, GraphQL UIAPI with mutations and introspection, UI API REST, Connect API)
  5. Opens the browser
bash
# Auto-discover the single bundle in this project and run it
nimbus app

# Pick a specific bundle by name
nimbus app reactRecipes

# Don't spawn vite — connect to one you started yourself
nimbus app reactRecipes --no-spawn

# List every bundle nimbus can see
nimbus app list

# Generate a GraphQL SDL from your local SObject metadata (replaces
# "npm run graphql:schema" which requires a real org)
nimbus app schema > schema.graphql

# Or fetch the same SDL from the live server
curl http://localhost:4545/__nimbus/app/schema > schema.graphql

# Run the bundle's production build (npm run build, no deploy)
nimbus app build reactRecipes

API surface served (full coverage of what Salesforce Multi-Framework production apps reach for):

  • OAuthPOST /services/oauth2/token, GET /services/oauth2/userinfo (OIDC identity)
  • SObject REST/services/data/v{ver}/sobjects/<Object> full CRUD; POST /sobjects/ContentVersion handles multipart/form-data file uploads
  • SOQLGET /query?q=...
  • SOSL (Search)GET /search?q=FIND ... RETURNING ..., /parameterizedSearch — translated to per-object SOQL with LIKE clauses across text fields
  • GraphQL UIAPIPOST /graphql. Queries: first/after cursor pagination, multi-field orderBy with NULLS FIRST/LAST, full where (eq/ne/lt/lte/gt/gte/like/nlike/in/nin + and/or/not combinators that nest), byId shortcut, child-relationship subqueries via nested edges/node, parent lookups via dotted paths. Aggregates: count, sum(field:), avg(field:), min(field:), max(field:) on every Connection. Pagination: real cursors with pageInfo (hasNextPage, hasPreviousPage, startCursor, endCursor). Directives: @skip, @include, @optional. Variables: resolved from request payload. Mutations: <Object>Create, <Object>Update, <Object>Delete per SObject. Introspection: __schema served from a model built at startup — codegen tools work directly against the running server.
  • UI API REST/ui-api/object-info/<Obj> (describe with picklists + record types + child relationships + defaults), /object-info/<Obj>/picklist-values/<rtId>[/<field>], /records[/<id>] (GET/POST/PATCH/DELETE), /record-defaults/create/<Obj> + /clone/<id>, /list-ui/<Obj> + /list-records/<listId>, /mru-list-ui/<Obj> + /mru-list-records/<Obj>, /related-list-info/<Obj>/<rel> + /related-list-records/<parentId>/<rel>, /layout/<Obj>, /actions/record/<id>, /duplicates/<Obj>
  • Composite APIPOST /composite (allOrNone with referenceId chaining), POST /composite/batch (independent requests), POST /composite/tree/<Obj> (nested record insert), POST /composite/sobjects (multi-type batch), GET /composite/sobjects/<Obj>?ids=...&fields=... (multi-get)
  • Bulk API 2.0 — full lifecycle for /jobs/ingest (POST create → PUT batches CSV → PATCH UploadComplete → GET state + /successfulResults / /failedResults) and /jobs/query (POST → poll → /results). Nimbus processes synchronously on UploadComplete since the runtime is in-process; the polling shape stays faithful.
  • Apex Invocable ActionsPOST /actions/custom/apex/<ClassName> dispatches to the class's @InvocableMethod via the interpreter
  • Apex REST/services/apexrest/<path> for your @RestResource classes
  • Connect / Chatter/chatter/users/me (sourced from local User SObject if present), /chatter/users/<id>, /chatter/feeds/news/.../feed-elements, /connect/communities, /connect/cms/delivery/channels
  • Limits/limits returns a stub governor-limits envelope so diagnostic UIs render
  • Diagnostics/__nimbus/app/status (JSON), /__nimbus/app/schema (live SDL), /__nimbus/app/dashboard (HTML)
  • Everything else falls through to Vite (HMR websocket, @vite/client, source maps, assets)

What's still not covered: sharing rules / FLS enforcement, picklist value dependencies as bitmaps (validFor arrays are empty), per-record-type picklist filtering, JWT / OAuth web-flow authentication, Tooling API, Reports/Dashboards, Wave Analytics, Surveys, Knowledge, TLS / HTTPS, live schema reload on metadata change, and production deploy (build only — deploy goes through sf project deploy). Anything not in the served list returns 501 NOT_IMPLEMENTED with the path so you see exactly what to fill in.

Bundle selection:

  • One bundle in the project → used automatically
  • Multiple bundles, name passed as argument → that bundle is used
  • Multiple bundles, no argument, terminal → interactive numbered picker
  • Multiple bundles, no argument, non-TTY (CI) → errors with the bundle list
  • --all → every bundle served simultaneously under /lwr/application/<name>/ with an HTML index page at root. Each bundle gets its own vite dev server with output prefixed by bundle name. Use --primary to pick which one opens in the browser by default.
FlagDefaultDescription
--addr127.0.0.1:4545Address the proxy listens on (matches sf ui-bundle dev)
--api-version66.0Salesforce API version exposed via SFDC_ENV
--no-spawnfalseDon’t run npm run dev — assume vite is already running at the bundle’s dev.url
--no-openfalseDon’t open the browser on startup
--allfalseServe every discovered bundle simultaneously (path-based routing under /lwr/application/<name>/)
--primaryIn --all mode, which bundle to open in the browser by default (first discovered if unset)
--installfalseRun npm install in the bundle directory if node_modules/ is missing (otherwise nimbus fails with a hint to install manually)

If the bundle has no node_modules/, nimbus fails early with the exact command to run rather than letting vite die with the cryptic sh: vite: command not found. Pass --install to have nimbus run npm install itself. Common vite failure modes (exit 127, EADDRINUSE) are recognised and surfaced with hints during the readiness wait — no more 30-second timeout waiting for a process that's already dead.

Subcommands:

FlagDefaultDescription
listPrint every bundle nimbus can see in this project
schemaPrint a GraphQL SDL derived from your local SObject metadata — byte-identical to what the running server advertises via introspection
buildRun the bundle’s production build (npm run build). Useful for CI: build once, deploy the artifact separately
testRun the bundle’s test suite (npm test). Wraps Vitest or whatever test runner the bundle uses
previewServe the bundle’s production build locally (npm run preview). Same API proxy on top of the optimised build

nimbus mcp

Start a Model Context Protocol server over stdio. AI coding agents (Claude Code, Cursor, and any other MCP-compatible client) call Nimbus's local test runner directly through structured tool calls instead of parsing CLI output.

The runner is initialised once at server start (database, project sources) and reused across tool calls. The agent disconnects when stdin closes; the server tears the runner down on exit.

Tools exposed:

  • run_apex_tests — run tests by pattern ("*", class name, Class.method, or path). Returns pass/fail counts, failure objects with file/line, and total duration. Optional arguments: timeout_seconds (default 300, 0 disables) caps wall time and surfaces a structured error on overrun; max_failures (default 50, 0 disables) caps the failure list returned to the agent — total_failures always reflects the true count and failures_truncated signals when the cap dropped entries. Each failure now carries a diagnostics object built for one-shot fixing: exception_type, message, the exact assert_file/assert_line, structured expected/actual for assertion failures (is_assertion), and recent_operations — the tail of SOQL queries and DML statements the test ran before it broke.
  • get_coverage — per-class line coverage from the most recent run.
  • list_test_classes — every @isTest class discovered in the project, returned as {name, file} pairs so the agent can jump straight to the source file. Does not run tests.
  • get_governor_usage — per-test governor-limit usage (SOQL queries, DML statements, CPU, heap, …) from the most recent run, plus the peak per limit. Optional class filter. Lets an agent spot a limit that scales with record count — the signature of un-bulkified code — without re-running.
  • execute_anonymous — run a block of anonymous Apex (like nimbus exec) in a fresh, rolled-back transaction. Argument: apex. Returns success, captured debug output, and the exception error on failure.
  • query — run a read-only SOQL SELECT against the local database and get the records back as JSON plus a row_count. Argument: soql. DML and non-SELECT statements are rejected — use execute_anonymous for writes.
  • describe_schema — describe an SObject: its fields (name, type, required, unique, reference_to, relationship_name, picklist values) and child relationships. Argument: sobject (optional — omit to list every SObject the project has schema for). Reads loaded metadata; runs nothing.
  • run_mutation_tests (Pro) — run mutation testing and return the mutation score plus surviving mutants (the changes your tests failed to catch). Arguments: pattern, class (mutate one class — recommended), timeout_seconds (per mutant, default 30), survivors_only (default true, returns just the actionable set).
  • query_graph — what a change to one class can reach: classes that reference it directly and transitively, what it references, and related tests. Where a coverage map exists, the tests that actually executed it come back separately, plus any that no syntactic path reaches. Argument: class. Returns limits, which agents must read — this is reachability for navigation, not a set of affected tests, and metadata-dispatched callers are invisible to it.
  • explain_failure — explain one failure in structured form: exception type, message, source location, assertion operands, and the SOQL/DML tail before it failed, plus what the explanation could not establish. Arguments: test (Class.method), redact (default false). Answers from the previous run when it covered that test, otherwise runs just that test. Returns the same nimbus.explain/v1 payload as nimbus explain --json, so agent and human never diverge.

Operator visibility. The server logs each tool call to stderr in structured form (mcp.run_apex_tests.start, ...complete, ...timeout, ...failed) so you can tail the agent's MCP transport and see exactly what's happening. --verbose raises the log level to debug.

Register with Claude Code:

bash
claude mcp add nimbus -- nimbus mcp

Agentic Resource Discovery. Nimbus publishes an ARD catalog so compatible registries and coding agents can discover this MCP server from task intent. ARD only handles discovery: after selecting Nimbus, the client still installs the local binary and invokes nimbus mcp over stdio.

Stdio safety. Stdout is reserved for JSON-RPC framing. The runner writes nothing to stdout, and the process-global os.Stdout is rerouted to stderr for the server's lifetime so any stray prints elsewhere in the call tree can't corrupt the protocol stream. Use stderr for any out-of-band logging; --verbose is safe.

FlagDefaultDescription
--parallel0Worker count for run_apex_tests (0 = NumCPU). Set at server start; cannot be changed per call.
--coveragetrueCollect coverage so get_coverage works after every run. Set false in tight agent loops that never call get_coverage.

Recommended skills

MCP exposes the primitives; skills are short playbooks that tell an agent when and how to use them. Install with nimbus skills install — the binary fetches from nimbus-skills and writes to the right path for your agent. Three skills to start:

  • fix-failing-apex-test — the inner loop. Read failure → narrow → edit → re-run, until green.
  • bootstrap-nimbus — set Nimbus up on a fresh SFDX project, including a CI snippet.
  • apex-coverage-uplift — raise coverage by writing targeted tests for uncovered branches.

nimbus skills

Install agent skills from the nimbus-skills repo. Skills are short, opinionated playbooks (one per workflow) that pair with MCP to give your agent both the primitives and the recipes to use them. The binary embeds no skill content — every install fetches from GitHub at runtime, so the skills repo evolves independently of nimbus releases.

Subcommands:

bash
nimbus skills list                    # available + which are installed locally
nimbus skills install fix-failing-apex-test
nimbus skills install all             # install every skill the agent supports
nimbus skills remove fix-failing-apex-test
nimbus skills path                    # print the install dir for the detected agent

Agent detection. Run from your project root and Nimbus picks the right agent automatically:

  • .claude/ present → Claude Code, writes to .claude/skills/<name>/SKILL.md
  • .cursor/ present → Cursor, writes to .cursor/rules/<name>.mdc
  • .aider.conf.yml or CONVENTIONS.md present → Aider, writes a single CONVENTIONS.md bundle
  • AGENTS.md or opencode.json present → generic AGENTS.md (also covers OpenCode), writes a single AGENTS.md bundle
  • .kiro/ present → Kiro, writes to .kiro/steering/<name>.md with manual-inclusion frontmatter (load via #<name> in your Kiro prompt)
  • Nothing matched → defaults to Claude Code (use --global to write to ~/.claude/skills/ instead).

If two markers are present, Nimbus refuses to guess — pass --agent to disambiguate.

Idempotent. Re-running install on a skill whose local copy is byte-identical to upstream is a no-op. If the local copy differs, the install fails until you pass --force — protects hand edits.

FlagDefaultDescription
--agentautoTarget agent: claude-code, cursor, aider, agents-md, kiro
--globalfalseClaude Code only: install into ~/.claude/skills/ instead of <project>/.claude/skills/
--forcefalseOverwrite an existing local copy when its content differs

nimbus sync

Sync your project's schema (SObject definitions, fields, relationships) into the local database. nimbus test reconciles the schema automatically before every run, so an explicit sync is mainly for warming the database up front or after large metadata changes.

Sync is incremental by default: it creates missing tables and adds missing columns without dropping anything, so a re-sync with no changes is near-instant even on orgs with hundreds of objects. Use --rebuild to force a full drop-and-recreate (the only mode that applies column type changes — it also wipes table data). Use -s to scope work to specific objects while iterating.

bash
nimbus sync                                        # Incremental sync of all objects
nimbus sync -s Account,Contact,Lead                # Sync only these objects
nimbus sync --rebuild                              # Full drop-and-recreate of all tables
nimbus sync -s My_Object__c --rebuild              # Rebuild just one object
FlagDefaultDescription
-s, --sobjects-Comma-separated list of objects to sync (others are left untouched)
--rebuildfalseDrop and recreate tables instead of incrementally reconciling. Applies column type changes; wipes table data.
-o, --org-Target Salesforce org alias

nimbus init

Initialize Nimbus for the current project. Creates the .nimbus/ directory and starts the embedded database. Only needs to be run once per project.

bash
# Run from your project root (where sfdx-project.json is)
nimbus init

After init, run nimbus sync -o <org> to populate the schema, then nimbus test.

nimbus new

Scaffold a new Apex class or trigger (and its -meta.xml) into your project's default package — the SFDX main/default/classes (or /triggers) convention. The API version comes from sourceApiVersion in sfdx-project.json.

bash
nimbus new test AccountServiceTest      # @IsTest class with a starter method
nimbus new queueable SyncContactsJob    # Queueable implementation
nimbus new batch NightlyRollup          # Database.Batchable<SObject>
nimbus new trigger AccountTrigger --sobject Account

Kinds: class, test, trigger, triggerhandler, batch, queueable, schedulable.

FlagDefaultDescription
--sobjectSObject for a trigger (required for the trigger kind).
--dirTarget directory (default: the package's main/default/classes or /triggers).
--api-versionsourceApiVersionAPI version written to the -meta.xml (falls back to 62.0).
--forcefalseOverwrite an existing file.

nimbus daemon Pro

The daemon is a long-lived background process that pre-parses your entire codebase, loads flows, record types, custom metadata, labels, and validation rules into memory at startup. Every subsequent test run — CLI or VS Code — connects to the already-warm daemon and starts executing immediately.

On a project with ~1,000 Apex files, cold-start parsing alone takes 10–15 seconds per run. With the daemon running, that startup cost drops to near zero.

See the daemon overview page for a full explanation of how the warm-up works and what stays in memory.

bash
nimbus daemon start          # Start the daemon (detaches to background)
nimbus daemon start --clean  # Start with a fresh database
nimbus daemon status         # Show uptime, files loaded, connection count
nimbus daemon list           # List running daemons across all projects
nimbus daemon stop           # Stop the daemon for the current project
nimbus daemon stop --all     # Stop every running daemon across all projects

Flags

FlagDefaultDescription
--cleanfalseDrop and recreate the database before starting
--foregroundfalseRun in foreground instead of detaching (useful for debugging)

VS Code integration

The VS Code extension starts the daemon automatically when it detects a Pro license. You do not need to run nimbus daemon start manually. The status bar shows a Nimbus indicator — if it shows a slash, run Nimbus: Restart Daemon from the command palette.

Free-tier users can still run tests from the CLI with full coverage and governor limit support. The daemon and live editor integration (inline results, trace viewer, coverage gutters, history) require Pro.

nimbus login

Activate Nimbus on this machine via your browser. No license keys to copy or paste — sign in with the same account you use in the portal and the CLI registers itself.

bash
nimbus login       # Open the browser, sign in, register this machine
nimbus logout      # Deactivate this machine and free up a seat
nimbus whoami      # Show the current account and license tier

Licenses are validated online and cached locally; a 7-day offline grace period is built in for outages and travel.

Headless / CI: set NIMBUS_LICENSE_KEY in the environment instead of running nimbus login. Team plans include a dedicated CI runner license with its own machine pool.

nimbus status

Show the current Nimbus status - database state, project info, daemon status, and license info at a glance.

bash
nimbus status

nimbus doctor

Run diagnostic checks on the current Salesforce project. Reports setup problems - missing configuration, no synced schemas, broken database, unknown config keys - with a concrete fix for each issue. Think brew doctor or gh auth status.

bash
nimbus doctor           # Run all checks
nimbus doctor --verbose # Show fix details for all checks, not just failures
nimbus doctor --json    # Output results as JSON

Checks

Check nameWhat it validatesSeverity
nimbus.properties foundConfig file exists in the project rootFail
Config syntaxAll keys in nimbus.properties are recognizedWarn
Apex parse errorsEvery .cls and .trigger file parses without syntax errorsFail
Schema files foundSObject schemas synced into .nimbus/schemas/Warn
Schema coverageEvery custom object defined in source has a synced schemaWarn
Stubs directory existsstubs/ present for managed package stubsWarn
Stub gapsAll managed-package namespace references in source have stub coverageWarn
Field metadata validityCustom field XML passes Salesforce deploy-time rulesWarn
Permission set coveragePermission sets and groups your tests resolve by name are defined in source or synced from the orgWarn
Apex test classes found@isTest classes exist under project source directoriesFail
Database connectivityEmbedded PostgreSQL starts and accepts connectionsFail
License statusCurrent tier (Free / Pro / Team) and expiry — key never printedWarn
FlagDefaultDescription
--verbosefalseShow fix details for all checks, not just warnings/failures
--suitefalseAlso run the test suite and report failure clusters, largest cause first
--jsonfalseOutput results as JSON

Exit codes

CodeMeaning
0All checks passed or warned - no hard failures
1One or more checks failed

CI usage

Gate your pipeline on a clean setup before running tests:

bash
nimbus doctor && nimbus test

nimbus orgs

List Salesforce orgs authenticated via SF CLI that Nimbus can use for sync and fallback operations.

bash
nimbus orgs

nimbus schema

Open a visual schema explorer showing synced SObjects, their fields, relationships, and field types. Useful for verifying your local database matches your org metadata.

bash
nimbus schema

nimbus cache

Manage the parsed AST cache. Nimbus caches parsed Apex classes to speed up subsequent test runs.

bash
nimbus cache stats   # Show cache hit rate, size, entry count
nimbus cache clear   # Clear all cached ASTs

nimbus config

Manage Nimbus configuration via the nimbus.properties file.

bash
nimbus config show         # Display current effective configuration
nimbus config init         # Create an example nimbus.properties file
nimbus config properties   # List all available configuration properties

nimbus reset

Reset the .nimbus/ directory for the current project - clears the database, AST cache, and trace files. Useful when things get into a bad state.

bash
nimbus reset

nimbus db

Manage the embedded PostgreSQL database directly.

bash
nimbus db status   # Show database status (running, port, data size)
nimbus db start    # Start the embedded database
nimbus db stop     # Stop the embedded database
nimbus db reset    # Reset the database (equivalent to nimbus reset)

nimbus stub

Scaffold and inspect stubs/ — the directory Nimbus loads before the main source tree so tests can reference managed-package classes that aren't in the project. See also the User Stubs / Managed Packages section.

List existing stubs

bash
nimbus stub list    # Show every .cls under stubs/
nimbus stub path    # Print the absolute stubs/ directory

Scaffold a new stub class

bash
# Minimal stub with an invoke() no-op
nimbus stub add Logger

# Nebula-style stub with specific method signatures
nimbus stub add Logger \
  --method info:void \
  --method error:void \
  --method saveLog:void

# Typed arg lists (emits "public static void info(String arg0)")
nimbus stub add Logger --method 'info(String):void'

# Replace an existing stub
nimbus stub add Logger --method debug:Boolean --force

Each generated class exposes public static Integer callCount and public static List<String> calls so tests can assert on how the stub was exercised. Edit the file freely — it's plain Apex.

Namespace-scoped stubs

Managed-package classes accessed as ns.ClassName (e.g. mp.Logger.info('hi')) need both a simple class name and the ns. prefix to resolve. Nimbus writes namespace stubs to a subdirectory matching the namespace, and the runner registers both lookup keys automatically.

bash
# Either form is accepted — the dot is treated as a namespace prefix
nimbus stub add mp.Logger --method 'info(String):void'
nimbus stub add Logger --namespace mp --method 'info(String):void'

# Writes: stubs/mp/Logger.cls (class body still says "public class Logger")
# Test code can call: mp.Logger.info('hi');

If you prefer to hand-author, just put the file under stubs/<ns>/<Class>.cls. The first-level subdirectory is the namespace; deeper nesting is ignored.

Remove a stub

bash
nimbus stub remove Logger

Auto-generate stubs from project source Pro

nimbus stub auto walks every Apex source file, finds references to classes Nimbus can't resolve (managed packages, missing project files), and writes one .cls per class — methods, constructors, and fields scaffolded from how your code actually uses them. Return types are inferred from assignment LHS, return statements, casts, logical operators (Boolean), and string concatenation (String). Generic type arguments (List<MyType>) are preserved.

bash
# Preview what would be written
nimbus stub auto --dry-run

# Generate (skips existing files; safe to re-run)
nimbus stub auto

# Re-run after adding new project usage
nimbus stub auto --merge        # appends new methods, preserves hand edits
nimbus stub auto --force        # full rewrite (discards hand edits)

Or fold it into the test loop:

bash
nimbus test --write-stubs                       # generates after a green run
nimbus test --write-stubs --write-stubs-merge   # appends to existing stubs
nimbus test --write-stubs --write-stubs-force   # rewrites existing stubs

Layout. Auto-stub writes one file per class under the namespace folder — stubs/Nebula/Logger.cls with public class Logger { ... }. The stub loader registers both Logger and Nebula.Logger as aliases, so test code can call Nebula.Logger.info() the same way it would against the installed package. The hand-written nested-class convention (stubs/Nebula/Nebula.cls with Logger as an inner class) is equally valid; both resolve identically at runtime.

What's not inferred. Parameter names aren't carried by call sites — generated stubs use arg0, arg1. Method bodies are an audit-friendly default: callCount++ and calls.add('methodName'), returning a type-default. Hand-edit either after generation; --merge on subsequent runs leaves your edits alone.

See the User Stubs page for the full workflow including dual-layout details.

nimbus upgrade

Check for a newer version of nimbus and upgrade the binary in-place.

bash
nimbus upgrade          # Download and install the latest version
nimbus upgrade --force  # Re-install even if already on the latest version

Nimbus also prints a one-line notice after every command when a newer version is available. Set NIMBUS_NO_UPDATE_CHECK=1 to disable this check.

Configuration

Global Flags

These flags work with every command:

FlagDefaultDescription
-o, --org-Target Salesforce org alias (passed to SF CLI for sync/fallback)
-v, --verbosefalseVerbose output - useful for debugging
-h, --help-Help for any command
--version-Print version and exit

Database Options

By default, Nimbus uses an embedded PostgreSQL instance in .nimbus/db/. For CI/CD or advanced setups, you can point at an external database.

FlagDefaultDescription
--db-providerembeddedDatabase provider: embedded, external
--db-url-Full PostgreSQL connection string (overrides provider)
--db-dir.nimbus/dbEmbedded Postgres data directory
--db-name-Database name override
--db-user-Database user override
--db-password-Database password override

External database

bash
# Use a Neon, RDS, or any managed Postgres
nimbus test --db-url "postgresql://user:pass@host:5432/mydb"

CI with external DB

bash
# In CI: use a service container or Neon ephemeral branch
nimbus test --db-url "$NIMBUS_DB_URL" --coverage --coverage-output json

Platform Fidelity Mode

Nimbus runs a compat validator pass against every program it loads. Inner-class restrictions, missing master-detail parents, exhausted governor limits, and similar rules surface as diagnostics tagged with a compat rule ID.

nimbus.compat.mode decides how diagnostics surface.

FlagDefaultDescription
strict(default)Emit diagnostics as errors and fail the test run with a non-zero exit code. Intended for CI.
warn-Print diagnostics to stderr but keep the run passing. Useful for local dev on a project that can’t close every gap at once.
off-Suppress diagnostics entirely. Opt-out; not recommended.
bash
# nimbus.properties
nimbus.compat.mode=strict

# Relaxed for day-to-day dev, strict in CI:
%dev.nimbus.compat.mode=warn
%ci.nimbus.compat.mode=strict

Governor Limits

Nimbus enforces Salesforce governor limits during execution and exposes them through the Apex Limits class. nimbus.governor.modecontrols how a limit breach is handled.

FlagDefaultDescription
strict(default)Throw System.LimitException when a limit is exceeded, matching the platform.
warn-Print a warning the first time a limit is exceeded but keep executing.
off-Do not enforce limits. Counters still accumulate for the Limits class.

Limits follow the platform by execution context. Synchronous code gets 100 SOQL queries; asynchronous code (Queueable, @future, Batch) gets 200. CPU time and heap likewise rise in async contexts. Both enforcement and the Limits class observe the context-appropriate value.

Orgs with Salesforce-raised limits can raise the ceilings to match. An explicit override is authoritative and applies in every context — it drives both enforcement and the Limits.getLimitQueries() / Limits.getLimitDmlStatements() return values. Leave a key unset to keep the context-aware platform default.

FlagDefaultDescription
nimbus.governor.modestrictEnforcement mode: strict | warn | off
nimbus.governor.soql-queries100 / 200Override SOQL queries per transaction. Unset: 100 sync, 200 async.
nimbus.governor.dml-statements150Override DML statements per transaction
nimbus.governor.heap-size12000000Override heap size in bytes
bash
# nimbus.properties — match an org with Salesforce-raised limits
nimbus.governor.mode=strict
nimbus.governor.soql-queries=300
nimbus.governor.dml-statements=300

Parallel Isolation

Every test runs inside its own Postgres transaction that rolls back at the end, so per-test state is always clean. When running in parallel, however, all workers share the same public schema by default and can hit row-level lock contention on hot tables.

Opt in to per-worker-schema to give each parallel worker its own cloned schema. On setup Nimbus replicates every public table (structure + seed rows) into a worker_N schema; every worker pins its transactions’ search_path there so row writes, locks, and visibility are physically confined per worker.

FlagDefaultDescription
shared(default)All workers share the public schema. Per-test transaction rollback still isolates state.
per-worker-schema-Each worker gets its own Postgres schema cloned from public. Adds one-time clone cost at startup; eliminates cross-worker lock contention.
bash
# nimbus.properties
nimbus.test.isolation=per-worker-schema

# Typical pattern: shared locally for speed, isolated in CI for safety
%ci.nimbus.test.isolation=per-worker-schema

Org Simulation

Nimbus simulates the running org via UserInfo and the singleton Organization SOQL row. These values default to en_US / USD / Developer Edition / not-sandbox, which works for most tests. When your code branches on locale, currency, sandbox flag, or org features (Multi-Currency, Person Accounts), set the matching key in nimbus.properties or pass the equivalent CLI flag.

FlagDefaultDescription
nimbus.org.currencyUSDReturn value for UserInfo.getDefaultCurrency().
nimbus.org.localeen_USReturn value for UserInfo.getLocale(). Also set as languageLocaleKey on Organization.
nimbus.org.language(falls back to locale)Return value for UserInfo.getLanguage().
nimbus.org.timezoneAmerica/Los_AngelesOrganization.TimeZoneSidKey and UserInfo.getTimeZone() display name.
nimbus.org.sandboxfalseReturn value for Organization.IsSandbox. Toggle when your code branches on sandbox vs production.
nimbus.org.features(none)Comma-separated feature toggles. MultiCurrency enables UserInfo.isMultiCurrencyOrganization()=true; PersonAccounts is reserved for future schema-level behaviour.
nimbus.org.faketime(real clock)ISO-8601 instant that pins Date.today() / DateTime.now() / System.now() for deterministic time-dependent tests.
bash
# nimbus.properties
nimbus.org.currency=EUR
nimbus.org.locale=de_DE
nimbus.org.sandbox=true
nimbus.org.features=MultiCurrency
nimbus.org.faketime=2030-01-15T12:00:00Z

Equivalent CLI flags on nimbus test override the config file when both are set: --sandbox, --feature MultiCurrency,PersonAccounts, --faketime 2030-01-15T12:00:00Z.

Permission Seam Visibility

Nimbus runs tests as a sysadmin-equivalent default user. Outside System.runAs(), FLS checks return true, WITH SECURITY_ENFORCED passes through, and $Permission.X / FeatureManagement.checkPermission() resolve against the default mock user (005000000000000AAA). This is faithful to Salesforce's "tests run as the configured user" model — but it creates a silent divergence: a test can pass under Nimbus and fail in a real org when the org's running user isn't assigned the perm.

Permission seams make every such case visible per-test. When a passing test consulted a permission outside a runAs block, the runner records a seam: the kind of check (FLS, object perm, SOQL user-mode, custom permission), the subject, and how it resolved (default-allow for FLS/object/SOQL; default-mock-user for custom perms granted via seeded nimbus.mock.permission-sets or test-inserted PSAs). Custom-permission seams use a distinct CUSTOM PERM prefix because they look like business logic, not security, in the test code — the most damaging variant of the divergence.

By default the runner shows a single end-of-run summary line with totals. --show-permission-seams expands it to a per-test list. --strict-permissions fails any test whose passing path consulted a permission outside runAs — opt-in for CI users who want to enforce the discipline.

bash
# Default summary (no flags) — single line under the test summary block:
# Permission seams: 12 tests consulted permissions outside System.runAs()
#   8 custom permission checks ($Permission / FeatureManagement.checkPermission)
#   4 FLS / object / SOQL user-mode checks

# Per-test detail:
nimbus test --show-permission-seams

# CI gate — fail tests with seams:
nimbus test --strict-permissions

# nimbus.properties (committable, profile-aware):
nimbus.test.permission-seams.show=true
%ci.nimbus.test.permission-seams.strict=true

Tests that explicitly use System.runAs() produce zero seams — the test author opted in to an explicit identity, so any divergence is by design. The fix when strict mode flags a test is to wrap the assertion in System.runAs(user) where user has the required PSA.

Under --json, a run that crossed at least one seam adds a top-level permission_seams block — { total, customPermission, flsObject, seams: [{ kind, subject, path, file?, line?, custom }] } — so agents and the release gate can read the divergence without scraping human output. It is omitted entirely on a clean run (zero seams). This is the block a release profile's permissionSeamPolicy reads to block a release that passed only through the default-allow path.

Read-only Mode

Refuse any DML inside tests via --readonly. Useful for CI stages that should only observe — e.g. post-merge sanity checks that re-run the suite against frozen data. When enabled, any insert, update, delete, orupsert statement inside a test throws System.DmlException with a diagnostic message.

bash
# nimbus.properties
nimbus.test.readonly=true

# Or per-profile:
%ci.nimbus.test.readonly=true

# Or as a one-off CLI flag:
# nimbus test --readonly

Setup-time DML (seeding profiles, list views, custom setting records) still succeeds — only DML inside an @IsTest method is rejected.