How Configuration is Handled in the TREK Server: Environment Variables, File Persistence, and Runtime Constants

The TREK server manages configuration through a hybrid system that prioritizes environment variables, falls back to persisted files in the data/ directory, and exposes derived constants throughout the application, with a minimal public endpoint for client bootstrapping.

The TREK server's configuration system is centralized in server/src/config.ts and implements a resilient multi-layer approach. It combines environment variables, file-based secret persistence, and runtime-derived constants to ensure sensitive keys survive restarts while allowing flexible deployment overrides. This architecture supports everything from encryption keys to session durations, with a type-safe public API for initial client configuration.

Configuration Loading Hierarchy in server/src/config.ts

The core configuration logic resides in server/src/config.ts, which implements a three-tier resolution strategy.

Environment Variable Resolution

The server first checks process.env for critical secrets and settings. Variables like ENCRYPTION_KEY, JWT_SECRET, DEFAULT_LANGUAGE, and SESSION_DURATION are read directly from the environment. This allows Docker deployments and cloud platforms to inject configuration without modifying source code.

File-Based Persistence in the data/ Directory

When environment variables are missing, the system falls back to files stored in the data/ directory. For example, if ENCRYPTION_KEY is unset, the code checks for data/.encryption_key. If JWT_SECRET is missing, the server reads from data/.jwt_secret or generates a fresh secret using crypto.randomBytes(32).toString('hex'). Critically, these resolved values are written back to their respective files upon startup, ensuring subsequent restarts maintain consistent secrets without requiring persistent environment variables.

Derived Application Constants

After raw values are resolved, the module exports convenience constants used throughout the codebase. The DEFAULT_LANGUAGE constant validates process.env.DEFAULT_LANGUAGE against SUPPORTED_LANG_CODES, defaulting to 'en'. Session durations are parsed from human-readable strings like "30d" or "1h" into milliseconds (SESSION_DURATION_MS) and seconds for JWT expiration. These derived values are imported by services such as server/src/services/authService.ts for token signing operations.

Public Configuration Endpoint

The TREK server exposes a minimal public API for pre-login client configuration.

NestJS ConfigModule Implementation

A dedicated NestJS module in server/src/nest/config/config.module.ts registers the ConfigController. This controller exposes a single GET endpoint at /api/config that returns only the DEFAULT_LANGUAGE value. This allows the client to localize the login page before authentication occurs.

Zod Schema Validation

The response shape is enforced using Zod in shared/src/config/config.schema.ts. The publicConfigSchema validates that the endpoint returns an object with a defaultLanguage string property, ensuring type safety across the client-server boundary.

Configuration Usage Examples

Accessing Secrets in Authentication Services

Services import constants directly from the configuration module:

import { JWT_SECRET, SESSION_DURATION_SECONDS } from '../../config';
import jwt from 'jsonwebtoken';

// Sign a token using the resolved secret
const token = jwt.sign({ userId }, JWT_SECRET, { 
  expiresIn: SESSION_DURATION_SECONDS 
});

This pattern appears in server/src/services/authService.ts where JWT signing operations rely on the persisted secrets.

Overriding Configuration with Docker Compose

Deployers can override secrets via environment variables in docker-compose.yml:

services:
  trek-server:
    environment:
      - ENCRYPTION_KEY=super-secret-key-123
      - JWT_SECRET=my-jwt-secret
      - DEFAULT_LANGUAGE=de
      - SESSION_DURATION=7d

The server will persist these values to the data/ directory on first boot.

Fetching Public Config from the Client

The client retrieves localization settings before login:

// client/src/api.ts
export async function fetchPublicConfig() {
  const res = await fetch('/api/config');
  const { defaultLanguage } = await res.json();
  return defaultLanguage;
}

Key Configuration Files

File Responsibility
server/src/config.ts Central loader, env-var resolution, secret persistence, constant derivation
server/src/nest/config/config.module.ts NestJS module registration for the public endpoint
server/src/nest/config/config.controller.ts /api/config controller exposing DEFAULT_LANGUAGE
shared/src/config/config.schema.ts Zod schema for public configuration validation
server/src/services/authService.ts Consumer of JWT_SECRET and session constants
docker-compose.yml Example environment variable injection

Summary

  • Environment variables take precedence for all configuration values in the TREK server.
  • File persistence in data/ ensures secret consistency across restarts when env vars are absent.
  • Derived constants like SESSION_DURATION_MS and DEFAULT_LANGUAGE provide type-safe, validated values used throughout the application.
  • Public endpoint /api/config exposes only the default language via a NestJS controller validated by Zod schemas.
  • Zero-downtime rotation is supported by updating env vars and restarting, with automatic persistence to the data directory.

Frequently Asked Questions

Where does TREK store generated secrets when environment variables are missing?

When ENCRYPTION_KEY or JWT_SECRET are not provided via process.env, the server reads from or writes to the data/ directory. Specifically, it checks data/.encryption_key and data/.jwt_secret. If these files do not exist, the server generates cryptographically secure random values and persists them to these files for subsequent restarts.

How is the session duration configured in TREK?

The session duration is controlled via the SESSION_DURATION environment variable using human-readable strings like "30d", "1h", or "24h". The server/src/config.ts module parses these strings into milliseconds (SESSION_DURATION_MS) and seconds using a duration parser, defaulting to 24 hours if unspecified.

What configuration data is exposed to unauthenticated clients?

The public /api/config endpoint exposes only the DEFAULT_LANGUAGE setting. This minimal exposure reduces the attack surface while allowing the client application to render localized login pages before a user authenticates. The response shape is validated against the Zod schema defined in shared/src/config/config.schema.ts.

How do services access configuration constants in the TREK server?

Services import constants directly from server/src/config.ts. For example, the authentication service imports JWT_SECRET and SESSION_DURATION_SECONDS to sign tokens. This direct import pattern avoids global state and provides compile-time checking for configuration values.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →