Cordis Architecture: Understanding the Relationship Between Context, Service, and Fiber

In Cordis, Context acts as the global service container that instantiates the root Fiber, while Service provides the base abstraction that connects user-defined functionality to the fiber's lifecycle management system.

Cordis is a hot-reloadable plugin framework designed for complex Node.js applications. Its Cordis architecture revolves around three tightly-coupled core abstractions that work together to provide dependency injection, lifecycle management, and stateful service orchestration.

The Three Pillars of Cordis Architecture

Context: The Service Container

The Context class serves as the root environment and dependency container. When instantiated, it immediately creates the root Fiber that drives the entire application lifecycle.

In packages/core/src/context.ts (lines 36-48), the constructor establishes this relationship:

this.fiber = new Fiber(self, {}, Object.create(null), null, () => [])

The Context holds global state, manages configuration isolation via [symbols.isolate] maps, and maintains intercept maps ([symbols.intercept]) for dependency injection. All services receive the same Context instance, giving them shared access to core facilities like events, logger, reflect, and registry.

Fiber: The Lifecycle Engine

The Fiber class manages the execution lifecycle of plugins and the root context itself. It runs effects, tracks disposables, and coordinates reload/unload cycles with a sophisticated state machine (FiberState).

Each Fiber maintains a back-reference to its Context (public readonly ctx: Context) as implemented in packages/core/src/fiber.ts. The root fiber handles application-wide effects, while child fibers manage individual plugin scopes. Services interact with the fiber primarily through the effect() method (lines 75-84), which registers cleanup-aware side effects that automatically dispose when the plugin reloads or unloads.

Service: The Developer Abstraction

The Service base class provides the standard API for building user-defined services (timers, loaders, database connectors). It abstracts the underlying context and fiber mechanics into a convenient interface.

In packages/core/src/service.ts (lines 33-35), the constructor stores the context reference and registers the instance with the reflection system:

this.ctx = ctx
ctx.reflect.provide(name, self, this[symbols.check])

Because services hold the context, they can access the fiber via this.ctx.fiber to invoke lifecycle methods like effect(), update(), and restart().

How Context, Service, and Fiber Interact

The relationship between these components follows a specific initialization and communication pattern:

  1. Root Creation – Context instantiates the root Fiber during construction, establishing the execution engine for all effects.

  2. Service Registration – When extending Service, the base class automatically registers the implementation with ctx.reflect.provide(), making it discoverable by the fiber's dependency injection system.

  3. Effect Handling – Services call ctx.fiber.effect(() => { /* ... */ }) to register side effects. The fiber tracks these as disposables and executes them during reload/unload cycles with proper error handling and ordering guarantees.

  4. State Coordination – Fiber maintains a FiberState machine (pending, loading, active, failed, disposed, unloading). Services request state transitions indirectly via ctx.fiber.update(config) or ctx.fiber.restart(), while the fiber manages the actual transition logic and notifies listeners through the internal/status event channel.

  5. Isolation Propagation – When spawning child fibers for plugins, the system copies parent intercepts (ctx.intercept(name, config)) and checks implementations (_checkImpl) against the reflection registry to determine plugin activation status, as seen in packages/core/src/fiber.ts lines 38-45 and 71-83.

Practical Implementation Examples

Creating the Root Context

Every Cordis application begins with a Context, which automatically initializes the root fiber:

import { Context } from 'cordis';

// Instantiates Context and creates root Fiber automatically
const ctx = new Context();

// Access lifecycle engine
console.log(ctx.fiber.name); // → 'root'

Defining a Custom Service

Services extend the base Service class and leverage the fiber for lifecycle-aware operations:

import { Service, Context } from 'cordis';

class TimerService extends Service {
  constructor(ctx: Context) {
    super(ctx, 'timer');
    
    // Register effect with automatic cleanup
    this.ctx.fiber.effect(() => {
      const timer = setInterval(() => {
        this.ctx.logger.info('tick');
      }, 1000);
      
      // Return disposer for hot-reload support
      return () => clearInterval(timer);
    }, 'timer.tick');
  }
}

Service Registration and Hot Reload

Register the service and utilize the fiber's update capabilities:

// Instantiate and register
const timer = new TimerService(ctx);

// Trigger hot-reload with new configuration
await ctx.fiber.update({ /* new config */ });

Low-Level Fiber Access

For scenarios requiring direct interaction outside the Service abstraction:

// Schedule standalone effect
ctx.fiber.effect(() => {
  console.log('One-off effect executed');
  // Return optional cleanup function
  return () => console.log('Cleanup executed');
});

Core Source Files and Their Roles

The Cordis architecture implementation spans these critical files in the cordiverse/cordis repository:

Summary

  • Context supplies the shared runtime environment and creates the root fiber upon instantiation.
  • Fiber serves as the execution engine, managing effects, state transitions, and cleanup cycles for plugins and services.
  • Service provides the developer-facing abstraction that bridges user code to the underlying fiber lifecycle system through the context reference.
  • The three components form a closed loop: Context creates Fiber, Service receives Context (and thus Fiber), and Fiber orchestrates the lifecycle of Services.

Frequently Asked Questions

What is the difference between Context and Fiber in Cordis?

Context is the static container holding configuration, services, and state, while Fiber is the dynamic execution engine managing lifecycles and effects. The Context instantiates the Fiber (this.fiber = new Fiber(...)) and stores a reference to it, but the Fiber controls when code runs, stops, and cleans up. Think of Context as the environment and Fiber as the process scheduler.

How does Cordis handle hot-reloading of services?

Cordis handles hot-reloading through the Fiber's effect system. When ctx.fiber.update(config) is called, the Fiber transitions through states (loading, active, unloading) and executes all registered disposables from previous effect() calls. Services don't manage reload logic directly; they register cleanup functions when calling effect(), and the Fiber guarantees these run before reinitializing with new configuration.

Why does Service need to register with ctx.reflect?

Registration with ctx.reflect.provide() (as seen in packages/core/src/service.ts) makes the service discoverable by Cordis's dependency injection system. When the Fiber checks implementations (_checkImpl) or resolves dependencies for plugins, it queries the reflection registry to locate service instances. Without this registration, the Fiber cannot inject the service into dependent components or verify that required dependencies exist.

Can I access the Fiber directly without using the Service base class?

Yes. While extending Service provides conveniences like automatic reflection registration, you can interact with the Fiber directly through any Context instance. Simply call ctx.fiber.effect(), ctx.fiber.update(), or other Fiber methods directly. This pattern is useful for lightweight scripts or when integrating Cordis into existing codebases where inheriting from Service isn't practical.

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 →