Nexlem environment variables
Overview
Nexlem reads several NEXLEM_* environment variables to redirect filesystem reads
and writes (SQLite DB, campaigns directory, consumer config directory) and to
locate the framework checkout. Specific path variables override the generic
NEXLEM_PROJECT_DIR parent fallback, which in turn overrides the caller's
projectDir argument (process.cwd() at the entry point).
This document is the single source of truth for env-var semantics in
src/commands/run-paths.ts and src/lib/env.ts. Consumers (UAT scripts,
headless claude -p workers, downstream orchestrators) MUST follow the
precedence rules below.
Variables
| Name | Type | Purpose | Default | Required |
|---|---|---|---|---|
NEXLEM_PROJECT_DIR | absolute or relative path | Parent fallback: when set, DB/campaigns/config resolve under this dir | (none — falls back to process.cwd()) | No |
NEXLEM_DB | absolute path | SQLite database file location | $NEXLEM_PROJECT_DIR/.nexlem/nexlem.db | No |
NEXLEM_CAMPAIGNS_DIR | absolute path | Campaigns base directory | $NEXLEM_PROJECT_DIR/campaigns | No |
NEXLEM_CONFIG_DIR | absolute path | Consumer config directory (config/) | $NEXLEM_PROJECT_DIR/config | No |
NEXLEM_FRAMEWORK_PATH | absolute path | Override framework root (managed by Phase 49 / GLOBAL-04) | Auto-detected from import.meta.url | No |
Defaults shown for the path variables collapse to <projectDir>/.nexlem/nexlem.db,
<projectDir>/campaigns, and <projectDir>/config when NEXLEM_PROJECT_DIR is
also unset (here <projectDir> is the argument passed to the resolver, typically
process.cwd() at the entry point — see resolveProjectDir()).
NEXLEM_FRAMEWORK_PATH is owned by Phase 49 / GLOBAL-04 (framework-path
resolution); it is listed here for completeness but is NOT modified by HEADLESS-05.
Precedence rules
For each path category, the resolvers evaluate sources in this order and return the first one that is set:
- DB (
resolveDbPath):NEXLEM_DB→NEXLEM_PROJECT_DIR/.nexlem/nexlem.db→<projectDir>/.nexlem/nexlem.db - Campaigns dir (
resolveCampaignsBaseDir):NEXLEM_CAMPAIGNS_DIR→NEXLEM_PROJECT_DIR/campaigns→<projectDir>/campaigns - Config dir (
resolveConfigDir):NEXLEM_CONFIG_DIR→NEXLEM_PROJECT_DIR/config→<projectDir>/config - Project root (
resolveProjectDir): explicit caller arg →NEXLEM_PROJECT_DIR→process.cwd()
The CLI flag --project-dir (HEADLESS-04) overrides ALL env-derived projectDir
values when present — it becomes the explicit caller arg passed through
resolveProjectDir() and downstream resolvers. Specific env vars
(NEXLEM_DB / NEXLEM_CAMPAIGNS_DIR / NEXLEM_CONFIG_DIR) still take
precedence over the CLI flag because they target a single path, not a parent
root — this matches the "specific overrides general" principle.
Note (HEADLESS-05): resolveCampaignDir(projectDir, configPath) (the
campaign-specific lookup that re-roots a stored campaign.config_path) is
intentionally NOT extended to honor NEXLEM_PROJECT_DIR. That value is a
persisted DB field whose anchor must remain stable across env-var changes; see
the JSDoc in src/commands/run-paths.ts for the full rationale.
Worked examples
Case A — pure parent fallback. Only NEXLEM_PROJECT_DIR is set:
NEXLEM_PROJECT_DIR=/tmp/test bun run src/main.ts campaign list
# DB: /tmp/test/.nexlem/nexlem.db
# Campaigns: /tmp/test/campaigns
# Config: /tmp/test/configCase B — specific override wins over parent. Both NEXLEM_PROJECT_DIR and
NEXLEM_DB are set; NEXLEM_DB wins for the DB, the rest still inherit from
NEXLEM_PROJECT_DIR:
NEXLEM_PROJECT_DIR=/tmp/test NEXLEM_DB=/tmp/override.db \
bun run src/main.ts campaign list
# DB: /tmp/override.db (NEXLEM_DB wins — specific)
# Campaigns: /tmp/test/campaigns (inherited from NEXLEM_PROJECT_DIR)
# Config: /tmp/test/config (inherited from NEXLEM_PROJECT_DIR)Case C — CLI flag wins for project root. --project-dir overrides the env
var:
bun run src/main.ts campaign list --project-dir /tmp/test
# DB: /tmp/test/.nexlem/nexlem.db
# Campaigns: /tmp/test/campaigns
# Config: /tmp/test/configRelative vs absolute paths (asymmetry)
NEXLEM_PROJECT_DIR (and the other path env vars) accept both absolute and
relative paths. Relative paths are resolved against the current working
directory at the moment the resolver is invoked (Node's path.join semantics).
The --project-dir CLI flag (HEADLESS-04) is stricter: it rejects relative
paths at parse time and exits non-zero. This is a defensive measure against
argv injection in headless wrappers, where untrusted callers may construct
argv strings programmatically.
Rationale. Env vars are set at shell level by the user, where interactive
ergonomics favor leniency — typing NEXLEM_PROJECT_DIR=./sandbox from a working
directory you control is a reasonable affordance. CLI argv may be constructed
by automated wrappers where strictness is safer — a relative --project-dir
value injected by an upstream tool could expand to an unexpected location
depending on the worker's spawn cwd.
Operational guidance: in headless contexts (claude -p workers, CI agents,
automated batches) always pass absolute paths via --project-dir. The
env-var leniency is a developer-ergonomics affordance for interactive use and
should NOT be relied on in production worker harnesses.
Forward-compatibility note
v2.4 headless invocations set NEXLEM_PROJECT_DIR once per spawn; specific env
vars remain available for UAT isolation and per-path overrides (e.g. pointing
the DB at a temporary location while keeping campaigns/config on the project
root).
Traceability
Implemented under HEADLESS-05 (Phase 50). Resolver source:
src/commands/run-paths.ts. Tests: src/commands/run-paths.test.ts (plus the
four existing call-site regressions in tests/unit/run-paths-resolve-campaign-dir.test.ts,
tests/unit/run-nexlem-db-override.test.ts, tests/unit/run-config-dir-override.test.ts,
and tests/integration/config-path-env-override.test.ts).
NEXLEM_FRAMEWORK_PATH schema and runtime resolution: Phase 49 / GLOBAL-04
(src/lib/env.ts and src/lib/framework-path.ts). Not modified by this plan.