# Configuration Options for moeru‑ai/airi: A Complete Guide to the Defu‑Based Config System

> Explore moeru-ai/airi configuration options. Master the defu-based system merging defaults, env vars, JSON, and runtime updates with ConfigManager.

- Repository: [Moeru AI/airi](https://github.com/moeru-ai/airi)
- Tags: how-to-guide
- Published: 2026-03-08

---

**The moeru‑ai/airi repository uses a hierarchical, defu‑based configuration system that merges default TypeScript objects, environment variables, JSON files, and runtime updates through a singleton ConfigManager.**

Understanding the configuration options for moeru‑ai/airi is essential for customizing the Twitter automation and AI agent behaviors. The project implements a unified configuration layer in `services/twitter-services/src/config/` that supports deep merging via `defu`, allowing granular overrides without breaking the default schema.

## How the Configuration System Works

The configuration hierarchy follows a strict precedence order: **hard‑coded defaults → environment variables → JSON file → runtime updates**. All values flow through the `ConfigManager` singleton exposed by `useConfigManager()`.

In [`services/twitter-services/src/config/index.ts`](https://github.com/moeru-ai/airi/blob/main/services/twitter-services/src/config/index.ts), the manager initializes by calling `getDefaultConfig()` from [`services/twitter-services/src/config/types.ts`](https://github.com/moeru-ai/airi/blob/main/services/twitter-services/src/config/types.ts), then conditionally loads a JSON file specified by the `CONFIG_PATH` environment variable (defaulting to [`./twitter-config.json`](https://github.com/moeru-ai/airi/blob/main/./twitter-config.json)). The merge uses `defu` from `@moeru/std`, ensuring that partial objects only override specific keys rather than replacing entire sections.

## Configuration Options Reference

The top‑level `Config` interface defined in [`services/twitter-services/src/config/types.ts`](https://github.com/moeru-ai/airi/blob/main/services/twitter-services/src/config/types.ts) organizes options into logical groups.

### Browser Configuration (BrowserConfig)

Controls Puppeteer/Playwright behavior via Stagehand and BrowserBase:

```typescript
browser: {
  apiKey: string,              // BROWSERBASE_API_KEY
  endpoint?: string,           // Optional Stagehand endpoint
  headless: boolean,           // BROWSER_HEADLESS
  userAgent: string,           // BROWSER_USER_AGENT
  viewport: { width: number, height: number },  // BROWSER_VIEWPORT_*
  timeout: number,             // BROWSER_TIMEOUT
  requestTimeout: number,      // BROWSER_REQUEST_TIMEOUT
  requestRetries: number       // BROWSER_REQUEST_RETRIES
}

```

### Twitter API Credentials

Optional OAuth 1.0a credentials for API v1.1 or v2 endpoints:

```typescript
credentials?: {
  apiKey?: string,             // TWITTER_API_KEY
  apiSecret?: string,          // TWITTER_API_SECRET
  accessToken?: string,        // TWITTER_ACCESS_TOKEN
  accessTokenSecret?: string   // TWITTER_ACCESS_TOKEN_SECRET
}

```

### Twitter Request Options

Default parameters for timeline and search operations:

```typescript
twitter: {
  defaultOptions?: {
    timeline?: { count: number, includeReplies: boolean, includeRetweets: boolean }
    search?: SearchOptions
  }
}

```

### Adapter Settings (Airi and MCP)

Toggles for external service integration:

```typescript
adapters: {
  airi?: {
    url?: string,              // AIRI_URL (default: http://localhost:3000)
    token?: string,            // AIRI_TOKEN
    enabled: boolean           // ENABLE_AIRI
  }
  mcp?: {
    port?: number,             // MCP_PORT (default: 8080)
    enabled: boolean           // ENABLE_MCP
  }
}

```

### System-Wide Settings

Global runtime behavior:

```typescript
system: {
  logLevel: 'error' | 'warn' | 'info' | 'verbose' | 'debug'
  logFormat?: 'json' | 'pretty'
  concurrency: number         // CONCURRENCY (default: 1)
}

```

## Loading and Merging Configuration Files

The `ConfigManager` constructor in [`services/twitter-services/src/config/index.ts`](https://github.com/moeru-ai/airi/blob/main/services/twitter-services/src/config/index.ts) handles file loading:

```typescript
const configPath = process.env.CONFIG_PATH || path.join(process.cwd(), 'twitter-config.json')
configInstance = new ConfigManager(fs.existsSync(configPath) ? configPath : undefined)

```

If the file exists, `ConfigManager.loadFromFile()` reads the JSON and applies `defu` to merge it with the defaults. Errors during parsing are caught and logged via `logger.config.errorWithError`.

## Runtime Configuration Updates

You can modify configuration without restarting the process using `updateConfig()`:

```typescript
import { useConfigManager } from '@/services/twitter-services/src/config'

function onUserChanges(newOpts: Partial<Config>) {
  useConfigManager().updateConfig(newOpts)
}

```

This method uses `defu` to deep‑merge the new partial object onto the existing configuration, ensuring that only specified keys change while preserving the rest of the state.

## Environment Variable Mapping

The `getDefaultConfig()` function in [`services/twitter-services/src/config/types.ts`](https://github.com/moeru-ai/airi/blob/main/services/twitter-services/src/config/types.ts) maps specific environment variables to configuration keys:

| Environment Variable | Config Path | Default Value |
|---------------------|-------------|---------------|
| `BROWSERBASE_API_KEY` | `browser.apiKey` | `''` |
| `BROWSER_HEADLESS` | `browser.headless` | `false` |
| `BROWSER_USER_AGENT` | `browser.userAgent` | Mozilla/5.0... |
| `BROWSER_VIEWPORT_WIDTH` | `browser.viewport.width` | `1280` |
| `BROWSER_TIMEOUT` | `browser.timeout` | `30000` |
| `BROWSER_REQUEST_TIMEOUT` | `browser.requestTimeout` | `20000` |
| `BROWSER_REQUEST_RETRIES` | `browser.requestRetries` | `2` |
| `TWITTER_API_KEY` | `credentials.apiKey` | `undefined` |
| `TWITTER_API_SECRET` | `credentials.apiSecret` | `undefined` |
| `TWITTER_ACCESS_TOKEN` | `credentials.accessToken` | `undefined` |
| `TWITTER_ACCESS_TOKEN_SECRET` | `credentials.accessTokenSecret` | `undefined` |
| `AIRI_URL` | `adapters.airi.url` | `http://localhost:3000` |
| `AIRI_TOKEN` | `adapters.airi.token` | `''` |
| `ENABLE_AIRI` | `adapters.airi.enabled` | `false` |
| `MCP_PORT` | `adapters.mcp.port` | `8080` |
| `ENABLE_MCP` | `adapters.mcp.enabled` | `true` |
| `CONCURRENCY` | `system.concurrency` | `1` |
| `CONFIG_PATH` | N/A (loader path) | [`./twitter-config.json`](https://github.com/moeru-ai/airi/blob/main/./twitter-config.json) |

Additionally, the server‑runtime package provides `optionOrEnv()` in [`packages/server-runtime/src/config/config.ts`](https://github.com/moeru-ai/airi/blob/main/packages/server-runtime/src/config/config.ts) for CLI tools that need to fall back to environment variables when explicit options are not provided.

## Accessing Configuration in Code

Always use the `useConfigManager()` singleton rather than importing raw JSON:

```typescript
import { useConfigManager } from '@/services/twitter-services/src/config'

// Read current configuration
const cfg = useConfigManager().getConfig()

// Access nested values safely
console.log('Browser endpoint →', cfg.browser.endpoint)
console.log('Enabled adapters →', 
  Object.entries(cfg.adapters)
    .filter(([, v]) => v?.enabled)
    .map(([k]) => k)
)

```

This ensures you receive the fully resolved configuration that includes all merges from defaults, environment variables, and runtime updates.

## Summary

- **Hierarchical merging** – Configuration options for moeru‑ai/airi are resolved through a four‑layer hierarchy: hard‑coded defaults → environment variables → JSON file → runtime updates.
- **Deep merge via defu** – The `ConfigManager` uses `defu` from `@moeru/std` to perform deep merges, ensuring partial objects only override specific keys.
- **Singleton access** – Always retrieve settings through `useConfigManager()` in [`services/twitter-services/src/config/index.ts`](https://github.com/moeru-ai/airi/blob/main/services/twitter-services/src/config/index.ts) to guarantee a single source of truth.
- **Environment mapping** – Dozens of environment variables (prefixed with `BROWSER_`, `TWITTER_`, `AIRI_`, `MCP_`, etc.) map directly to nested configuration keys.
- **Runtime mutability** – Call `updateConfig()` to modify settings without restarting the process, enabling dynamic UI‑driven configuration changes.

## Frequently Asked Questions

### What is the default configuration file path for airi?

By default, the `ConfigManager` looks for [`twitter-config.json`](https://github.com/moeru-ai/airi/blob/main/twitter-config.json) in the current working directory. You can override this by setting the `CONFIG_PATH` environment variable to point to any JSON file location.

### How does airi handle configuration conflicts between environment variables and JSON files?

The system uses a precedence stack: environment variables are baked into the default configuration object first, then the JSON file (if present) is deep‑merged on top using `defu`. This means JSON file values override environment defaults, and runtime updates override both.

### Can I update airi configuration options at runtime without restarting?

Yes. The `ConfigManager` exposes `updateConfig(partialConfig)` which performs a live deep‑merge into the existing configuration singleton. This is used by UI panels and plugins to toggle adapters or adjust browser settings on the fly.

### Where is the ConfigManager singleton defined in the moeru-ai/airi repository?

The singleton is defined and exported from [`services/twitter-services/src/config/index.ts`](https://github.com/moeru-ai/airi/blob/main/services/twitter-services/src/config/index.ts). It instantiates the `ConfigManager` class, which loads the initial state from `getDefaultConfig()` (located in [`services/twitter-services/src/config/types.ts`](https://github.com/moeru-ai/airi/blob/main/services/twitter-services/src/config/types.ts)) and optionally merges a JSON configuration file.