Configuration Settings
All configurable settings for DMART. Bound to the strongly-typed DmartSettings record and loaded from a config.env dotenv file and/or environment variables. Values are read once at boot and validated before the server starts — there is no hot reload and no file watcher.
Loading Order
Sources are layered with a strict precedence (later wins): appsettings.json < config.env < environment variables (Dmart__Xxx).
The config.env file is located with a first-match-wins lookup:
The path in the BACKEND_ENV (or DMART_ENV) environment variable — used for dev/CI, e.g. a repo-local file
~/.dmart/config.env — per-user install
/etc/dmart/config.env — system-wide RPM/DEB install
A cwd-relative ./config.env is not looked up implicitly. Because the file may carry JWT_SECRET and DATABASE_PASSWORD, DMART refuses to read it if "other" permission bits are set.
Strict validation. Configuration is bound and validated at startup (ValidateOnStart). Any unknown key is rejected and aborts startup — there is no silent fallthrough for typos. Retired keys — REDIS_HOST/PORT/PASSWORD/CONNECTION, DATABASE_DRIVER, and ACTIVE_DATA_DB — no longer exist and now hard-fail the boot if present.
General Configuration
| Setting |
Description |
Default |
APP_URL |
Public base URL of the server, used to assemble short-link URLs. Empty disables short-link assembly. |
"" |
MANAGEMENT_SPACE |
Name of the space that holds users, roles, groups and permissions |
"management" |
MAX_QUERY_LIMIT |
Hard cap on the number of records returned by a query |
10000 |
URL_SHORTER_EXPIRES |
Short-link expiration (seconds) |
3600 |
REQUEST_TIMEOUT |
Outbound HTTP timeout (seconds) for plugins/webhooks |
35 |
JQ_TIMEOUT |
Timeout (seconds) for the jq subprocess used by join sub-queries carrying a jq_filter |
2 |
Server & Network
| Setting |
Description |
Default |
LISTENING_HOST |
Hostname/IP the Kestrel server binds to |
"0.0.0.0" |
LISTENING_PORT |
Port the server listens on (HTTP + WebSocket share this port) |
8282 |
CXB_URL |
URL path prefix for the embedded CXB admin SPA. Set to / to serve it at the root. |
"/cxb" |
CAT_URL |
URL path prefix for the embedded Catalog SPA |
"/cat" |
WEBSITE_URL |
URL path prefix for the site dmart website build generates. / turns serving off. |
"/website" |
WEBSITE_DIR |
Where dmart website build writes and the server reads. Empty means ~/.dmart/website. |
"" |
ALLOWED_CORS_ORIGINS |
Comma-separated origins allowed to make cross-site requests. Empty allows only the same-host origin. |
"" |
TRUSTED_PROXIES |
Comma-separated IPs/CIDRs whose X-Forwarded-For is trusted (for real client IP behind nginx/an LB) |
"" |
FORWARDED_FOR_HOP_COUNT |
Number of proxy hops in front of DMART |
1 |
OTLP_ENDPOINT |
OpenTelemetry collector endpoint for metrics/traces. Empty disables observability entirely. |
"" |
Database & Storage
PostgreSQL (via Npgsql) is the sole runtime data store. There is no Redis and no filesystem runtime adapter; all caches are in-process. Provide a full connection string with POSTGRES_CONNECTION, or leave it unset and let DMART assemble one from the individual DATABASE_* components below.
PostgreSQL Connection
| Setting |
Description |
Default |
POSTGRES_CONNECTION |
Full Npgsql connection string. When set, the component settings below are ignored. |
"" (unset) |
DATABASE_HOST |
Database hostname |
"localhost" |
DATABASE_PORT |
Database port |
5432 |
DATABASE_USERNAME |
Database username |
"dmart" |
DATABASE_PASSWORD |
Database password |
"" |
DATABASE_NAME |
Database name |
"dmart" |
DATABASE_POOL_SIZE |
Connection pool size |
10 |
DATABASE_MAX_OVERFLOW |
Max overflow connections |
10 |
DATABASE_POOL_TIMEOUT |
Pool timeout (seconds) |
30 |
DATABASE_POOL_RECYCLE |
Pool recycle time (seconds) |
1800 |
DATABASE_KEEPALIVE |
Seconds of socket inactivity before a keepalive probe is sent. 0 disables. |
30 |
Spaces Folder (import/export only)
The on-disk spaces/ + .dm/meta.*.json layout is a transfer/backup format used only by import, export, seed and migrate — never as a live store. SPACES_FOLDER defaults empty and, when set, is also the root for an optional append-only events.jsonl.log audit trail (disabled unless configured).
Security & Authentication
Authentication is JWT Bearer (HS256, symmetric key from JWT_SECRET) with Argon2 password hashing, plus an OAuth 2.1 Authorization Server. The signing algorithm is fixed at HS256 and is not configurable.
JWT & Sessions
| Setting |
Description |
Default |
JWT_SECRET |
Symmetric signing key (min 32 bytes). Required — the server refuses to start on the placeholder value. |
"change-me…" |
JWT_ISSUER |
Expected iss claim |
"dmart" |
JWT_AUDIENCE |
Expected aud claim |
"dmart" |
JWT_ACCESS_EXPIRES |
Access-token lifetime (seconds) |
2592000 (30d) |
JWT_REFRESH_DAYS |
Refresh-token lifetime (days) |
30 |
JWT_REQUIRE_TOKEN_USE |
Reject JWTs lacking the token_use claim (access vs refresh separation) |
false |
MAX_SESSIONS_PER_USER |
Max concurrent sessions per user |
5 |
SESSION_INACTIVITY_TTL |
Idle seconds before a JWT is rejected and its session deleted (0 = disabled) |
0 |
SESSION_MAX_LIFETIME_SECONDS |
Hard cap on the refresh-token chain from original login (0 = disabled) |
0 |
LOGOUT_ON_PWD_CHANGE |
Invalidate all sessions on password change |
true |
CSRF_PROTECT_COOKIE_AUTH |
Gate cookie-borne auth against cross-site requests (bearer-header callers unaffected) |
true |
Brute-force & Rate Limiting
| Setting |
Description |
Default |
MAX_FAILED_LOGIN_ATTEMPTS |
Failed attempts before an account is auto-locked |
5 |
LOCKOUT_COOLDOWN_SECONDS |
Seconds an account stays locked before a retry may clear it (0 = permanent until admin reset) |
900 |
AUTH_RATE_LIMIT_PER_MINUTE |
Per-IP cap on /user/login + /user/otp-request per 60s |
10 |
MAX_OTP_VERIFY_ATTEMPTS |
Wrong guesses allowed against one OTP code before it is invalidated (0 = uncapped) |
5 |
LOCK_PERIOD |
Seconds a PUT /managed/lock stays held before another user can take it |
300 |
Registration, OTP & Profiles
| Setting |
Description |
Default |
IS_REGISTRABLE |
Allow self-registration via POST /user/create |
true |
IS_OTP_FOR_CREATE_REQUIRED |
Require a verified OTP for account creation |
true |
OTP_TOKEN_TTL |
One-time-password time-to-live (seconds) |
300 |
ALLOW_OTP_RESEND_AFTER |
Minimum seconds between OTP re-sends to one destination |
60 |
ALLOW_PASSWORD_RESET_RESEND_AFTER |
Minimum seconds between password-reset OTP re-sends |
60 |
USER_CREATE_DEFAULT_ROLE |
Single role assigned to every self-created user (self-service create ignores roles in the body) |
"" (none) |
USER_CREATE_DEFAULT_GROUP |
Single group assigned to every self-created user |
"" (none) |
USER_PROFILE_PAYLOAD_PROTECTED_FIELDS |
CSV of payload fields users cannot update via POST /user/profile |
"" |
ALLOWED_SUBMIT_MODELS |
CSV of space.schema pairs allowed for public /submit endpoints (empty = none) |
"" |
Admin Bootstrap
| Setting |
Description |
Default |
ADMIN_EMAIL |
Email seeded for the dmart admin on first boot only |
"" |
ADMIN_PASSWORD |
First-boot admin password. Intentionally omitted from config.env; the recommended flow is dmart passwd dmart <pwd>. Honoured via Dmart__AdminPassword if provided. |
"" (unset) |
Email & Notifications
SMTP
| Setting |
Description |
Default |
MAIL_HOST |
SMTP server host. Empty falls back to logging the code only. |
"" |
MAIL_PORT |
SMTP port |
587 |
MAIL_USERNAME |
SMTP username |
"" |
MAIL_PASSWORD |
SMTP password |
"" |
MAIL_USE_TLS |
Use TLS for the SMTP connection |
true |
MAIL_FROM_ADDRESS |
From email address |
"noreply@admin.com" |
MAIL_FROM_NAME |
From name |
"" |
MOCK_SMTP_API |
Short-circuit SMTP delivery (dev/test) |
false |
SMS Gateway
| Setting |
Description |
Default |
SEND_SMS_OTP_API |
POST endpoint for OTP SMS delivery. Empty logs the code only. |
"" |
SEND_SMS_API |
POST endpoint for general SMS delivery |
"" |
SMS_SENDER |
Optional sender ID / from-name inlined into the SMS request body |
"" |
SMPP_AUTH_KEY |
Value of the auth-key header sent to the SMS gateway |
"" |
MOCK_SMPP_API |
Short-circuit SMS delivery (dev/test) |
false |
MOCK_OTP_CODE |
Fixed OTP code returned when mocking is enabled |
"123456" |
Logging
| Setting |
Description |
Default |
LOG_FORMAT |
"text" (human-readable) or "json" (structured JSON lines) |
"text" |
LOG_LEVEL |
trace, debug, information, warning, error, critical, none |
"information" |
LOG_FILE |
Log file path. Empty = stdout only (container/journald friendly). |
"" |
LOG_MAX_BYTES |
Max file size before rotation. 0 disables rotation. |
1073741824 (1 GB) |
LOG_BACKUP_COUNT |
Rotated archive retention (< 0 unlimited, 0 truncate, > 0 keep N) |
-1 |
Third-Party Integrations
OAuth / Social Login
Leaving a provider’s CLIENT_ID blank disables it cleanly — its endpoints return a "provider not configured" error instead of attempting an outbound call.
| Setting |
Description |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET / GOOGLE_OAUTH_CALLBACK |
Google OAuth credentials + redirect URI |
FACEBOOK_CLIENT_ID / FACEBOOK_CLIENT_SECRET / FACEBOOK_OAUTH_CALLBACK |
Facebook OAuth credentials + redirect URI |
APPLE_CLIENT_ID / APPLE_TEAM_ID / APPLE_KEY_ID / APPLE_P8_PRIVATE_KEY / APPLE_OAUTH_CALLBACK |
Apple Sign In. The client secret is a short-lived ES256 JWT that DMART mints on the fly from the .p8 key. |
Embeddings (semantic search, opt-in)
When the pgvector extension is installed and EMBEDDING_API_URL is set, DMART embeds every entry on create/update and exposes POST /managed/semantic-search plus the dmart_semantic_search MCP tool. The endpoint is OpenAI-compatible.
| Setting |
Description |
Default |
EMBEDDING_API_URL |
Embeddings endpoint. Empty keeps semantic search off. |
"" |
EMBEDDING_API_KEY |
Bearer token for the embeddings endpoint |
"" |
EMBEDDING_MODEL |
Model name sent in the request body |
"text-embedding-3-small" |
Channel Authentication
| Setting |
Description |
Default |
ENABLE_CHANNEL_AUTH |
Gate requests on the x-channel-key header |
false |
CHANNELS_CONFIG_PATH |
Path to the channels JSON file (empty = ~/.dmart/channels.json) |
"" |
Miscellaneous
| Setting |
Description |
Default |
ENFORCE_FOLDER_CONTENT_POLICY |
Enforce a folder’s content-policy arrays on create/update/move. False = dry-run (violations warn-logged but allowed). |
true |
ENABLE_INNER_JOIN_PUSHDOWN |
Push eligible inner joins into SQL as correlated EXISTS semi-joins. False forces the in-memory fallback. |
true |
ENABLE_MCP |
Expose the Model Context Protocol surface: /mcp, the OAuth 2.1 authorization server (/oauth/*) and its /.well-known/oauth-* discovery documents. False leaves all of them unmapped (INVALID_ROUTE, HTTP 422). |
false |
IMPORT_MAX_ENTRIES |
Reject zip imports declaring more than this many entries (decompression-bomb guard). 0 disables. |
500000 |
IMPORT_MAX_UNCOMPRESSED_BYTES |
Reject zip imports whose declared uncompressed size exceeds this. 0 disables. |
2147483648 (2 GiB) |