Skip to main content

Crate byteflow

Crate byteflow 

Source
Expand description

Byteflow — embeddable flow runtime (package byteflow-actors).

Not a language, not Tokio, not a JVM. You assemble register bytecode in host Rust (Program / Fn), spawn many lightweight flows on an M:N scheduler, and they talk through mailboxes with a strict hop protocol.

Dependents write use byteflow::... (crate name) while crates.io lists the package as byteflow-actors.

§What you get

PieceRole
Program / Fn / OpcodeAssemble .bf programs in Rust (no source language)
verifyStatic gate for untrusted chunks — mandatory, not advisory
Vm / VmResultPer-flow register interpreter; effects hand off to the scheduler
RuntimeWorker pool + timer; spawn / join / host Runtime::send
FlowHandleCollect an outcome: blocking FlowHandle::join or a bounded form
Value::MessageAtomic Hop envelope — the only value allowed on Send / Ask
Value::CapFlowCap address for bytecode delivery (Send / Ask targets)
Cap / CapRights / NativeMaskHolder + rights; Cap::attenuate is the only grant path
QuotaConfig / FlowQuotaPer-flow CPU / heap / spawn-send budgets
SupervisorOTP strategies (OneForOne / OneForAll / RestForOne)
std_native_tableprint, now_ms, make_msg (3-arg), msg_*, msg_reply_cap

§Atomic Hop (messaging contract)

Every bytecode Send / Ask carries exactly one Message:

Message { sender, reply_cap, request_id, tag, payload }
  • Bare Int / Pid / Str on Send → trap / SendError::NotAHop
  • Scheduler stamps sender (authenticated origin) and mints reply_cap (SEND-only Cap back to the caller)
  • Reply with std_native_table’s msg_reply_capnot msg_sender (Pid is identity, not an address)

Also: selective receive (ReceiveMatch), Ask / AskTimeout for correlated RPC.

§FlowCap (addressing)

ValueUse
Value::CapTarget of Send / Ask; from SelfPid, Spawn, or reply_cap
Value::PidIdentity inside a delivered hop (msg_sender)

Host Runtime::send still takes FlowId (trusted embedder path).

Caps resolve only for the holder with sufficient rights. Derive a weaker grant with Fn::delegate / Cap::attenuate — never by copying a CapId. Host spawn is ROOT; bytecode Fn::spawn_confined starts at NONE.

§Values (ABI v5)

Unit | Bool | Int | Float | Pid | Message | Cap | Str | Bytes

CapId is a 128-bit CSPRNG token. Message.payload is a nested Value. Str / Bytes are Arc-backed for cheap register/mailbox clones. They are not Atomic Hops by themselves.

§Quick start — scalar

use byteflow::{Program, FlowOutcome, Runtime, Value};

let mut program = Program::new("demo");
program.function("main", 0, |f| {
    let a = f.load_i32(41);
    let b = f.load_i32(1);
    let sum = f.add(a, b);
    f.return_(sum);
});

let rt = Runtime::new(program.build())?;
let outcome = rt.spawn(0, &[])?.join();
rt.shutdown();
assert!(matches!(outcome, FlowOutcome::Completed(Value::Int(42))));

§Quick start — Atomic Hop (ping-pong)

Hop demos need the std native table (make_msg / msg_*):

use byteflow::{samples, std_native_table, FlowOutcome, Runtime, Value};

let rt = Runtime::with_natives(samples::ping_pong(), std_native_table())?;
let Some(main) = rt.function_index("main") else { return Ok(()); };
let handle = rt.spawn(main, &[])?;
let outcome = handle.join();
rt.shutdown();
assert!(matches!(outcome, FlowOutcome::Completed(Value::Int(2))));

More samples: samples::atomic_request_reply, samples::ask_reply, samples::selective_receive, forged-sender security regressions.

§Collecting a result

FlowHandle::join blocks, which suits a main with nothing else to do. Anything holding a deadline picks its own bound instead:

CallWaitsWhile the flow is still running
FlowHandle::try_joinneverNone
FlowHandle::join_timeout / FlowHandle::join_deadlineup to the boundNone
FlowHandle::joinunbounded(blocks)
use byteflow::{Program, FlowOutcome, Runtime, Value};
use std::time::Duration;

let mut program = Program::new("slow");
program.function("main", 0, |f| {
    let ms = f.load_i32(300);
    f.sleep(ms);
    let out = f.load_i32(7);
    f.return_(out);
});

let rt = Runtime::new(program.build())?;
let handle = rt.spawn(0, &[])?;

// Neither of these consumes the handle or the outcome.
assert!(handle.try_join().is_none());
assert!(handle.join_timeout(Duration::from_millis(10)).is_none());

let outcome = handle.join_timeout(Duration::from_secs(10));
rt.shutdown();
assert!(matches!(outcome, Some(FlowOutcome::Completed(Value::Int(7)))));

A flow destroyed before it produced an outcome — Runtime::shutdown does not drain suspended flows — wakes its joiner with a failure instead of leaving it parked forever. See docs::error_model.

§Design guides (rendered on docs.rs)

§What this is not

  • Not a replacement for Tokio / async Rust (no .await IO loop)
  • Not a distributed cluster runtime (single process, in-memory mailboxes)
  • Not a full object-capability OS (no distributed revocation / Cap persistence)

Host owns I/O and policy. Byteflow owns cheap concurrency and hop delivery.

Re-exports§

pub use bytecode::asm_macros;
pub use bytecode::decode;
pub use bytecode::decode_with;
pub use bytecode::disassemble;
pub use bytecode::encode;
pub use bytecode::verify;
pub use bytecode::verify_with;
pub use bytecode::Cap;
pub use bytecode::CapId;
pub use bytecode::CapIdError;
pub use bytecode::CapRights;
pub use bytecode::CapTarget;
pub use bytecode::Chunk;
pub use bytecode::Fn;
pub use bytecode::ConstantKind;
pub use bytecode::FormatError;
pub use bytecode::FuncId;
pub use bytecode::FunctionDef;
pub use bytecode::Instruction;
pub use bytecode::Label;
pub use bytecode::Message;
pub use bytecode::NativeIdx;
pub use bytecode::NativeMask;
pub use bytecode::Opcode;
pub use bytecode::Program;
pub use bytecode::Reg;
pub use bytecode::RegWindow;
pub use bytecode::RevocationCell;
pub use bytecode::TrustLevel;
pub use bytecode::Value;
pub use bytecode::VerifyConfig;
pub use bytecode::VerifyError;
pub use bytecode::ABI_VERSION;
pub use bytecode::MAGIC;
pub use bytecode::TAG_SYS_DOWN;
pub use bytecode::TAG_SYS_EXIT;
pub use output::NullSink;
pub use output::OutputSink;
pub use output::StdoutSink;
pub use natives::std_native;
pub use natives::std_native_map;
pub use natives::std_native_table;
pub use natives::std_native_table_with;
pub use natives::std_natives;
pub use scheduler::fault_count;
pub use scheduler::next_flow_id;
pub use scheduler::flow_id_from_u64;
pub use scheduler::report_fault;
pub use scheduler::CapError;
pub use scheduler::Capability;
pub use scheduler::ChildSpec;
pub use scheduler::Delivery;
pub use scheduler::DownEvent;
pub use scheduler::FlowExitReason;
pub use scheduler::FlowQuota;
pub use scheduler::LifecycleError;
pub use scheduler::LinkId;
pub use scheduler::Mailbox;
pub use scheduler::MailboxBytes;
pub use scheduler::MailboxCapacity;
pub use scheduler::MailboxConfig;
pub use scheduler::MailboxFull;
pub use scheduler::MailboxFullReason;
pub use scheduler::MailboxStats;
pub use scheduler::MonitorRef;
pub use scheduler::OverflowPolicy;
pub use scheduler::QuotaConfig;
pub use scheduler::QuotaError;
pub use scheduler::RegistryName;
pub use scheduler::WaitEpoch;
pub use scheduler::Flow;
pub use scheduler::FlowHandle;
pub use scheduler::FlowId;
pub use scheduler::FlowMetrics;
pub use scheduler::FlowOutcome;
pub use scheduler::RestartPolicy;
pub use scheduler::RestartStrategy;
pub use scheduler::Runtime;
pub use scheduler::RuntimeConfig;
pub use scheduler::RuntimeError;
pub use scheduler::RuntimeMetrics;
pub use scheduler::RuntimeMetricsSnapshot;
pub use scheduler::RuntimeSpawner;
pub use scheduler::SendError;
pub use scheduler::SpawnError;
pub use scheduler::Supervisor;
pub use scheduler::SupervisorConfig;
pub use scheduler::DEFAULT_QUANTUM;
pub use scheduler::check_admin;
pub use scheduler::check_monitor;
pub use scheduler::exec_delegate;
pub use scheduler::AdminError;
pub use scheduler::DelegateError;
pub use scheduler::LinkError;
pub use scheduler::JitConfig;jit
pub use vm::expect_arg;
pub use vm::expect_bool;
pub use vm::expect_int;
pub use vm::expect_message;
pub use vm::expect_u64;
pub use vm::check_native_call;
pub use vm::Fault;
pub use vm::NativeCallError;
pub use vm::NativeFn;
pub use vm::NativeGate;
pub use vm::NativeResult;
pub use vm::NativeTable;
pub use vm::NativeTableBuilder;
pub use vm::NativeTableError;
pub use vm::Vm;
pub use vm::VmResult;
pub use vm::MAX_CALL_DEPTH;
pub use jit::apply_exit_to_vm;jit
pub use jit::force_compile;jit
pub use jit::hot_threshold;jit
pub use jit::run_compiled_trace;jit
pub use jit::run_compiled_trace_ref;jit
pub use jit::run_vm_with_jit;jit
pub use jit::run_vm_with_jit_runtime;jit
pub use jit::sync_slots_from_vm;jit
pub use jit::try_run_hot;jit
pub use jit::try_run_hot_runtime;jit
pub use jit::CompileError;jit
pub use jit::CompiledTrace;jit
pub use jit::ExitReason;jit
pub use jit::HotCounter;jit
pub use jit::JitContext;jit
pub use jit::JitEntry;jit
pub use jit::JitFrame;jit
pub use jit::JitReturn;jit
pub use jit::JitRuntime;jit
pub use jit::SyncSlotsResult;jit
pub use jit::TraceCache;jit
pub use jit::TraceCompiler;jit
pub use jit::TraceKey;jit
pub use jit::TraceSpan;jit
pub use jit::HOT_THRESHOLD;jit
pub use jit::MAX_TRACE_LENGTH;jit
pub use jit::JIT_BUDGET;jit
pub use jit::JIT_CONTINUE;jit
pub use jit::JIT_DEOPT;jit
pub use jit::JIT_EFFECT;jit
pub use jit::JIT_RETURN;jit
pub use jit::JIT_TRAP;jit

Modules§

bytecode
Instruction set, .bf (BFV0) wire format, assembler and verifier.
docs
Long-form design notes shipped inside the crate (also under docs/ on GitHub).
jitjit
Trace JIT for Byteflow — Cranelift backend, isolated unsafe.
log
Host-side scheduler diagnostics on stderr, gated by BYTEFLOW_LOG.
natives
Standard native (FFI) table shipped with the facade.
output
Host-side output for the print native.
samples
Built-in demo chunks assembled with crate::Program.
scheduler
M:N flows, mailboxes, timer, supervisor and Runtime.
vm
Register-based interpreter for one virtual flow.

Macros§

emit_native1_from
emit_native1_from!(builder, dest, src, native) — see [Fn::native1_from].
emit_native_n
emit_native_n!(builder, base, native, argc) — see [Fn::native_n].