NukeBase

Starting the Database

Start the NukeBase server with configuration options by calling startDB() once at the end of your configuration:

Starting the database
// Basic setup - pass the domain object to startDB
const nukebase = addDomain({
  authPath: ["users"],
  host: "127.0.0.1", // optional
  port: 3000 // optional
});

startDB(nukebase);

addDomain Configuration Options:

  • authPath: Array - path to user authentication data (e.g., ["users"])
  • host: String (optional) - the IP address to bind to
    • Use "127.0.0.1" to accept connections only from the local machine (default)
    • Use a specific IP address like "126.23.45.1" to bind to that server address
    • Use "0.0.0.0" to accept connections from any IP
  • port: Number (optional) - the port to listen on (default: 3000)

Environment Variables

The server reads these from the environment. Every one has a working default — you normally set none of them.

Deployment

VariableDefaultEffect
DOMAIN—Public origin used to build magic-link URLs and console output (e.g. https://your-app.nukebase.com).
SOCKET—If set, the server listens on this Unix domain socket instead of host/port (useful behind nginx/Caddy). The socket file is created, chmoded to 777, and replaced if it already exists.
TRUST_PROXYautoPeer-only override. See the note below — you almost certainly do not need this.
DB_ALLOW_MISSING_SPLITS0Set to "1" to downgrade a $split marker whose subdirectory (or its data.json) is missing or empty from a fatal startup error to a logged warning, treating the subtree as empty. A data.json that does not parse is always fatal. Default is fail-fast so a bad disk state can't silently produce data loss on the next flush.
BACKUP_KEEP_DAYS7How many days of daily backups to keep under server/backup/. Dated directories older than this are removed after each successful backup; nothing else in that directory is touched. 0 keeps every backup forever.

The client IP is worked out from where the connection came from — there is nothing to configure. A forwarding header (x-real-ip, then x-forwarded-for) is honored only when the peer could plausibly be a proxy you put there:

  • Unix socket — no peer address exists, so the header is the only source of a client IP. This is how the managed platform runs.
  • Loopback or private address (127.0.0.0/8, ::1, RFC1918, fc00::/7, fe80::/10) — a local proxy. The header is used if present, otherwise the peer.
  • Public address — the open internet. The peer address wins and the header is ignored, because a caller on a public port can set and rotate that header themselves.

This replaced an earlier TRUST_PROXY flag that had exactly one correct value per deployment and no way for anyone to know which. Setting TRUST_PROXY="false" or "0" still forces the peer address unconditionally — useful only for a topology that answers the question wrongly, such as a reverse proxy on another host reaching the engine over a public address. Any other value, including "true", has no effect.

On the hosted platform

A hosted project's process is started by the platform, so its environment has to come from the platform too. Keep a .env file in your project root — the same file node --env-file=.env server/database.js loads locally — and push it separately from your code:

Hosted environment
nukebase env push          # make the hosted environment equal to ./.env, then restart
nukebase env list          # variable names currently set (add --show to print values)

Every variable in the file lands in process.env — your own settings and any of the engine tunables above. env push replaces the whole set, so removing a line locally and pushing removes it on the server. .env is never carried by an ordinary push or pull; on the server it is kept outside the project directory, root-owned, where no sync, ZIP download or restore point can reach it. SOCKET and DOMAIN are set by the platform and are refused. Values are single-line and up to 8 KB each; encode anything multi-line, such as a PEM key, before putting it in the file.

Files

VariableDefaultEffect
FILES_DIRserver/filesRoot of the file store. Nothing outside it is reachable.
FILE_MAX_BYTES67108864 (64 MB)Hard ceiling on a single file, enforced on the bytes that actually arrive.
FILE_MAX_DIR_ENTRIES10000Entries returned by one directory listing before it is truncated.
FILE_WRITE_RATE_MAX120Uploads + deletes per IP per minute.

Limits and capacity

VariableDefaultEffect
WS_RATE_LIMIT_MAX10000WebSocket messages per second per connection. Exceeding it sends a rateLimit notice and closes the socket. Also caps { durable: true } writes per session per second. Raise it for bulk loaders.
HTTP_MAX_BODY_BYTES1048576 (1 MB)Default body cap for postWithBody handlers. Override per route with { maxBytes }.
QUERY_MAX_RESULTS1000000Maximum records one query may return before it is truncated.
QUERYSUB_MAX_RESULTS1000Matching records above which a querySub is refused or dropped.
WINDOW_SUB_SCAN_MAX1000Collection size above which a windowed subscription requires an index-order plan.
SPLIT_THRESHOLD_MB128Size in MB above which a data.json is split into per-key subdirectories. A flush re-serializes each dirty file in full, so this also bounds how long one flush can stall the engine (roughly 2.7 ms per MB). Lower it to trade more, smaller files for shorter stalls.
FLUSH_CACHE_MB256Memory budget for the flush cache, which keeps the serialized text of each top-level key so a flush only re-serializes keys written since the last one. Costs about the on-disk size of the cached files. Once full, further keys are simply re-serialized as before. 0 disables it.
MAX_QUERY_BUCKETS_PER_PATH64Distinct query-subscription windows allowed on one path/event/action.
MAX_TOTAL_INDEX_ENTRIES8000000Total index entries held across the whole process.
KEY_INDEX_MIN_RECORDS1000Collection size at which an automatic $key index becomes worth building.

Graceful shutdown is automatic. On SIGTERM or SIGINT the server clears its flush timer, writes every dirty subtree to disk, and exits — so a normal restart never loses the last few seconds of writes. It exits with process.exitCode (0 unless you set it): to flush and report a failure — from an uncaughtException handler, say — set process.exitCode = 1 before sending yourself SIGTERM, so a supervisor can tell a crash from a deliberate restart.

Important: Call startDB() only once at the end of your server configuration. This function initializes and starts the database server with all configured domains and settings. Multiple calls to startDB() can cause resource conflicts and unexpected behavior. Once you've defined all your domains, middleware, triggers, and functions, finish with a single call to startDB() to launch your server.
↑