bankai/rules/registry
Pillar 2 — mobile rules: content-addressed, executable, sync-able.
A “rule” is a gleamunison S-expression source string. Its identity is the SHA-256 of its source (gleamunison/identity.hash_bytes). Rules are stored in a registry, gated by an allow-list, executed via gleamunison/repl, and synced across registries by hash (identical sources dedupe cleanly).
SECURITY (per ADR-0003, layered defense in depth):
- Trust: a rule runs only if its hash is explicitly approved; register != approve (registration is arrival, approval is trust).
- Capability: rules are pure — repl’s eval surface exposes no file/net/proc builtins. Effectful rules require capability tokens (future).
- Resource: every eval is bounded by a wall-clock timeout (default 1s).
- Isolation: eval runs in an UNLINKED spawned process (a crash/loop can’t
reach the daemon handler); monitor-based instant crash
detection + timeout-bounded, runaway killed. See
run_isolated.
Types
pub type Rule {
Rule(name: String, source: String, hash: identity.Hash)
}
Constructors
-
Rule(name: String, source: String, hash: identity.Hash)
Values
pub fn approve(reg: Registry, hash: identity.Hash) -> Registry
pub const default_eval_max_heap_words: Int
Hard worker heap cap in BEAM words (~2 MiB on a 64-bit VM).
pub const default_eval_reduction_limit: Int
Maximum reductions consumed by one rule evaluation.
pub const default_eval_timeout_ms: Int
Default wall-clock budget for a single rule eval (ms).
pub fn eval(
reg: Registry,
hash: identity.Hash,
) -> Result(String, String)
Execute an approved rule, isolated + timeout-bounded at the default budget.
pub fn eval_source_with_input(
source: String,
input_json: String,
timeout_ms: Int,
) -> Result(String, String)
Evaluate a source artifact as a pure unary lambda over an immutable JSON text view. The quoted argument prevents task-view data escaping into syntax; this worker has no task, file, network, or process authority.
pub fn eval_with_timeout(
reg: Registry,
hash: identity.Hash,
timeout_ms: Int,
) -> Result(String, String)
Execute an approved rule with an explicit wall-clock budget (ms).
pub fn is_approved(reg: Registry, hash: identity.Hash) -> Bool
pub fn lookup(
reg: Registry,
hash: identity.Hash,
) -> Result(Rule, Nil)
pub fn merge(a: Registry, b: Registry) -> Registry
Content-addressed sync: union two registries by rule hash (identical sources
dedupe — same hash). BUT approvals stay LOCAL — BUG-06 fix: the old code
set.union-ed approvals, letting a rule approved on ANY rig become
executable everywhere after merge, bypassing ADR-0003’s trust layer
(“registration is arrival, approval is trust”). Sync propagates RULES (data),
never TRUST — each rig approves locally.
pub fn register(
reg: Registry,
name: String,
source: String,
) -> #(Registry, identity.Hash)
Register a rule. NOT auto-approved — call approve to make it executable.
(ADR-0003 trust layer: registration is arrival, approval is trust.)
pub fn revoke(reg: Registry, hash: identity.Hash) -> Registry
pub fn run_bounded(
work: fn() -> Result(String, String),
timeout_ms: Int,
max_heap_words: Int,
reduction_limit: Int,
) -> Result(String, String)
pub fn run_isolated(
work: fn() -> Result(String, String),
timeout_ms: Int,
) -> Result(String, String)
Execute pure work in an unlinked worker bounded independently by wall-clock, heap words, and BEAM reductions. The Erlang boundary owns process monitoring so a heap kill cannot be mistaken for a normal evaluator result.
pub fn source_hash(source: String) -> identity.Hash
Content address of a rule source = SHA-256 of the source bytes.
pub fn source_hash_text(source: String) -> String