How to Configure Cordis: A Complete Guide to Context, Loaders, and Runtime Updates

Cordis stores configuration in a mutable Loader.Config object attached to the Context, supporting both static initialization and runtime updates via the internal/update event system.

The cordiverse/cordis framework treats configuration as a first-class citizen that lives alongside the dependency injection container. When you configure Cordis, you are not merely passing static settings; you are establishing a reactive configuration graph that propagates changes from the global context down to individual entries and groups.

The Loader Constructor and Initial Configuration

Configuration enters the system through the Loader class constructor in [packages/loader/src/index.ts](https://github.com/cordiverse/cordis/blob/main/packages/loader/src/index.ts#L59). The loader accepts a Context instance and an optional Loader.Config object, which it stores as a public property:

// packages/loader/src/index.ts
constructor(ctx: Context, public config: Loader.Config = {}) {
  if (config.baseUrl) {
    this.ctx.baseUrl = config.baseUrl
  }
}

This initialization step establishes the base URL and other global options immediately. The config parameter accepts any plain JavaScript object, allowing you to define arbitrary hierarchical settings without a enforced schema.

Runtime Configuration Updates

Cordis supports dynamic reconfiguration through its event system. The loader listens for the internal/update event to rewrite the stored configuration and notify all dependent components:

// packages/loader/src/index.ts
ctx.on('internal/update', function (config, noSave, next) {
  this.entry.options.config = unparse ? unparse(config) : config
  next()
})

As shown at line 74 of the loader index, the handler receives the new configuration, optionally serializes it using an unparse function, and assigns it to the entry's options. Calling next() propagates the update through the middleware chain.

Entry-Level Configuration Interpolation

Individual entries do not directly access the global config object. Instead, they receive an interpolated version specific to their context. In [packages/loader/src/config/entry.ts](https://github.com/cordiverse/cordis/blob/main/packages/loader/src/config/entry.ts#L80), the Entry class exposes a config getter that processes the raw options through the interpolate function:

// packages/loader/src/config/entry.ts
get config() {
  if (plugin[EntryGroup.key]) return this.options.config
  return interpolate(this.ctx, this.options.config)
}

This mechanism allows entries to resolve configuration values that reference context variables or other dynamic sources. If an entry belongs to a group, it returns the raw config; otherwise, it interpolates the values against the current context.

Group Configuration Propagation

Configuration changes cascade through the hierarchy via groups. The Group class in [packages/loader/src/config/group.ts](https://github.com/cordiverse/cordis/blob/main/packages/loader/src/config/group.ts#L79) listens for the same internal/update event and triggers updates for all member entries:

// packages/loader/src/config/group.ts
ctx.on('internal/update', (config) => {
  this.update(config)
})

When a group's configuration updates, the update method iterates over its children and reapplies the configuration, ensuring that the entire tree remains synchronized with the global state.

Static Configuration with YAML Files

For declarative setups, Cordis supports loading configuration from YAML files. The test suite includes an example at [packages/hmr/tests/cordis.yml](https://github.com/cordiverse/cordis/blob/main/packages/hmr/tests/cordis.yml) that demonstrates the expected structure:

plugins:
  - name: example
    config:
      enabled: true
      endpoint: https://api.example.com

You can load this file at startup by parsing it with a YAML library and passing the result to the context constructor:

import yaml from 'js-yaml'
import { readFileSync } from 'fs'
import { createContext } from '@cordis/loader'

const raw = readFileSync('cordis.yml', 'utf8')
const cfg = yaml.load(raw) as Record<string, any>
const ctx = createContext(cfg)

Practical Configuration Examples

Initializing with a Custom Configuration

Create a context with predefined settings for plugins, logging, and base URLs:

import { createContext } from '@cordis/loader'

const ctx = createContext({
  baseUrl: 'https://my.api.com',
  logger: { level: 'debug' },
  plugins: [
    { name: 'database', config: { connectionString: 'postgres://localhost/db' } },
    { name: 'cache', config: { ttl: 3600 } }
  ]
})

Updating Configuration at Runtime

Modify settings dynamically by emitting the update event with the new configuration partial:

// Reduce log verbosity and disable a plugin without restart
ctx.emit('internal/update', {
  logger: { level: 'error' },
  plugins: [
    { name: 'cache', config: { enabled: false } }
  ]
})

Entry-Specific Configuration Access

Inside a plugin or service, access your assigned configuration slice through the entry's config property:

export default class MyService {
  constructor(ctx: Context) {
    const entry = ctx.get('loader').getEntry(this)
    const myConfig = entry.config
    
    console.log(`Endpoint configured as: ${myConfig.endpoint}`)
  }
}

Summary

Frequently Asked Questions

How do I update Cordis configuration at runtime without restarting the application?

Emit the internal/update event on the context with the new configuration object. The loader listens for this event at line 74 of the loader index and automatically propagates changes to all registered entries and groups.

What is the difference between entry config and group config in Cordis?

Entry config applies to individual plugins or services and is processed through the interpolate function to resolve context variables. Group config manages collections of entries; when updated, it cascades the configuration to all members through the [update method in group.ts](https://github.com/cordiverse/cordis/blob/main/packages/loader/src/config/group.ts#L79).

Can I use YAML files instead of JavaScript objects to configure Cordis?

Yes. Cordis accepts any plain object as configuration, so you can parse YAML files using libraries like js-yaml and pass the resulting object to createContext(). The test suite provides a working example of the expected YAML structure.

Where is the configuration stored internally within the Cordis framework?

The configuration is stored as a public property config on the Loader class instance, which is typically attached to the global context. Entries access their specific configuration slice through the getter defined in [packages/loader/src/config/entry.ts](https://github.com/cordiverse/cordis/blob/main/packages/loader/src/config/entry.ts#L80), which interpolates values against the current context state.

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 →