What Is a Fiber in Cordis and What Is Its Purpose?

A Fiber in Cordis is the fundamental runtime unit that encapsulates a single plugin instance and its execution context, managing everything from lifecycle states and configuration validation to dependency injection and hot‑reloading.

In the cordiverse/cordis framework, every plugin you register becomes a Fiber. This architecture abstracts the complex orchestration of plugin systems, ensuring side‑effects are tracked deterministically and the runtime remains stable even when individual plugins fail.

Core Responsibilities of a Fiber

The Fiber class in packages/core/src/fiber.ts handles seven critical responsibilities that make Cordis plugins robust and predictable.

Lifecycle State Management

A Fiber tracks its current state through the FiberState enum, which defines six possible states:

  • PENDING – initial state before loading begins
  • LOADING – actively executing the plugin function
  • ACTIVE – successfully loaded and running
  • FAILED – encountered an error during load or reload
  • UNLOADING – actively disposing and cleaning up
  • DISPOSED – fully cleaned up and inactive

The internal state machine transitions between these states based on method calls and async operations. You can inspect fiber.state at any time to check a plugin's health.

Configuration Validation

Before a plugin executes, the Fiber validates its configuration against a StandardSchemaV1 definition. The resolveConfig function handles this validation, throwing a ValidationError when the provided config fails schema requirements.

This validation happens in the Fiber constructor and during fiber.update() calls, ensuring malformed configuration never reaches plugin code.

Effect Execution and Disposal

The effect method registers side‑effects that automatically clean up when the Fiber unloads:

ctx.fiber.effect(() => {
  const timer = setInterval(() => ctx.logger.info('tick'), 1000)
  return () => clearInterval(timer)  // disposer
}, 'periodic tick')

Effects can return:

  • A function (disposer)
  • An iterable of disposers
  • A promise resolving to either

The _execute helper in packages/core/src/fiber.ts manages execution and guarantees proper disposal ordering—inner effects dispose before outer effects.

Dependency Injection

When created, a Fiber instantiates a child Context that inherits from its parent while exposing itself via ctx.fiber. This context is injected into the plugin function, allowing access to services declared by other plugins.

The constructor injection logic (packages/core/src/fiber.ts#L33-L45) validates that required services exist and satisfy interface constraints before activation proceeds.

Hot‑Reloading and Inertia Locking

Cordis supports dynamic plugin updates through the _setEpoch, _reload, and _unload methods. When configuration changes or dependencies update, the Fiber:

  1. Computes a new epoch string identifying the desired state
  2. Acquires the inertia promise as a lock to prevent overlapping operations
  3. Executes unload then reload in sequence
  4. Releases the lock for the next operation

This inertia mechanism ensures consistent state even during rapid successive changes.

Error Propagation

Runtime errors in plugins transition the Fiber to FiberState.FAILED and reject the await() promise. Callers can handle failures through standard promise methods:

const badFiber = await root.plugin(faultyPlugin)
await badFiber.catch(err => {
  console.error('Plugin failed:', err)  // fiber.state === FiberState.FAILED
})

Errors in _reload and other internal methods follow the same propagation path through the await implementation (packages/core/src/fiber.ts#L22-L27).

Public Control API

The Fiber exposes three primary control methods for runtime interaction:

Method Purpose
update(config) Change configuration and trigger hot‑reload
restart() Force unload and reload with current configuration
await() Returns a promise that resolves when Fiber reaches ACTIVE or rejects on FAILED

Creating and Using Fibers

Basic Plugin Registration

Registering a plugin always returns a Fiber instance:

import { Context } from '@cordis/core'

function hello(ctx: Context) {
  ctx.logger.info('Hello, world!')
}

const root = new Context()
const fiber = await root.plugin(hello)  // ← Fiber created

await fiber  // resolves when ACTIVE

The root.plugin() method internally constructs a new Fiber, executes its loading sequence, and returns the instance for control and monitoring.

Configuration Updates and Hot‑Reload

Plugins accepting configuration can be updated dynamically:

function greeter(ctx: Context, config: { greeting: string }) {
  ctx.logger.info(config.greeting)
}

const fiber = await root.plugin(greeter, { greeting: 'Hi' })
await fiber  // ACTIVE

fiber.update({ greeting: 'Hello again' })
await fiber  // waits for reload completion

The update method triggers _setEpoch with new configuration, queueing a reload through the inertia lock system.

Fiber Implementation Architecture

The core implementation lives in packages/core/src/fiber.ts with supporting systems throughout the codebase:

Summary

  • A Cordis Fiber is the runtime container for a single plugin, managing its complete lifecycle from loading through disposal
  • State machine tracking via FiberState enum provides visibility into plugin health
  • Automatic effect disposal ensures resources clean up in correct order when plugins unload
  • Hot‑reload with inertia locking prevents race conditions during configuration changes
  • Configuration validation through StandardSchemaV1 rejects invalid configs before plugin execution
  • Promise-based control via await(), update(), and restart() enables reactive plugin management

Frequently Asked Questions

How does a Fiber differ from a Context in Cordis?

A Context (ctx) is the dependency injection container passed to plugins, while a Fiber is the lifecycle manager that creates and owns that Context. Every Fiber has exactly one Context accessible via ctx.fiber, but Contexts can form hierarchies where child contexts inherit parent services. The Fiber handles the plugin's execution state; the Context handles service resolution and event routing.

What triggers a Fiber to reload automatically?

Automatic reloads occur when: (1) fiber.update(newConfig) is called with different configuration, (2) a service declared as a dependency is added, removed, or modified in a parent context, or (3) fiber.restart() is invoked explicitly. The _setEpoch method computes a string fingerprint of the desired state; when this differs from the current epoch, the Fiber queues an unload/reload cycle through the inertia lock.

Can multiple Fibers share the same plugin function?

Yes. The plugin function is just a factory; each call to ctx.plugin(myFunction) creates a distinct Fiber with its own state, context, and configuration. Multiple Fibers from the same function operate independently—one can be ACTIVE while another is FAILED or DISPOSED, with no shared state unless explicitly designed through external variables or injected services.

What happens if a Fiber's effect throws during disposal?

Disposal errors are caught and logged, but they do not prevent subsequent effects from disposing. The Fiber proceeds through its cleanup sequence, transitioning to DISPOSED once all registered disposers have executed. If critical cleanup is required, effects should implement their own error handling and retry logic rather than relying on the Fiber's default behavior.

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 →