Spec — Cambium Signal Protocol
Document Status
Draft V1
Purpose
This spec defines the nervous system of Cambium: how layers, agents, and subsystems communicate through structured signals rather than flat event logs.
The event backbone in the Core+Hypha PRD defines the infrastructure. This spec defines the biology on top of it — signal types, propagation rules, cascade chains, decay, and receptor contracts.
Without this, Cambium is a modular monolith with good logging. With this, it is an organism that responds, adapts, and coordinates through biochemical-grade communication.
Core Concept: Signals vs Events
An event is a record of something that happened. It is emitted, stored, and queryable.
A signal is an event that carries intent and triggers response. It has: - a type that maps to a biological communication class - a source (which layer or agent emitted it) - a affinity (which receptor types should bind to it) - a intensity (how urgent or severe) - a decay rate (how quickly it loses relevance) - a cascade potential (can it trigger secondary signals)
Every signal is also an event. Not every event is a signal.
Signal Classes
Signals are organized into biological communication classes.
1. Autonomic Signals
The heartbeat and vital signs of the platform.
Biological analog: Autonomic nervous system — involuntary, continuous, life-sustaining.
Examples:
- pulse.heartbeat — periodic liveness check from each subsystem
- pulse.subsystem_alive — response to heartbeat probe
- pulse.subsystem_degraded — subsystem responding but unhealthy
- pulse.subsystem_dead — no response within timeout
- pulse.rhythm_irregular — heartbeat interval variance exceeds threshold
Properties: - Emitted on fixed intervals (configurable per subsystem, default 10s) - Never decay — absence of pulse IS the signal - Mandatory for all registered subsystems and agents - Monitored by Homeostasis layer
2. Stress Signals
System health and resource pressure.
Biological analog: Cortisol, adrenaline — hormonal stress response.
Examples:
- stress.latency_spike — execution latency exceeds threshold
- stress.queue_pressure — work queue depth exceeds threshold
- stress.token_burn — token consumption rate exceeds budget
- stress.error_rate — error frequency exceeds threshold
- stress.context_pressure — context window utilization high
- stress.resource_exhaustion — memory, CPU, or connection pool pressure
Properties: - Emitted by Health layer based on metric thresholds - Consumed by Hypha (for throttling/rerouting), Loom (for display), Immune (for pattern detection) - Decay: 5-minute half-life by default (configurable) - Can cascade: sustained stress → immune anomaly
3. Immune Signals
Anomaly detection and trust events.
Biological analog: Cytokines, interferons — immune cell communication.
Examples:
- immune.anomaly_detected — behavioral pattern flagged
- immune.quarantine_requested — recommendation to isolate a run or agent
- immune.quarantine_enforced — isolation applied
- immune.trust_degraded — agent or integration trust score reduced
- immune.pattern_violation — execution deviates from expected pattern
- immune.escalation_required — severity exceeds auto-response threshold
Properties: - Emitted by Immune layer - Consumed by Quorum (for governance escalation), Hypha (for pause/block), Loom (for visibility), Health (for stress correlation) - Decay: 30-minute half-life (configurable, severe signals persist longer) - Can cascade: immune escalation → quorum gate
4. Governance Signals
Approval, decision, and policy events.
Biological analog: Quorum sensing molecules (autoinducers) — collective threshold signaling.
Examples:
- governance.approval_required — action gated pending review
- governance.approval_granted — threshold met, proceed
- governance.approval_denied — action rejected
- governance.escalation_triggered — severity pushed to higher review
- governance.policy_activated — policy bound to action
- governance.override_applied — operator override with audit
Properties: - Emitted by Quorum layer - Consumed by Hypha (for gate/resume), Loom (for approval UI), Audit (for records) - No decay — governance decisions are permanent records - Can cascade: denial → reroute signal to Hypha
5. Coordination Signals
Work routing, agent availability, and stigmergic markers.
Biological analog: Pheromones, chemotaxis gradients — environmental coordination.
Examples:
- coord.work_available — task ready for assignment
- coord.agent_ready — agent available for work
- coord.agent_busy — agent at capacity
- coord.path_hot — execution path seeing heavy traffic
- coord.path_cold — execution path underutilized
- coord.resource_depleted — resource exhausted at this node
- coord.resource_abundant — surplus available
- coord.marker_placed — stigmergic marker deposited
- coord.marker_decayed — stigmergic marker expired
Properties: - Emitted by agents, Hypha, and the stigmergic environment - Consumed by Hypha (for routing), agents (for self-selection), Loom (for topology display) - Decay: configurable per marker type, default 2-minute half-life - Can cascade: resource depletion → stress signal
6. Lifecycle Signals
Agent and workflow lifecycle transitions.
Biological analog: Growth factors, apoptosis signals — cell lifecycle regulation.
Examples:
- lifecycle.agent_genesis — new agent instantiated from template
- lifecycle.agent_activated — agent online and ready
- lifecycle.agent_hibernating — agent dormant, preserving state
- lifecycle.agent_decommissioned — agent removed from active pool
- lifecycle.agent_composted — learnings extracted and fed back
- lifecycle.workflow_created — new workflow definition
- lifecycle.run_started — execution begun
- lifecycle.run_completed — execution finished
- lifecycle.checkpoint_created — seed layer snapshot
Properties: - Emitted by Genesis system, Hypha, Seed layer - Consumed by all layers as needed - No decay — lifecycle transitions are permanent records
7. Recovery Signals
Checkpoint, replay, and restoration events.
Biological analog: Wound healing cascade — hemostasis, inflammation, proliferation, remodeling.
Examples:
- recovery.checkpoint_available — valid recovery point exists
- recovery.replay_initiated — replay from checkpoint started
- recovery.resume_initiated — run resuming from pause
- recovery.rollback_initiated — reverting to prior state
- recovery.restoration_complete — recovery finished
- recovery.restoration_failed — recovery attempt unsuccessful
Properties: - Emitted by Seed layer - Consumed by Hypha (for execution), Loom (for operator visibility), Audit (for records) - No decay - Can cascade: restoration failure → immune anomaly
Signal Structure
Every signal follows this contract:
interface Signal {
// Identity
id: string; // unique signal ID
type: string; // e.g., "stress.latency_spike"
class: SignalClass; // autonomic | stress | immune | governance | coordination | lifecycle | recovery
// Source
source_layer: string; // which layer emitted (hypha, health, immune, quorum, seed, agent, stigmergy)
source_id: string; // specific emitter ID
// Context
workspace_id: string;
workflow_run_id?: string;
task_execution_id?: string;
agent_id?: string;
// Signal properties
intensity: number; // 0.0 - 1.0 (severity/urgency)
decay_rate: number; // half-life in seconds (0 = permanent)
cascade_potential: boolean; // can this trigger secondary signals
payload: Record<string, any>; // type-specific data
// Receptor targeting
affinity: string[]; // receptor types that should bind (e.g., ["hypha.router", "loom.display", "quorum.gate"])
// Timestamps
emitted_at: string; // ISO timestamp
expires_at?: string; // computed from decay_rate
}
Receptor Model
Each layer and agent declares its receptors — signal types it binds to and how it responds.
interface Receptor {
id: string;
owner: string; // layer or agent ID
binds_to: string[]; // signal type patterns (e.g., "stress.*", "immune.escalation_required")
response: ReceptorResponse; // what happens on binding
threshold?: number; // minimum intensity to trigger (0.0 - 1.0)
cooldown?: number; // minimum ms between responses
}
interface ReceptorResponse {
action: string; // e.g., "throttle", "pause", "display", "escalate", "log"
config: Record<string, any>; // action-specific parameters
emit_signal?: string; // optional: cascade by emitting a new signal
}
Default Receptor Bindings
| Layer | Binds To | Response |
|---|---|---|
| Hypha | governance.approval_granted |
Resume gated execution |
| Hypha | governance.approval_denied |
Reroute to fallback or fail |
| Hypha | stress.* (intensity > 0.7) |
Throttle execution rate |
| Hypha | immune.quarantine_enforced |
Pause affected runs |
| Hypha | coord.agent_ready |
Consider for task dispatch |
| Hypha | recovery.replay_initiated |
Execute from checkpoint |
| Health | stress.* |
Update health state model |
| Health | pulse.* |
Update liveness state |
| Health | coord.resource_depleted |
Emit stress signal if systemic |
| Immune | stress.* (sustained) |
Evaluate for anomaly pattern |
| Immune | lifecycle.agent_genesis |
Monitor new agent behavior |
| Immune | governance.override_applied |
Flag for review |
| Quorum | immune.escalation_required |
Create approval gate |
| Quorum | stress.* (intensity > 0.9) |
Emergency governance check |
| Seed | lifecycle.run_started |
Evaluate checkpoint policy |
| Seed | immune.quarantine_enforced |
Create emergency checkpoint |
| Loom | * (all signals) |
Display in operator console |
Cascade Chains
These are the hardwired critical paths — signals that MUST propagate regardless of subscription state.
Chain 1: Stress Escalation
stress.* (sustained > 5min) → immune.anomaly_detected → [if severity > 0.8] → governance.escalation_required → quorum gate
Chain 2: Immune Emergency
immune.quarantine_requested → [auto-approve if severity > 0.9] → immune.quarantine_enforced → hypha.pause + seed.emergency_checkpoint
Chain 3: Liveness Failure
pulse.subsystem_dead → stress.resource_exhaustion → immune.anomaly_detected → [if critical subsystem] → governance.escalation_required
Chain 4: Recovery Failure
recovery.restoration_failed → immune.anomaly_detected → stress.error_rate → [if repeated] → governance.escalation_required
Chain 5: Agent Distress
coord.resource_depleted (from agent) → stress.resource_exhaustion → health.state_change → hypha.reroute
Signal Propagation Model
Primary: Subscribe-Based
- Layers register receptors for signal types they care about
- Signal bus delivers matching signals to registered receptors
- Receptors filter by intensity threshold before triggering response
- This handles 90% of communication
Secondary: Hardwired Cascades
- Critical safety paths are wired directly (cascade chains above)
- These fire regardless of subscription state
- Cannot be disabled without explicit operator override + audit record
- This prevents the system from becoming deaf to its own distress
Signal Bus Implementation
- In-process pub/sub for V1 (modular monolith)
- Signals persisted to event store before delivery
- Delivery is at-least-once within the process
- Failed receptor responses are retried with backoff
- Dead-letter handling for persistently failed deliveries
Heartbeat Protocol
The heartbeat is a first-class autonomic function, not a health metric.
How It Works
- A Pulse Monitor (part of Cambium Core) sends
pulse.heartbeatprobes to all registered subsystems and agents on a fixed interval - Each subsystem/agent responds with
pulse.subsystem_aliveorpulse.subsystem_degraded - No response within timeout →
pulse.subsystem_deademitted - Irregular response intervals →
pulse.rhythm_irregularemitted - Health layer consumes pulse signals as primary liveness input
Configuration
interface HeartbeatConfig {
interval_ms: number; // default: 10000 (10s)
timeout_ms: number; // default: 5000 (5s)
irregular_threshold_ms: number; // default: 3000 (3s variance)
max_missed_before_dead: number; // default: 3
}
Subsystem Registration
Every layer and agent must register with the Pulse Monitor on startup and respond to heartbeat probes. This is not optional. An unregistered subsystem is invisible to the organism.
Signal Decay Model
Signals decay over time to prevent stale information from driving decisions.
effective_intensity = original_intensity * (0.5 ^ (elapsed_seconds / half_life_seconds))
- When
effective_intensitydrops below 0.05, the signal is considered expired - Expired signals are retained in the event store for audit but removed from active signal state
- Permanent signals (decay_rate = 0) never expire: governance decisions, lifecycle events, audit records
Open Questions
- Should signal intensity be computed or declared by the emitter?
- What is the maximum cascade depth before circuit-breaking?
- Should agents be able to define custom signal types, or only emit from the predefined taxonomy?
- How should conflicting signals from multiple sources be resolved (e.g., two health assessments disagree)?