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
| Piece | Role |
|---|---|
Program / Fn / Opcode | Assemble .bf programs in Rust (no source language) |
verify | Static gate for untrusted chunks — mandatory, not advisory |
Vm / VmResult | Per-flow register interpreter; effects hand off to the scheduler |
Runtime | Worker pool + timer; spawn / join / host Runtime::send |
FlowHandle | Collect an outcome: blocking FlowHandle::join or a bounded form |
Value::Message | Atomic Hop envelope — the only value allowed on Send / Ask |
Value::Cap | FlowCap address for bytecode delivery (Send / Ask targets) |
Supervisor | OTP strategies (OneForOne / OneForAll / RestForOne) |
std_native_table | print, now_ms, make_msg, 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/StronSend→ trap /SendError::NotAHop - Scheduler stamps
sender(authenticated origin) and mintsreply_cap(SEND-only Cap back to the caller) - Reply with
std_native_table’smsg_reply_cap— notmsg_sender(Pidis identity, not an address)
Also: selective receive (ReceiveMatch), Ask / AskTimeout for correlated RPC.
§FlowCap (addressing)
| Value | Use |
|---|---|
Value::Cap | Target of Send / Ask; from SelfPid, Spawn, or reply_cap |
Value::Pid | Identity inside a delivered hop (msg_sender) |
Host Runtime::send still takes FlowId (trusted embedder path).
§Values (ABI v4)
Unit | Bool | Int | Float | Pid | Message | Cap | Str | Bytes
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:
| Call | Waits | While the flow is still running |
|---|---|---|
FlowHandle::try_join | never | None |
FlowHandle::join_timeout / FlowHandle::join_deadline | up to the bound | None |
FlowHandle::join | unbounded | (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)
docs::atomic_hop— hop protocol, Cap addressing, natives tabledocs::beam_mapping— BEAM / OTP mental model → Byteflow equivalentsdocs::lifecycle— monitors, links, registry,WAITING_SENDdocs::mailbox— bounded inbox, overflow, lost-wakeupdocs::security— threat model, invariants S1–S7, roadmapdocs::error_model— fail-closed errors (nounwrap), bounded joinsdocs::vm_safety— trust boundary:verifyvs per-stepFault
§What this is not
- Not a replacement for Tokio / async Rust (no
.awaitIO loop) - Not a distributed cluster runtime (single process, in-memory mailboxes)
- Not a full object-capability OS (native quotas / Cap attenuation come later)
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::disassemble;pub use bytecode::encode;pub use bytecode::verify;pub use bytecode::Chunk;pub use bytecode::Fn;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::Opcode;pub use bytecode::Program;pub use bytecode::Reg;pub use bytecode::RegWindow;pub use bytecode::Value;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 natives::std_native;pub use natives::std_native_map;pub use natives::std_native_table;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::CapId;pub use scheduler::CapRights;pub use scheduler::ChildSpec;pub use scheduler::Delivery;pub use scheduler::DownEvent;pub use scheduler::FlowExitReason;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::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::JitConfig;jitpub 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::Fault;pub use vm::NativeFn;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;jitpub use jit::force_compile;jitpub use jit::hot_threshold;jitpub use jit::run_compiled_trace;jitpub use jit::run_compiled_trace_ref;jitpub use jit::run_vm_with_jit;jitpub use jit::run_vm_with_jit_runtime;jitpub use jit::sync_slots_from_vm;jitpub use jit::try_run_hot;jitpub use jit::try_run_hot_runtime;jitpub use jit::CompileError;jitpub use jit::CompiledTrace;jitpub use jit::ExitReason;jitpub use jit::HotCounter;jitpub use jit::JitContext;jitpub use jit::JitEntry;jitpub use jit::JitFrame;jitpub use jit::JitReturn;jitpub use jit::JitRuntime;jitpub use jit::SyncSlotsResult;jitpub use jit::TraceCache;jitpub use jit::TraceCompiler;jitpub use jit::TraceKey;jitpub use jit::TraceSpan;jitpub use jit::HOT_THRESHOLD;jitpub use jit::MAX_TRACE_LENGTH;jitpub use jit::JIT_BUDGET;jitpub use jit::JIT_CONTINUE;jitpub use jit::JIT_DEOPT;jitpub use jit::JIT_EFFECT;jitpub use jit::JIT_RETURN;jitpub 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). - jit
jit - 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.
- 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].