# Configuration File Resolution Order for Ponytail: Environment Variables, JSON Configs, and Fallbacks

> Understand Ponytail's configuration file resolution order. Learn how environment variables, JSON configs, and fallbacks set your default runtime mode for optimal performance.

- Repository: [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail)
- Tags: how-to-guide
- Published: 2026-09-03

---

**Ponytail determines its default runtime mode through a strict three-tier hierarchy: first checking the `PONYTAIL_DEFAULT_MODE` environment variable, then the `defaultMode` property in a platform-specific [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json) file, and finally defaulting to the built-in constant `'full'` when no valid external configuration exists.**

The DietrichGebert/ponytail repository implements this resolution logic in [`hooks/ponytail-config.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js) to ensure consistent behavior across Unix, macOS, and Windows environments. Understanding this precedence order allows developers to effectively manage runtime modes (off, lite, full, or ultra) across development, CI, and production deployments.

## The Three-Level Configuration Hierarchy

The `getDefaultMode()` function in [`hooks/ponytail-config.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js) evaluates configuration sources sequentially, returning immediately upon finding the first valid runtime mode. Invalid values at any level trigger a fallthrough to the next source rather than causing errors.

### 1. Environment Variable: PONYTAIL_DEFAULT_MODE

The resolver inspects `process.env.PONYTAIL_DEFAULT_MODE` before accessing the filesystem. If the variable exists and contains a valid mode string—`off`, `lite`, `full`, or `ultra`—the function returns the lowercase value immediately. This top-level priority enables temporary, session-specific overrides without modifying persistent configuration files.

```bash

# Override for current shell session

export PONYTAIL_DEFAULT_MODE=lite

```

### 2. Platform-Specific Configuration Files

When the environment variable is unset or invalid, Ponytail searches for [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json) in platform-specific configuration directories. The resolution follows XDG Base Directory specifications on Unix systems and Windows conventions:

- **`$XDG_CONFIG_HOME/ponytail/config.json`** (checked first if `XDG_CONFIG_HOME` is defined)
- **`~/.config/ponytail/config.json`** on macOS and Linux (fallback)
- **`%APPDATA%\ponytail\config.json`** on Windows

The file must contain a top-level `defaultMode` property. As implemented in [`hooks/ponytail-config.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js) (lines 76–99), the parser strips UTF-8 BOM characters before calling `JSON.parse()` to ensure compatibility with editors that insert byte-order marks.

```json
{
  "defaultMode": "ultra",
  "quietStartup": true,
  "hideStatus": false
}

```

### 3. Built-in Default Mode Constant

If neither the environment variable nor a valid configuration file exists, the function returns `DEFAULT_MODE`, a constant hard-coded to `'full'`. This fallback ensures Ponytail remains operational in zero-configuration environments, providing sensible defaults for first-time users.

## Implementation in hooks/ponytail-config.js

The resolution logic resides in the `getDefaultMode()` function, which [`hooks/ponytail-mode-tracker.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-mode-tracker.js) and [`pi-extension/index.js`](https://github.com/DietrichGebert/ponytail/blob/main/pi-extension/index.js) consume during initialization. The implementation uses explicit validation against the `RUNTIME_MODES` array to prevent invalid state propagation:

```javascript
// 1. Environment variable (highest priority)
const envMode = process.env.PONYTAIL_DEFAULT_MODE;
if (envMode && RUNTIME_MODES.includes(envMode.toLowerCase())) {
  return envMode.toLowerCase();
}

// 2. Config file
try {
  const configPath = getConfigPath();
  const config = JSON.parse(
    fs.readFileSync(configPath, 'utf8').replace(/^\uFEFF/, '')
  );
  if (config.defaultMode && RUNTIME_MODES.includes(config.defaultMode.toLowerCase())) {
    return config.defaultMode.toLowerCase();
  }
} catch (e) {
  // Config file missing or malformed – fall through
}

// 3. Default
return DEFAULT_MODE;

```

This structure ensures that malformed JSON or invalid mode strings in [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json) do not crash the application; instead, the resolver silently proceeds to the built-in fallback.

## Practical Usage Examples

### Overriding Defaults in Shell Sessions

For CI pipelines or temporary testing, set the mode without touching the filesystem:

```bash

# Bash/Zsh

PONYTAIL_DEFAULT_MODE=off ponytail process ./files

# Windows PowerShell

$env:PONYTAIL_DEFAULT_MODE="lite"; ponytail process ./files

```

### Creating Persistent Configuration Files

Establish a default mode for all future invocations by creating the appropriate directory structure and JSON file:

```bash

# Linux/macOS

mkdir -p ~/.config/ponytail
echo '{"defaultMode": "full"}' > ~/.config/ponytail/config.json

# Windows PowerShell

New-Item -ItemType Directory -Force -Path "$env:APPDATA\ponytail"
Set-Content -Path "$env:APPDATA\ponytail\config.json" -Value '{"defaultMode": "full"}'

```

### Reading and Writing Defaults Programmatically

The [`pi-extension/index.js`](https://github.com/DietrichGebert/ponytail/blob/main/pi-extension/index.js) file exposes utilities for external callers and plugins. Read the resolved effective default:

```javascript
const { getDefaultMode } = require('./hooks/ponytail-config');

console.log('Resolved mode:', getDefaultMode()); // "full", "lite", "ultra", or "off"

```

Persist a new default to [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json) using `writeDefaultMode()`:

```javascript
const { writeDefaultMode } = require('./hooks/ponytail-config');

// Updates or creates config.json with "defaultMode": "lite"
writeDefaultMode('lite');

```

## Summary

- **Environment variables take precedence**: `PONYTAIL_DEFAULT_MODE` overrides file-based configuration when set to a valid mode.
- **XDG compliance on Unix**: The resolver checks `$XDG_CONFIG_HOME` before falling back to `~/.config/`, following freedesktop.org standards.
- **Windows uses APPDATA**: On Windows systems, configuration resides in `%APPDATA%\ponytail\config.json`.
- **UTF-8 BOM tolerance**: The JSON parser strips byte-order marks to prevent parsing errors from editor-generated files.
- **Graceful degradation**: Invalid values at any level trigger fallthrough to the next source, ultimately defaulting to `'full'` mode.
- **Programmatic API available**: `getDefaultMode()` and `writeDefaultMode()` in [`hooks/ponytail-config.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js) enable runtime inspection and persistent updates.

## Frequently Asked Questions

### What happens if the config.json file contains an invalid mode name?

If the `defaultMode` property in [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json) contains a value not included in the `RUNTIME_MODES` array (such as `off`, `lite`, `full`, or `ultra`), the resolver ignores it and falls through to the built-in default of `'full'`. Similarly, malformed JSON or missing files trigger the same fallback behavior without throwing errors.

### Can I override the configuration file location using an environment variable?

No. As implemented in [`hooks/ponytail-config.js`](https://github.com/DietrichGebert/ponytail/blob/main/hooks/ponytail-config.js), the `getConfigPath()` function uses hard-coded platform logic based on `$XDG_CONFIG_HOME` and `%APPDATA%`. There is no environment variable to specify an alternative config file path; you must place the file in one of the standard platform-specific locations.

### Does changing the configuration file affect already-running Ponytail processes?

No. The configuration resolution occurs once during process initialization, typically when [`ponytail-mode-tracker.js`](https://github.com/DietrichGebert/ponytail/blob/main/ponytail-mode-tracker.js) invokes `getDefaultMode()`. Modifications to [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json) or the `PONYTAIL_DEFAULT_MODE` environment variable only affect processes started after the change; existing processes retain their initially resolved mode.

### How do I programmatically determine which configuration source is active?

Ponytail does not expose the specific source of the resolved default through its public API. However, you can infer the source by checking `process.env.PONYTAIL_DEFAULT_MODE` before requiring the config module—if the environment variable is set and valid, it is the active source. Otherwise, if `getDefaultMode()` returns a value other than `'full'`, it likely originated from [`config.json`](https://github.com/DietrichGebert/ponytail/blob/main/config.json).