Skip to content

Backend configuration

Every setting is an environment variable. The backend also reads a .env file in the directory it starts in; a variable set in the environment wins over the file.

General

Variable Type Default What it does
PARLEML_DATA_ROOT path data_root (in the directory the backend starts in) On-disk layout root — every Workspace folder lives under here (DATA_ROOT/workspaces/<workspace_id>/).
PARLEML_CATALOG_PATH path unset SQLite catalog file path (derived index over the filesystem). Default sits next to the data root so a clean checkout boots cleanly.
PARLEML_CORS_ORIGINS JSON list of text ["http://localhost:5173"] CORS — wide-open in local dev, locked down in production.
PARLEML_MODE one of dev, test, prod dev Operating mode — drives a small number of defaults (e.g. catalog auto-rebuild on stale schema). Kept narrow so it doesn't sprawl.
PARLEML_AGENT_MAX_POINTS integer 2000 (≥ 1) Max points per series returned by the agent-facing chart "data mode" (?format=data). Caps trace arrays so a large scatter / time series doesn't blow up an agent's context window; the figure (UI) path is unaffected.
PARLEML_AGENT_MAX_ROWS integer 1000 (≥ 1) Row ceiling for the analyst SQL endpoint (POST /api/workspaces/{id}/query). Caps result sets so an LLM-authored SELECT can't pull an unbounded table into context; the body limit is clamped to this ceiling.
PARLEML_MAX_UPLOAD_MB integer 512 (≥ 1) Hard ceiling (MiB) for any single multipart upload (datasets, KB source files, BYO models). Uploads are buffered in memory before parsing, so an unbounded body is a trivial memory-exhaustion vector; a crafted zip-based workbook can also decompress far past its wire size, so this caps the compressed bytes we ever read.
PARLEML_SAMPLES_DIR path <repo>/sample_datasets (image: /opt/parleml-data/samples) The sample datasets Home's first-run gallery starts workspaces from: the repository's sample_datasets/ in a checkout; the delivery image ships them and points here. A sample whose file is missing is left out of the gallery, which hides itself when none is left.
PARLEML_WARM_IMPORTS true or false true Import the ML stack the read paths load lazily (UMAP and its numba kernels, the AutoGluon pipeline, the model families, SHAP) on a background thread at startup, so the first reads of a freshly started server — an agent's first get_capabilities / get_model_evaluation — do not outlive a client's timeout. Readiness never waits for it: /api/health answers at once. The test suite turns it off, so each app a test builds skips those imports.

Background jobs (subprocess execution)

Each long workload runs as python -m app.worker <job_id>; the parent enforces per-kind concurrency caps with a pending queue and a scheduler+reaper tick. See backend/README.md (Subprocess execution model).

Variable Type Default What it does
PARLEML_JOBS_HEAVY_CONCURRENCY integer 0 (≥ 0) Server-wide cap on heavy workers — experiment fits, explanation computes and batch scoring — running at once; further ones wait as queued (the experiment and explanation status say how many are ahead). Each fit uses every core it is given, so more at once only slows all of them and the API with them. 0 resolves to max(1, cpu//4) (see heavy_concurrency).
PARLEML_JOBS_MLI_CONCURRENCY integer 1 (≥ 1) MLI explanation worker slots: compute + on-demand narration share them. One by default — an explain of a large ensemble can take many GB.
PARLEML_JOBS_BATCH_CONCURRENCY integer 2 (≥ 1) Batch-predict slots (unkeyed; many jobs per experiment).
PARLEML_JOBS_PROBE_CONCURRENCY integer 2 (≥ 1) Signal Check probe slots (tabular + forecast). Each probe loads a sample and a torch foundation model; more than two at once competes for memory, not for answers.
PARLEML_JOBS_BUILD_CONCURRENCY integer 2 (≥ 1) Build slots: a dataset's EDA profile or leakage scan, and an experiment's model card (AutoDoc). Short jobs with their own slots, so they never wait behind a long fit.
PARLEML_JOBS_CANCEL_GRACE_S
PARLEML_JOBS_KILL_GRACE_S
number
number
10.0 (≥ 0)
30.0 (≥ 0)
Cancel escalation: CANCEL sentinel -> after cancel_grace SIGTERM -> after kill_grace SIGKILL, driven by the reaper off the persisted pid so it survives a parent restart.
PARLEML_JOBS_REAPER_INTERVAL_S number 2.0 (> 0) How often the parent scheduler+reaper tick runs.
PARLEML_JOBS_RETENTION_HOURS integer 24 (≥ 1) Terminal job rows older than this are pruned by the reaper (the console keeps recent history; the table stays bounded).
PARLEML_JOBS_WORKER_LOG_MAX_BYTES integer 64 * 1024 * 1024 (≥ 1024) Hard ceiling on a run's worker.log. That file captures the entire worker stdout + stderr (AutoGluon / joblib / loky chatter), has no rotation of its own, lives under data_root forever, and is streamed whole by the download endpoint — a long fit can otherwise write hundreds of MB per run. Checked at spawn and on every reaper tick; past the ceiling the file is truncated and a marker line is written (see app.services.job_launcher.cap_worker_log), so the Console tab shows recent output rather than nothing. Floored at 1 KiB — the marker line alone is ~120 bytes, so a smaller cap would re-truncate every tick.
PARLEML_JOBS_HEARTBEAT_INTERVAL_S number 10.0 (> 0) Stall detection (see app.services.worker_watchdog). A worker's watchdog thread checks this often whether its job made progress — its processes used CPU, or its main thread's stack moved — and touches the job's worker.heartbeat when it did.
PARLEML_JOBS_STACK_DUMP_AFTER_S number 300.0 (> 0) A job that made no progress this long has every thread's stack written to its worker.log, and again each time this passes while it stays stuck; a healthy job's log carries none. Shorter than the stall limit, so a job the reaper fails as stalled always leaves its stacks behind.
PARLEML_JOBS_STALL_AFTER_S number 900.0 (> 0) A running job whose last heartbeat is older than this is failed as stalled and its process tree stopped; its waiters get the failure. Must exceed the longest silent wait a job legitimately makes (an LLM call's own timeout, a model download on a slow link).

Authentication / SSO (OIDC relying-party)

All off by default so single-tenant local dev keeps working without credentials.

Variable Type Default What it does
PARLEML_AUTH_REQUIRED true or false false The one authentication gate (and break-glass toggle). When true, every /api and /mcp request (except GET /api/health, the login flow and the OpenAPI docs) needs a session cookie or a bearer API token. Without oidc_issuer nobody can sign in, so only tokens minted before the gate was turned on get through (a headless, token-only server). When false the API is open: local dev, or emergency recovery while SSO is fixed.
PARLEML_SESSION_SECRET text unset HMAC secret for the stateless session JWT. When unset we derive a stable key from the data-root .secret_key (see services.sessions), so sessions survive a restart without any extra configuration.
PARLEML_SESSION_TTL_HOURS integer 4 (≥ 1) Session lifetime. Short by default to bound the window after an IdP deprovision (revocation = IdP deprovision + TTL); the cookie is sliding-renewed while the user stays active.
PARLEML_OIDC_ISSUER
PARLEML_OIDC_CLIENT_ID
PARLEML_OIDC_CLIENT_SECRET
PARLEML_OIDC_REDIRECT_URI
PARLEML_OIDC_SCOPES
text
text
text
text
text
unset
unset
unset
unset
openid email profile
OIDC relying-party config. Presence of oidc_issuer enables SSO; discovery (<issuer>/.well-known/openid-configuration) yields the JWKS + endpoints, so one code path serves Entra / Okta / Google / Ping.
PARLEML_POST_LOGIN_REDIRECT text / Where to send the browser after a successful login. In dev the SPA is served separately (:5173) from the API (:8000), so set this to the SPA origin; in prod the SPA is same-origin and / is correct.
PARLEML_OIDC_RP_LOGOUT true or false false RP-initiated logout (OIDC): when true, signing out of parleml also ends the IdP SSO session (the browser is bounced through the issuer's end_session_endpoint) so the next login re-prompts. Default off — most SSO deployments expect the IdP session to persist across apps.
PARLEML_OIDC_POST_LOGOUT_REDIRECT text unset Where the IdP returns the browser after RP-initiated logout. Must be a post-logout redirect URI registered with the IdP. Falls back to post_login_redirect (the SPA origin) when unset.
PARLEML_OPEN_MODE_AGENT_ADMIN true or false false Open mode (sign-in off) only: the anonymous user administers agents — profiles, skill packs, connections — through the published port too, not only from inside the container. An agent administrator can add a local tool server, which this server starts as a program, so with this on anyone who can reach the port can run programs here. It is for a single person on their own machine, where the published port reaches the app from the container bridge and the default left Settings → Agents closed to everyone.
PARLEML_ADMIN_EMAILS comma-separated list empty Emails granted the admin role at SSO provisioning time. Accepts a comma-separated env value ([email protected],[email protected]); NoDecode skips pydantic-settings' JSON decoding so the validator below can split it.
PARLEML_OAUTH_ISSUER text unset The issuer of ParleML's OAuth authorization server for agent hosts: the backend's public origin, where /mcp, /oauth/* and /.well-known/* are served (https://parleml.example.com). Unset, the origin each request arrived at. Set it behind a proxy that rewrites the host. It is not where the web app's links point: that is PARLEML_PUBLIC_URL, which differs in local dev (the UI on Vite).

Agent runtime

The agent runtime ParleML embeds (parleml_agents) has settings prefixed PARLEML_AGENTS_. The backend reads them from the same environment and .env as its own settings and builds the runtime's settings itself, so there is one reader. Where the runtime's files live follows PARLEML_DATA_ROOT and cannot be set: configuration (profiles, connections, skill packs, preferences and the vault) in <data_root>/config, runtime state in <data_root>/agents. Settings → Profiles, Skill packs and Connections edit the configuration.

Variable Type Default What it does
PARLEML_AGENTS_PLATFORM_DIR path <data_root>/agents (set by ParleML; cannot be set) Runtime state: runs/ and work/. ParleML passes <data_root>/agents.
PARLEML_AGENTS_CONFIG_DIR path <data_root>/config (set by ParleML; cannot be set) Installation configuration: connections.yaml, profiles/, packs/, settings.yaml and vault.json. Unset, it is <platform_dir>/config; ParleML passes <data_root>/config.
PARLEML_AGENTS_VAULT_KEY_FILE path <data_root>/.secret_key (set by ParleML; cannot be set) The Fernet key that encrypts vault.json. Unset, it is <config_dir>/.vault_key, created on the first write. ParleML passes its data-root key, so one key protects every secret it stores.
PARLEML_AGENTS_MCP_HOME path . Installation-local root for stdio MCP servers referenced by relative cwd values in connections.yaml.
PARLEML_AGENTS_HOST_ADAPTER
PARLEML_AGENTS_CLAUDE_BINARY
one of claude-cli, cursor-cli, codex-cli, gemini-cli, fake
text
claude-cli
claude
The adapter used when an agent profile does not name one. agents.py describes what each supports; hosts builds them.
PARLEML_AGENTS_FAKE_SCENARIO text unset With host_adapter: fake, a named script every skill follows (hosts/fake_scenarios.py): end-to-end tests and demos without a CLI.
PARLEML_AGENTS_CLAUDE_PINNED_VERSION text 2.1.270 The argv in hosts/claude_cli.py is verified against this exact version.
PARLEML_AGENTS_CODEX_BINARY
PARLEML_AGENTS_CODEX_PINNED_VERSION
text
text
codex
0.162.1
codex exec --json (hosts/codex_cli.py). Signs in with codex login (ChatGPT plan) or OPENAI_API_KEY; verified against 0.162.1.
PARLEML_AGENTS_GEMINI_BINARY
PARLEML_AGENTS_GEMINI_PINNED_VERSION
text
text
gemini
0.62.0
gemini -p --output-format stream-json (hosts/gemini_cli.py). Signs in with Google or GEMINI_API_KEY; verified against 0.62.0.
CURSOR_API_KEY
PARLEML_AGENTS_CURSOR_API_KEY
text unset Cursor Dashboard → Integrations. Also accepted unprefixed as CURSOR_API_KEY.
PARLEML_AGENTS_CURSOR_HOST_ASSETS_ENABLED true or false false Release gate for pack-owned Cursor rules/hooks. Plain Cursor runs remain available while the credentialed native-assets spike is unresolved.
PARLEML_AGENTS_USER_SETTINGS_ALLOWED true or false false Whether an agent profile may launch its CLIs with the running account's own settings (host_assets.include_user_settings). Those settings can carry instructions, hooks and permissions nobody reviewed, so only a local, one-person install should turn this on.
PARLEML_AGENTS_SESSION_MCP_URL text http://127.0.0.1:8000/session-mcp/ Address the spawned host uses to reach the session MCP mount. The embedding application mounts it at /session-mcp; the trailing slash is required, without it the endpoint 307s.
PARLEML_AGENTS_MCP_TOOL_TIMEOUT_MS integer 86_400_000 The chosen maximum human wait. A gate holds an MCP call this long, and so does await_operation on a blocking adapter.
PARLEML_AGENTS_CURSOR_DEFERRED_MCP_WAITS_ENABLED true or false true Cursor's MCP client currently drops the configured timeout and cancels calls after about 60 seconds. Defer only session calls that wait.
PARLEML_AGENTS_CURSOR_DEFERRED_WAIT_SECONDS number 50.0 One bounded await, kept under Cursor's observed timeout with enough margin for the round trip. Every second below that cutoff is a second the operator's answer is observed by a wait already in flight instead of by a poll the model has to choose to make, so stay close to it.
PARLEML_AGENTS_AWAITABLE_POLL_SECONDS
PARLEML_AGENTS_AWAITABLE_PROGRESS_SECONDS
number
number
2.0
30.0
How often a held await_operation on a registered kind (for example job:<id>) polls the kind's status function, and how often it sends MCP progress so a host's idle timer never fires during a long wait.
PARLEML_AGENTS_AWAITABLE_MAX_HOLD_SECONDS number 900.0 The longest one await_operation on a registered kind holds on a blocking adapter. Past it the call hands back pending with how long the run has waited on the handle and the kind's remedy (how to stop the work, or what to read), so stuck server work never holds a run silently for the whole mcp_tool_timeout_ms; awaiting again keeps waiting. Cursor's slices are shorter still; past this total wait they carry the same report.
PARLEML_AGENTS_WALL_CLOCK_SECONDS
PARLEML_AGENTS_MAX_TURNS
PARLEML_AGENTS_MAX_CONCURRENT_CHILDREN
integer
integer
integer
86_400
80
5
Default run caps for one attempt. A profile or skill may raise them.
PARLEML_AGENTS_BUILTIN_TOOLS JSON list of text Read, Write, Edit, Glob, Grep Built-in host tools granted besides MCP. No shell and no web fetch by default: ML work goes through the business MCP, and the agent's own files go to the per-root work directory. Claude Code gets the file tools per directory: reads in the work directory, uploads, reference paths and packs, writes in the work directory only.
PARLEML_AGENTS_PREFLIGHT_TIMEOUT_SECONDS number 30.0 Per-connection bound on how long a preflight probe may take. A CLI's status call can take over 10 s while training jobs hold the CPU, and a shorter bound reads a working sign-in as none.
PARLEML_AGENTS_SSE_QUEUE_SIZE integer 1000 Outbound SSE queue depth per browser connection. Overflow drops transcript frames (recoverable) and closes on durable events (not).
PARLEML_AGENTS_ACCESS_LOG_READS true or false false When true, uvicorn's access log also prints successful reads. Off by default: the UI polls, so those lines are noise that hides the rest. Writes and every 4xx/5xx print either way.
PARLEML_AGENTS_MODEL_ROUTING_ENABLED true or false true When false, orchestrator routing uses only its local scorer. The scorer matches words, so work described in business terms rarely matches an orchestrator described in implementation terms; turning this off trades that accuracy for no model call on the start screen.
PARLEML_AGENTS_ADMIN_ENABLED true or false true When false, /admin returns 403 so the UI renders access-denied rather than partially editable controls.
PARLEML_AGENTS_AUTHORING_ADAPTER one of model, fake model model calls the configured host for metadata proposals; fake returns deterministic proposals for CI and unit tests.
PARLEML_AGENTS_ASK_CONCURRENCY integer 2 (≥ 1) How many one-shot parleml_agents.ask.ask calls (annotations, routing, suggestions) run at once in one process. ParleML also caps its annotation jobs at this number, so a burst of finished objects queues instead of starting a CLI per object.
PARLEML_AGENTS_RUN_DELETE_ENABLED true or false true When false, deleting a run tree returns 403. Deleting lives on /runs, which admin_enabled does not guard, so a shared host needs its own switch for the one irreversible action there.
PARLEML_AGENTS_MAX_UPLOAD_BYTES integer 25 * 1024 * 1024 Bound on one operator upload. Applied before anything reaches disk.
PARLEML_AGENTS_MAX_UPLOAD_FILES integer 20 (≥ 1) Bound on how many files one start may attach, each read into memory.
PARLEML_AGENTS_TOMBSTONE_RETENTION_DAYS integer 365 (≥ 1) How long a deleted session's tombstone stays readable. Older ones are removed at start and once a day after.

Read directly by the backend

Variable Default What it does
PARLEML_BUILD_COMMIT set by the image build The commit the image was built from, shown in Help → About. Set by the build, not by hand.
PARLEML_BUILD_DATE set by the image build When the image was built (UTC), shown in Help → About. Set by the build, not by hand.
PARLEML_DISABLE_TRUSTSTORE unset 1 keeps Python's bundled certificate list. By default TLS goes through the operating system's trust store, so a corporate proxy's root CA is trusted for model downloads.
PARLEML_EDA_MAX_ROWS 1000000 Rows an EDA profile reads at most, as a seeded random sample; 0 profiles every row.
PARLEML_HELP_DIR unset (image: /opt/parleml-help) The built documentation (delivery/build_help.py output) the backend serves at /help. Unset, or without an index.html, /help answers with a page that says how to build it.
PARLEML_MLI_CANDIDATE_MAX_ROWS 6000 Rows the candidate pass of Explain computes on. Raise it for a deeper, slower pass.
PARLEML_MLI_EDA on 0, false, no or off skips the EDA profile inside Explain.
PARLEML_MLI_EXPLANATIONS on 0, false, no or off skips the explanation step of Explain.
PARLEML_MLI_HOLDOUT_MAX_ROWS 50000 Held-out rows a supervised Explain profile tests its rules on, capped separately so the mining cap never thins the test.
PARLEML_MLI_MAX_ROWS 20000 Rows an Explain profile mines at most (a seeded sample).
PARLEML_MLI_RULES_ALL on 0, false, no or off skips mining applyable rules for every class.
PARLEML_MLI_UMAP_MAX_POINTS 20000 Points the UMAP scatter sends to the browser at most.
PARLEML_MLI_VERBOSE on 0, false, no or off quiets the Explain computation's progress log.
PARLEML_SAMPLE_VALUES_PER_COLUMN 5 Sample values the Columns tab shows per column.
PARLEML_WEB_DIST unset A built web app (apps/web/dist) for the backend to serve at /. Unset, the web app is served separately.

Read by the MCP server

The hosted /mcp endpoint runs inside the backend; the standalone server (python -m parleml_mcp.server) reads the same variables.

Variable Default What it does
PARLEML_API_BASE http://localhost:8000 The backend the standalone MCP server forwards to.
PARLEML_API_TOKEN unset The API token the standalone MCP server uses when its host passes none. The hosted /mcp endpoint never falls back to it.
PARLEML_APPS_DIR the package's own copy A directory of built app panels to serve instead of the packaged ones.
PARLEML_MCP_HOST 127.0.0.1 The address the standalone HTTP MCP server listens on.
PARLEML_MCP_PORT 8765 The port the standalone HTTP MCP server listens on.
PARLEML_MCP_TRANSPORT stdio http serves the standalone MCP server over HTTP.
PARLEML_PUBLIC_URL unset Where people open the web app, for the links agents hand out (ui_url). It must be an absolute http(s) URL with no query or fragment; a bad value refuses to boot. Unset, links use the request's own origin. Not the OAuth issuer: that is PARLEML_OAUTH_ISSUER.
PARLEML_REFERENCE_DIR the package's own copy A directory of playbook pages to serve instead of the packaged ones.

Read by your agent host

Set where the agent host (Claude Code, Cursor, Codex, Gemini) runs, not on the server.

Variable Default What it does
PARLEML_MCP_URL unset ParleML's /mcp address, which the skill bundles' MCP server entry reads from the agent host's environment (with PARLEML_API_TOKEN). The server itself never reads it.