Skip to content

Configuration

Config in Keryx is statically defined at boot — there's no dynamic config reloading. That said, every config value supports per-environment overrides via environment variables, so you can set things differently in test, development, and production without touching code.

Structure

Config is split into modules:

backend/config/
├── index.ts        # Aggregates everything into one `config` object
├── database.ts     # Database connection string, auto-migrate flag
├── logger.ts       # Log level, timestamps, colors
├── process.ts      # Process name, shutdown timeout
├── rateLimit.ts    # Rate limiting windows and thresholds
├── redis.ts        # Redis connection string
├── session.ts      # Session TTL, cookie security flags
├── tasks.ts        # Task queue settings
└── server/
    ├── cli.ts      # CLI error display, quiet mode
    ├── web.ts      # Web server port, CORS, security headers, WS limits
    └── mcp.ts      # MCP server toggle, route, OAuth TTLs

Everything rolls up into a single config object:

ts
import { config } from "../config";

config.database.connectionString; // Postgres URL
config.server.web.port; // 8080
config.logger.level; // "info"

Environment Overrides

The loadFromEnvIfSet() helper is where the magic happens:

ts
import { loadFromEnvIfSet } from "../util/config";

export const configDatabase = {
  connectionString: await loadFromEnvIfSet("DATABASE_URL", "x"),
  autoMigrate: await loadFromEnvIfSet("DATABASE_AUTO_MIGRATE", true),
};

The resolution order is:

  1. DATABASE_URL_TEST (env var with NODE_ENV suffix — checked first)
  2. DATABASE_URL (plain env var)
  3. "x" (the default value)

This means you can set DATABASE_URL_TEST=postgres://localhost/bun-test and it'll automatically be used when NODE_ENV=test, without any conditional logic in your config files.

The helper is also type-aware — it parses "true"/"false" strings into booleans and numeric strings into numbers. So DATABASE_AUTO_MIGRATE=false does what you'd expect.

Reference

Database

KeyEnv VarDefault
connectionStringDATABASE_URL"x"
autoMigrateDATABASE_AUTO_MIGRATEtrue

Logger

KeyEnv VarDefault
levelLOG_LEVEL"info"
includeTimestampsLOG_INCLUDE_TIMESTAMPStrue
colorizeLOG_COLORIZEtrue

Redis

KeyEnv VarDefault
connectionStringREDIS_URL"redis://localhost:6379/0"

Session

KeyEnv VarDefaultDescription
ttlSESSION_TTL86400 (1 day in seconds)Session lifetime
cookieNameSESSION_COOKIE_NAME"__session"Cookie name
cookieHttpOnlySESSION_COOKIE_HTTP_ONLYtruePrevent JavaScript access
cookieSecureSESSION_COOKIE_SECUREfalseHTTPS-only cookies
cookieSameSiteSESSION_COOKIE_SAME_SITE"Strict"CSRF protection (Strict, Lax, None)

Process

KeyEnv VarDefault
namePROCESS_NAME"server"
shutdownTimeoutPROCESS_SHUTDOWN_TIMEOUT30000 (30s)

Web Server

KeyEnv VarDefault
enabledWEB_SERVER_ENABLEDtrue
portWEB_SERVER_PORT8080
hostWEB_SERVER_HOST"localhost"
applicationUrlAPPLICATION_URL"http://localhost:8080"
apiRouteWEB_SERVER_API_ROUTE"/api"
allowedOriginsWEB_SERVER_ALLOWED_ORIGINS"*"
allowedMethodsWEB_SERVER_ALLOWED_METHODS"HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS"
allowedHeadersWEB_SERVER_ALLOWED_HEADERS"Content-Type"
staticFilesEnabledWEB_SERVER_STATIC_ENABLEDtrue
includeStackInErrorsWEB_SERVER_INCLUDE_STACK_IN_ERRORStrue (dev) / false (prod)
websocketMaxPayloadSizeWS_MAX_PAYLOAD_SIZE65536 (64 KB)
websocketMaxMessagesPerSecondWS_MAX_MESSAGES_PER_SECOND20
websocketMaxSubscriptionsWS_MAX_SUBSCRIPTIONS100

Security Headers

All HTTP responses include these headers. Each is configurable:

HeaderEnv VarDefault
Content-Security-PolicyWEB_SECURITY_CSPdefault-src 'self'
X-Content-Type-OptionsWEB_SECURITY_CONTENT_TYPE_OPTIONSnosniff
X-Frame-OptionsWEB_SECURITY_FRAME_OPTIONSDENY
Strict-Transport-SecurityWEB_SECURITY_HSTSmax-age=31536000; includeSubDomains
Referrer-PolicyWEB_SECURITY_REFERRER_POLICYstrict-origin-when-cross-origin

Tasks

KeyEnv VarDefault
enabledTASKS_ENABLEDtrue
timeoutTASK_TIMEOUT5000
taskProcessorsTASK_PROCESSORS1

Rate Limiting

See the Security guide for details on how rate limiting works.

KeyEnv VarDefault
enabledRATE_LIMIT_ENABLEDtrue (disabled in test)
windowMsRATE_LIMIT_WINDOW_MS60000 (1 min)
unauthenticatedLimitRATE_LIMIT_UNAUTH_LIMIT20
authenticatedLimitRATE_LIMIT_AUTH_LIMIT200
keyPrefixRATE_LIMIT_KEY_PREFIX"ratelimit"
oauthRegisterLimitRATE_LIMIT_OAUTH_REGISTER_LIMIT5
oauthRegisterWindowMsRATE_LIMIT_OAUTH_REGISTER_WINDOW_MS3600000 (1 hour)

CLI

KeyEnv VarDefault
includeStackInErrorsCLI_INCLUDE_STACK_IN_ERRORStrue
quietCLI_QUIETfalse

MCP Server

KeyEnv VarDefault
enabledMCP_SERVER_ENABLEDfalse
routeMCP_SERVER_ROUTE"/mcp"
oauthClientTtlMCP_OAUTH_CLIENT_TTL2592000
oauthCodeTtlMCP_OAUTH_CODE_TTL300

Released under the MIT License.