How Cordis Implements Spatiotemporal Composability: Isolating Space and Time

Cordis achieves spatiotemporal composability by strictly separating spatial concerns (service scopes via Context) from temporal concerns (lifecycle management via Fiber), allowing plugins to be isolated logically and reloaded independently without state leakage.

The Cordis framework (available at cordiverse/cordis) provides a novel architecture for building modular, hot-reloadable applications. By treating space as the logical domain where services reside and time as the execution lifecycle of those services, Cordis enables developers to compose complex systems that can be modified at runtime without cascading failures.

Spatial Composability Through Context Isolation

Spatial composability in Cordis refers to the ability to create isolated logical scopes where services and plugins operate independently. This prevents state leakage between different functional areas of an application.

The Context.isolate() Method

In packages/core/src/context.ts, the Context class provides the isolate() method that generates isolated sub-contexts. When called, it returns a new context that inherits the parent's traceable state while maintaining a private isolation map stored in symbols.isolate at line 65.

This mechanism ensures that plugins registered within one isolated context cannot access or interfere with services in sibling contexts. Each isolated context maintains its own registry entries, interceptors, and symbol definitions.

Private Isolation Maps and Symbol Scoping

The isolation map acts as a boundary that separates the symbol registry of parent and child contexts. When you create an isolated space using root.isolate('user'), the resulting context receives a fresh namespace while preserving the ability to trace back to its origin. This spatial separation is fundamental to multi-tenant architectures where user and admin domains must remain distinct.

Temporal Composability via the Fiber System

While Context handles where code runs, the Fiber class in packages/core/src/fiber.ts manages when code runs and for how long. Fibers model the temporal dimension through explicit lifecycle management and effect registration.

The Fiber Class and Lifecycle Management

Each Fiber instance represents a running plugin instance and maintains its own FiberState. The class tracks a private _disposables array that stores cleanup functions, ensuring that resources are released when a fiber unloads or restarts.

The constructor at line 33 receives a parent Context, which may itself be an isolated context from the spatial layer. This injection links the temporal fiber to a specific spatial scope, creating the foundation for spatiotemporal coordination.

Effect Registration and Disposable Management

The Fiber.effect() method at line 75 provides the primary API for temporal composition. Developers register effects—functions that execute when the fiber loads—that automatically clean up when the fiber unloads:

userSpace.fiber.effect(() => {
  console.log('User plugin started')
  return () => console.log('User plugin stopped')
}, 'user-plugin')

Effects are composable and can be reordered or restarted independently. When Fiber.restart() is called, the system triggers a complete unload-reload cycle of all registered effects while preserving the original spatial isolation.

Bridging Space and Time: The Spatiotemporal Integration

Cordis unifies its spatial and temporal layers through specific architectural bridges that allow contexts and fibers to interact without breaking isolation boundaries.

Fiber Injection of Isolated Contexts

When instantiating a new fiber via new Fiber(parent, ...), the constructor injects the parent context. If this parent was created via Context.isolate(), the fiber automatically operates within that isolated spatial scope. This means calling Fiber.update or Fiber.restart triggers temporal changes that respect the original spatial boundaries.

Epoch-Based Reloading and State Propagation

The _refresh() method at line 85 implements the temporal update mechanism by computing an "epoch" string based on impl.fiber.uid. When implementations change epochs, the fiber schedules _reload or _unload operations.

This epoch-based system enables efficient change propagation across isolated fibers. When one fiber updates its configuration, dependent fibers can react to these changes through the RegistryService and ReflectService without directly accessing each other's internal state. The Impl class tracks its owning fiber via impl.fiber at line 66, allowing the system to locate and notify only the relevant spatial slices when temporal events occur.

Practical Implementation Example

The following example demonstrates both spatial and temporal composition working together:

import { Context } from 'cordis'

// Spatial composition: Create isolated logical spaces
const root = new Context()
const userSpace = root.isolate('user')
const adminSpace = root.isolate('admin')

// Services remain scoped to their specific space
userSpace.registry.register('profile', () => ({ name: 'Alice' }))
adminSpace.registry.register('profile', () => ({ name: 'Bob' }))

// Temporal composition: Register lifecycle-bound effects
userSpace.fiber.effect(() => {
  console.log('User plugin started')
  return () => console.log('User plugin stopped')
}, 'user-plugin')

// Hot reload: Restart temporal flow while preserving spatial isolation
await userSpace.fiber.restart()

Summary

  • Spatial isolation is achieved through Context.isolate() in packages/core/src/context.ts, which creates private isolation maps using symbols.isolate to prevent service leakage between contexts.
  • Temporal control is managed by the Fiber class in packages/core/src/fiber.ts, which tracks lifecycle states, manages disposables via _disposables, and provides the effect() API for registration of load/unload hooks.
  • Spatiotemporal bridging occurs when fibers receive isolated contexts during construction, allowing Fiber.restart() and Fiber.update to trigger temporal reloads within fixed spatial boundaries.
  • Change propagation uses epoch-based refreshing via Fiber._refresh() and implementation tracking through impl.fiber references to notify relevant contexts without breaking isolation.

Frequently Asked Questions

How does Cordis prevent state leakage between plugins?

Cordis prevents state leakage through the Context.isolate() method, which creates isolated sub-contexts with private symbol maps. Each isolated context maintains its own registry entries in packages/core/src/context.ts, ensuring that services registered in one space cannot access or modify services in another, even when operating concurrently.

What happens to effects when a fiber restarts?

When Fiber.restart() is called, the system invokes all disposables stored in the _disposables array, runs the unload lifecycle, and then re-executes all registered effects from Fiber.effect(). This temporal reset occurs within the same spatial context, preserving isolation while refreshing the plugin's runtime state as implemented in packages/core/src/fiber.ts.

How does Cordis track which fiber owns a specific implementation?

The framework tracks ownership through the Impl class's fiber property referenced at line 66 in packages/core/src/fiber.ts. Each implementation object stores a reference to its parent fiber (impl.fiber), while RegistryService and ReflectService maintain the mappings between plugin names and their runtime instances, enabling targeted updates without scanning unrelated contexts.

Can isolated contexts share any state, or are they completely separate?

While isolated contexts maintain private symbol registries to prevent accidental leakage, they inherit traceable state from their parent contexts. This allows for intentional sharing of specific services while keeping plugin registrations and interceptors isolated, creating a hierarchical scoping mechanism rather than complete sandboxes.

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 →