Skip to main content

CNStra API Reference - Neurons, Signals, Collaterals, Context

collateral<T>(id: string)

Create a typed output channel.

const userEvent = collateral<{ userId: string }>('user:event');
const simpleEvent = collateral('simple:event');

collateral.createSignal(payload?)

Create a signal from a collateral. Payload is optional for collaterals without payload type.

const signal = userEvent.createSignal({ userId: '123' });
const emptySignal = simpleEvent.createSignal(); // no payload

neuron(id: string, axon: Axon)

Create a neuron with the given axon.

const myNeuron = neuron('my-neuron', { output: myCollateral });

Signal ownership

Signal ownership

A neuron may emit only collaterals declared in its own axon. It must not emit another neuron's collaterals. Cross-neuron orchestration is done by having a controller own request collaterals and letting each domain neuron emit its own responses.

Incorrect (emits someone else's collateral):

// DON'T: myNeuron emits otherAxon.some
return otherAxon.some.createSignal(result);

Correct (controller-owned request, domain emits its own):

const controller = neuron('controller', { requestA });
const serviceA = neuron('serviceA', { doneA })
.dendrite({ collateral: requestA, response: (_, axon) => axon.doneA.createSignal(...) });
// controller emits requestA; serviceA emits doneA

neuron.dendrite({...})

Add a dendrite bound to a collateral.

myNeuron.dendrite({
collateral: inputCollateral,
response: async (payload, axon, ctx) => {
// ctx: per-neuron per-stimulation context (metadata storage)
if (ctx.abortSignal?.aborted) return;
// Business data flows through payloads, not context
return axon.output.createSignal(result);
}
});

neuron.bind(axon, map)

Exhaustive bind to every collateral of another neuron's axon (compile-time safety).

// Context is for per-neuron per-stimulation metadata, not business data
withCtx<{ attempt: number }>().neuron('order-mailer', {})
.bind(order, {
created: (payload, axon, ctx) => {
// Context stores per-neuron per-stimulation metadata
const attempt = (ctx.get()?.attempt || 0) + 1;
ctx.set({ attempt });
// Business data flows through payloads
},
updated: (payload) => { /* ... */ },
cancelled: (payload) => { /* ... */ },
});

neuron.setConcurrency(n: number | undefined)

Set per-neuron global concurrency limit (shared across all parallel stimulations).

const worker = neuron('worker', { out })
.setConcurrency(2) // max 2 parallel executions across all runs
.dendrite({ collateral: task, response: async (p, axon) => { /* ... */ } });

This limits how many concurrent executions of this neuron's dendrites can run at the same time, even across different stimulate() calls. Useful for rate-limiting external APIs or heavy I/O operations.

neuron.setMaxDuration(ms: number | undefined)

Set maximum execution duration for this neuron's dendrites. If exceeded, the dendrite execution will be aborted.

const worker = neuron('worker', { out })
.setMaxDuration(5000) // 5 seconds max
.dendrite({ collateral: task, response: async (p, axon) => { /* ... */ } });

CNS

Main orchestrator. new CNS(neurons, options?)

Constructor options:

const cns = new CNS(neurons, {
autoCleanupContexts: false // Auto-cleanup unused contexts (performance warning: O(V²) cost)
});

Properties:

  • cns.network — Access to network graph analysis (SCC, subscribers, etc.)
const unsubscribe = cns.addResponseListener(r => { /* ... */ });

cns.stimulate(signal, options?)

Run a stimulation. Returns a CNSStimulation instance. Use stimulation.waitUntilComplete() to await completion.

const stimulation = cns.stimulate(userCreated.createSignal({ id: '123', name: 'John' }));
await stimulation.waitUntilComplete();

cns.activate(tasks, options?)

Start a stimulation with activation tasks directly. Useful for advanced scenarios where you need fine-grained control over task execution.

const tasks: TCNSNeuronActivationTask[] = [
{ stimulationId: 'run-1', neuronId: 'worker', dendriteCollateralName: 'task', input: signal }
];
const stimulation = cns.activate(tasks, { concurrency: 4 });
await stimulation.waitUntilComplete();

Single entry point

stimulate(...) and activate(...) are the entry points that begin execution. Nothing runs until you explicitly stimulate a signal or activate tasks. This is the "inverted" part of CNS: you start the run and each dendrite returns the explicit continuation.

Stimulation options

const stimulation = cns.stimulate(signal, {
onResponse: (r) => { /* per-stimulation hook */ },
abortSignal, // Abort the whole run cooperatively
concurrency: 4, // Per-stimulation parallelism
maxNeuronHops: undefined, // Disabled by default; set to cap traversal length
allowName: (neuronName) => true, // Filter allowed neurons by name
stimulationId: 'run-123', // Optional id for tracing
ctx, // Pre-supplied context store
contextValues: { 'neuron-id': { attempt: 0 } }, // Pre-populate context values
});
await stimulation.waitUntilComplete();

Response shape (for listeners)

Both onResponse and global listeners receive the same object:

{
inputSignal?: TCNSSignal; // when a signal is ingested
outputSignal?: TCNSSignal; // when a dendrite returns a continuation
contextValue: TCNSStimulationSerializedContextValue; // per-neuron per-stimulation metadata (not business data)
queueLength: number; // current work queue size
stimulation: CNSStimulation; // reference to the stimulation instance
error?: Error; // when a dendrite throws
hops?: number; // present if maxNeuronHops is set
stimulationId?: string; // unique identifier for this stimulation
}

Global response listeners (middleware‑style)

Use addResponseListener to attach cross‑cutting concerns (logging, metrics, tracing) that run for every stimulation.

const off = cns.addResponseListener((r) => {
if (r.error) {
metrics.count('error', 1);
return;
}
if (r.outputSignal) {
trace.log('out', r.outputSignal.collateralName);
} else if (r.inputSignal) {
trace.log('in', r.inputSignal.collateralName);
}
});

// later
off();

CNSStimulation methods

stimulation.waitUntilComplete()

Wait for the stimulation to complete. Returns a Promise that resolves when all tasks are done or rejects on error.

await stimulation.waitUntilComplete();

stimulation.getContext()

Get the context store for this stimulation. Useful for saving/restoring stimulation state.

const ctx = stimulation.getContext();
// Save for retry
const savedCtx = ctx;

stimulation.getFailedTasks()

Get all tasks that failed or were aborted.

const failures = stimulation.getFailedTasks();
failures.forEach(f => console.error(f.error));

cns.network — Network Graph Analysis

Access network analysis utilities:

// Get subscribers for a collateral
const subscribers = cns.network.getSubscribers('user:created');

// Get neuron by name
const neuron = cns.network.getNeuronByName('user-service');

// Get collateral by name
const collateral = cns.network.getCollateralByName('user:created');

// Get all neurons
const neurons = cns.network.getNeurons();

// Get all collaterals
const collaterals = cns.network.getCollaterals();

// Get strongly connected components
const sccs = cns.network.stronglyConnectedComponents;

Notes

  • Local onResponse (per stimulation) runs as well as global listeners; both can be async.
  • All listeners run in parallel per response; errors from any listener reject the stimulation.waitUntilComplete() Promise.
  • If all listeners are synchronous, no extra async deferrals are introduced.
  • Use allowName/maxNeuronHops to constrain traversal if needed.