This document lists the public runtime APIs exposed by the Python bindings and the common Node.js equivalents where available. It is intended to be a comprehensive reference for everyday usage: spawning actors, messaging, supervision, timers, JIT/offload helpers, registry, and network helpers.
Notes:
- All Python examples assume
from iris import Runtimeorimport irisas appropriate. - Node.js examples use the shipped
NodeRuntimewrapper where shown.
Python:
from iris import Runtime
rt = Runtime()Node.js (conceptual):
const { NodeRuntime } = require('./index.js');
const rt = new NodeRuntime();spawn(handler, budget: int = 100, release_gil: bool = False) -> int- Spawn a push-based actor (green-thread).
handler(message)is called for each incoming message.
- Spawn a push-based actor (green-thread).
def handler(msg):
print('got', msg)
pid = rt.spawn(handler, budget=50)-
spawn_py_handler_bounded(handler, budget: int, capacity: int, release_gil: bool = False) -> int- Spawn a Python handler with a bounded mailbox capacity.
-
spawn_with_mailbox(handler, budget: int = 100) -> int- Spawn a pull-based actor running in a dedicated OS thread.
handler(mailbox)should callmailbox.recv().
- Spawn a pull-based actor running in a dedicated OS thread.
-
spawn_child(parent: int, handler, budget: int = 100, release_gil: bool = False) -> int- Spawn a child actor that is automatically stopped when
parentexits.
- Spawn a child actor that is automatically stopped when
-
spawn_child_pool(parent, handler, workers: int, budget: int = 100, release_gil: bool = False) -> list[int]- Spawn a persistent pool of child actors tied to
parent(returns worker PIDs).
- Spawn a persistent pool of child actors tied to
-
spawn_child_with_mailbox(parent, handler, budget: int = 100) -> int- Child variant of
spawn_with_mailbox.
- Child variant of
-
spawn_virtual(handler, budget: int = 100, idle_timeout_ms: Optional[int] = None) -> int- Reserve a PID and lazily activate the actor on first message.
-
spawn_with_path_observed(budget: int, path: str) -> int- Spawn an observed handler and register it under
path(useful for monitoring/testing).
- Spawn an observed handler and register it under
Example — push vs pull:
# push actor
rt.spawn(lambda m: print('push', m))
# pull actor
def mailbox_worker(mailbox):
while True:
msg = mailbox.recv()
if not msg:
break
print('pull', msg)
rt.spawn_with_mailbox(mailbox_worker)send(pid: int, data: bytes-like) -> bool— send user payload (bytes,bytearray,memoryview) to a PID.send_many(pid: int, payloads: Iterable[bytes-like]) -> int— batch-send payloads and return accepted count.send_named(name: str, data: bytes-like) -> bool— resolve and send by registered name.send_buffer(pid: int, buffer_id: int) -> bool— zero-copy send via buffer ID (useallocate_buffer).send_after(pid: int, delay_ms: int, data: bytes-like) -> int— schedule one-shot message; returns timer id.send_interval(pid: int, interval_ms: int, data: bytes-like) -> int— schedule repeating message; returns timer id.cancel_timer(timer_id: int) -> bool— cancel timer/interval.
Example:
timer = rt.send_after(pid, 200, b'tick')
# cancel
rt.cancel_timer(timer)
# batch send
accepted = rt.send_many(pid, [b"a", bytearray(b"b"), memoryview(b"c")])
print("accepted", accepted)mailbox_size(pid: int) -> Optional[int]— number of queued user messages, orNoneif PID unknown.mailbox_backpressure(pid: int) -> Optional[str]— inferred backpressure level for the mailbox:"NORMAL","HIGH", or"CRITICAL".send_with_backpressure(pid: int, data: bytes) -> Tuple[bool, str]— send with immediate pressure result (tight loop use case; returns send success and currentBackpressureLevel).send_user_with_backpressure(pid: int, buffer_id: int) -> Tuple[bool, str]— same as above for zero-copy buffer path.selective_recv(pid, matcher: Callable, timeout: Optional[float] = None) -> Awaitable[Optional[bytes | PySystemMessage]]— await a message that satisfiesmatcher(for observed actors).selective_recv_blocking(pid, matcher, timeout=None)— blocking convenience wrapper.
Example:
size = rt.mailbox_size(pid)
print('size=', size)register(name: str, pid: int)— register a local name.unregister(name: str)— remove registration.resolve(name: str) -> Optional[int]— local resolve.whereis(name: str) -> Optional[int]— alias forresolve.
Path-based registry:
register_path(path: str, pid: int)— register under hierarchical path (e.g.,/svc/payments/one).unregister_path(path: str)— remove a path registration.whereis_path(path: str) -> Optional[int]— exact path resolution.list_children(prefix: str)— list all descendant registrations underprefix(returns list of(path, pid)).list_children_direct(prefix: str)— list immediate children.
Example:
rt.register('auth', pid)
assert rt.whereis('auth') == pid
rt.register_path('/svc/test/one', pid)
print(rt.list_children('/svc/test'))create_path_supervisor(path: str)— create path-scoped supervisor.remove_path_supervisor(path: str)— remove it.path_supervisor_watch(path: str, pid: int)— register pid with path supervisor.path_supervisor_children(path: str)— list children supervised by path supervisor.path_supervise_with_factory(path: str, pid: int, factory, strategy: str)— attach a Python factory to restart actors (restartone/restartall).children_count()/child_pids()— global supervised children count and list.
Example:
rt.create_path_supervisor('/svc/test')
rt.path_supervisor_watch('/svc/test', pid)
print(rt.path_supervisor_children('/svc/test'))hot_swap(pid: int, new_handler)— atomically swap actor logic.behavior_version(pid: int) -> int— current behavior version.rollback_behavior(pid: int, steps: int = 1) -> int— roll back behavior bystepsversions; returns new version.get_messages(observer_pid)— (guest API) fetch observed messages (see language bindings).
Exit reasons and system messages are delivered as structured PySystemMessage objects with fields such as type_name, from_pid, target_pid, reason, and optional metadata.
Example:
def a(msg):
print('got', msg)
pid = rt.spawn(a)
rt.hot_swap(pid, lambda m: print('upgraded', m))listen(addr: str)— start TCP server to accept remote messages and resolves.resolve_remote(addr: str, name: str) -> Optional[int]— blocking remote resolve (returns local proxy PID).resolve_remote_py(addr: str, name: str) -> Awaitable[Optional[int]]— async remote resolve.is_node_up(addr: str) -> bool— quick probe for remote node reachability.send_remote(addr: str, pid: int, data: bytes-like)— send bytes-like payload to a remote PID (creates/reuses proxy internally).monitor_remote(addr: str, pid: int)— watch remote PID; triggers local supervisor on failure.
Example:
proxy = await rt.resolve_remote_py('192.168.1.5:9000', 'auth_worker')
if proxy:
rt.send(proxy, b'login')set_release_gil_limits(max_threads: int, pool_size: int)— configure dedicated GIL threads and shared pool.set_release_gil_strict(strict: bool)— whenTrue, spawning withrelease_gil=Truereturns an error if limits exceeded.
Example:
rt.set_release_gil_limits(8, 16)
rt.set_release_gil_strict(True)When Iris is built with the vortex feature, PyRuntime exposes automatic ghost-arbitration controls and telemetry.
vortex_set_auto_ghost_policy(policy: str) -> bool- Accepted values:
FirstSafePointWins,PreferPrimary.
- Accepted values:
vortex_get_auto_ghost_policy() -> Optional[str]vortex_get_auto_resolution_counts() -> Tuple[int, int]- Returns
(primary_wins, ghost_wins).
- Returns
vortex_get_auto_replay_count() -> intvortex_reset_auto_telemetry() -> None
Example:
rt = Runtime()
rt.vortex_reset_auto_telemetry()
rt.vortex_set_auto_ghost_policy("PreferPrimary")
primary_wins, ghost_wins = rt.vortex_get_auto_resolution_counts()
replayed = rt.vortex_get_auto_replay_count()
print(primary_wins, ghost_wins, replayed)stop(pid: int)— stop an actor and close mailbox.join(pid: int)— block until actor exits (useful in tests).is_alive(pid: int) -> bool— check actor liveness.
Example:
rt.stop(pid)
rt.join(pid)
assert not rt.is_alive(pid)Imported into package namespace:
offloaddecorator:@iris.offload(strategy='jit' | 'actor', return_type=...)— intercepts a Python function to either route to Rust actor pool or compile to native where supported.set_jit_logging,get_jit_logging,set_quantum_speculation,get_quantum_speculation— runtime controls for the JIT subsystem.
Example:
@iris.offload(strategy='jit', return_type='float')
def vec_mag(x: float, y: float, z: float) -> float:
return (x*x + y*y + z*z) ** 0.5
print(vec_mag(1.0, 2.0, 3.0))Use spawn_py_handler_bounded to set capacity, then configure overflow policy:
set_overflow_policy(pid, policy, target=None)—policyis one of:dropnew,dropold,redirect,spill,block.redirect/spillrequiretargetPID.
Example (Python):
primary = rt.spawn_py_handler_bounded(lambda m: print(m), 100, capacity=1)
fallback = rt.spawn(lambda m: print('fallback', m), 100)
rt.set_overflow_policy(primary, 'redirect', fallback)Python — spawn, register, send, timers:
rt = Runtime()
def hello(msg):
print('hello', msg)
pid = rt.spawn(hello)
rt.register('greeter', pid)
rt.send_named('greeter', b'world')
tid = rt.send_interval(pid, 1000, b'ping')
# later
rt.cancel_timer(tid)Node.js — conceptual:
const rt = new NodeRuntime();
const pid = rt.spawn((msg) => console.log('got', msg));
rt.send(pid, Buffer.from('hello'));- Architecture details: docs/architecture.md
- JIT internals and controls: docs/jit.md
- Distributed mesh & discovery: docs/distributed.md