# How Cordis Implements Spatiotemporal Composability: Isolating Space and Time

> Discover how Cordis implements spatiotemporal composability by separating spatial and temporal concerns. Learn how plugins are isolated and reloaded independently. Understand Cordis's innovative approach to state management.

- Repository: [Cordiverse/cordis](https://github.com/cordiverse/cordis)
- Tags: deep-dive
- Published: 2026-08-23

---

**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`](https://github.com/cordiverse/cordis/blob/main/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`](https://github.com/cordiverse/cordis/blob/main/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:

```typescript
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:

```typescript
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`](https://github.com/cordiverse/cordis/blob/main/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`](https://github.com/cordiverse/cordis/blob/main/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`](https://github.com/cordiverse/cordis/blob/main/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`](https://github.com/cordiverse/cordis/blob/main/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`](https://github.com/cordiverse/cordis/blob/main/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.