SignalPy Kernel

A reactive component kernel for backend services

The Idea

Frontend frameworks like Vue and React changed how UIs work: you declare what depends on what, and when data changes, the framework updates everything automatically. No manual callbacks. No stale state.

SignalPy brings this model to backend services.

Your components declare dependencies. The kernel wraps every injected service in a reactive Signal. When you read self.rt.config inside an @effect, the kernel tracks that read. When the config service changes — new provider hot-added, config pushed, service ranking changed — your effect re-runs automatically.

from signalpy.kernel import component, requires, computed, effect, runnable
from signalpy.kernel.contracts import IConfig

@component("greeter")
@requires(config=IConfig)
class Greeter:

    @computed                                        # cached, auto-recomputes
    def greeting(self):
        return self.rt.config.get("greeting", "Hello")

    @effect                                          # re-runs when deps change
    def log_greeting(self):
        print(f"Greeting is now: {self.greeting()}")

    @runnable("greet", params=GreetParams, description="Greet someone")
    async def greet(self, params):
        return {"message": f"{self.greeting()}, {params.name}!"}

No @bind. No @unbind. No manual event wiring. The reactive graph handles propagation.

How Reactivity Works

Three primitives, implemented in 327 lines of pure Python:

Primitive What it does In the kernel
Signal Holds a value. Reading tracks the caller. Writing notifies all readers. Each self.rt.config, self.rt.db is a Signal
Computed Derives a value from Signals. Caches. Recomputes only when deps change. @computed decorator on component methods
Effect Runs a function. Re-runs when any Signal it read changes. @effect decorator on component methods

sequenceDiagram
    participant S as Signal (self.rt.config)
    participant E as @effect method
    participant C as @computed property

    Note over S,C: Boot: effect runs once, tracks deps
    E->>S: reads config → tracked
    S-->>E: subscriber registered

    Note over S,C: Later: config changes
    S->>E: notify → effect re-runs
    S->>C: notify → computed marked dirty
    C->>C: next read → recomputes

When you write self.rt.config.get("url") inside an @effect:

  1. self.rt.config → calls Signal.get() → kernel records “this Effect depends on this Signal”
  2. Later, config changes → Signal.set(new_config) → kernel notifies all subscribers
  3. Your @effect re-runs automatically — reads the new config, does whatever it needs

This is Vue 3’s reactivity model applied to service injection. Same algorithm (dependency tracking via a context variable), same composability (Computed chains, Effects compose), different domain (backend services instead of DOM).

The 13 Decorators

@component  @provides  @requires           # core
@computed   @effect                         # reactive
@lifecycle.activate/deactivate/health       # lifecycle
@runnable   @api                            # surface
@prop   @kind   @skill                      # metadata
@subscribe                                  # events

Learn

Tutorials teach one concept at a time. Each builds on the last.

# Tutorial Concepts
1 First Component @component, @lifecycle, boot, shutdown
2 Give and Take @provides, @requires, typed contracts, self.rt
3 Dynamic Services list[C] aggregate, @effect, hot_add/hot_remove
4 Runnables & API @runnable, @api, bus invocation, transports
5 Auth & Policy requires_action, requires_role, bus-level auth
6 Building a Provider Write your own ICache or IDatabase, bridge pattern
7 Commercial Patterns Secret rotation, A/B testing, multi-tenant, extension bundles, circuit breaker, audit trail

Reference

Page What’s in it
Traits (L0–L3) 23 traits across 4 levels — what each is, how you get it, example
Decorators All 12 decorators — signature, parameters, example
Contracts 8 Protocol interfaces — methods, providers, usage
Kernel API Kernel, ServiceRegistry, Bus, Runtime, Signal/Computed/Effect

Concepts

Read in this order — each builds on the last:

Page What it explains
Architecture Constitution, two-axis model, what’s in the kernel
Reactivity by Example Consumer / Provider / Kernel side-by-side, one full cycle from boot to re-run
Context, ContextVar, Active Consumer Why a single _active_consumer ContextVar is enough for many effects
Reactive Engine: Line by Line Every line of reactive.py annotated
Threading Model Threads, asyncio, RLock, ContextVar isolation, vs stdlib
Bridging External Systems Bridge pattern, three access levels, 7 rules
Deployment Scales Monolith → containers → Dapr → agent system

Examples

Example Domain Run
01_hello.py PYTHONPATH=src python src/signalpy/examples/01_hello.py
02_give_and_take.py IoT/sensors PYTHONPATH=src python src/signalpy/examples/02_give_and_take.py
03_reactive_config.py Data pipeline PYTHONPATH=src python src/signalpy/examples/03_reactive_config.py
04_dynamic_plugins.py Alerting PYTHONPATH=src python src/signalpy/examples/04_dynamic_plugins.py
05_auth_protected_api.py CMS PYTHONPATH=src python src/signalpy/examples/05_auth_protected_api.py
06_multi_provider_ranking.py Database PYTHONPATH=src python src/signalpy/examples/06_multi_provider_ranking.py
07_fastapi_full.py Web app PYTHONPATH=src python src/signalpy/examples/07_fastapi_full.py
secret_rotation.py Credential mgmt PYTHONPATH=src python -m signalpy.examples.secret_rotation
ab_testing.py Experimentation PYTHONPATH=src python -m signalpy.examples.ab_testing
multi_tenant.py SaaS / multi-tenant PYTHONPATH=src python -m signalpy.examples.multi_tenant
extension_bundle.py Plugin system PYTHONPATH=src python -m signalpy.examples.extension_bundle
circuit_breaker.py Resilience PYTHONPATH=src python -m signalpy.examples.circuit_breaker
audit_trail.py Compliance PYTHONPATH=src python -m signalpy.examples.audit_trail

At a Glance

Kernel core ~2600 lines, 9 files, zero required dependencies
Decorators 12: @component, @provides, @requires, @computed, @effect, @runnable, @api, @lifecycle.*, @prop, @kind, @skill, @subscribe
Reactivity Signal, Computed, Effect — Vue 3-style, zero dependencies
Providers 9: config, logging, credentials, storage, auth, tracing, workspace, gateway, configadmin
Adapters 3: REST (FastAPI), MCP, CLI (Click)
Tests 58
Traits 23 across 4 levels (L0 Kernel, L1 Platform, L2 App, L3 Instance)