byteflow/lib.rs
1//! Byteflow — embeddable register-based **flow** runtime.
2//!
3//! # Overview
4//!
5//! Assemble programs with [`ChunkBuilder`] in host Rust (no separate source
6//! language). Spawn lightweight **flows** on an M:N scheduler; they communicate
7//! through FIFO mailboxes via **Atomic Hops** ([`Value::Message`] only on
8//! `Send` / `Ask`) and can be supervised on failure.
9//!
10//! The crates.io package is **`byteflow-actors`**; this library crate is named
11//! `byteflow`, so dependents write `use byteflow::...`.
12//!
13//! # Security (authenticated hops + FlowCap)
14//!
15//! Structural typing (`Send` requires [`Value::Message`]) is **not**
16//! authentication or authorization. Bytecode may forge `Message.sender` via
17//! `make_msg`; the scheduler **overwrites** that field and mints a
18//! **SEND**-only `reply_cap` before delivery. `Send` / `Ask` targets must be
19//! [`Value::Cap`] — raw [`Value::Pid`] is identity only.
20//!
21//! Full threat model, invariants **S1–S7**, and the capability roadmap:
22//! `docs/security.md` in the crate sources (also shipped on docs.rs when
23//! `docs/` is included in the package).
24//!
25//! # Quick example
26//!
27//! ```
28//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
29//! use byteflow::{ChunkBuilder, Opcode, FlowOutcome, Runtime, Value};
30//!
31//! let mut b = ChunkBuilder::new("demo");
32//! b.begin_function("main", 0, 2);
33//! b.emit_load_imm(0, 41);
34//! b.emit_load_imm(1, 1);
35//! b.emit_binop(Opcode::Add, 0, 0, 1);
36//! b.emit_return(0);
37//!
38//! let rt = Runtime::new(b.finish())?;
39//! let outcome = rt.spawn(0, &[])?.join();
40//! rt.shutdown();
41//! assert!(matches!(outcome, FlowOutcome::Completed(Value::Int(42))));
42//! # Ok(())
43//! # }
44//! ```
45//!
46//! Host owns I/O. Byteflow owns cheap concurrency.
47#![forbid(unsafe_code)]
48// Tests may use unwrap/expect for brevity; production paths must not.
49#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
50
51pub mod bytecode;
52pub mod log;
53pub mod natives;
54pub mod samples;
55pub mod scheduler;
56pub mod vm;
57
58pub use bytecode::{
59 asm_macros, decode, disassemble, encode, verify, Chunk, ChunkBuilder, FormatError,
60 FunctionDef, Instruction, Label, Message, Opcode, Value, VerifyError, ABI_VERSION, MAGIC,
61};
62pub use natives::{std_native_map, std_native_table, std_natives};
63pub use scheduler::{
64 fault_count, next_flow_id, flow_id_from_u64, report_fault, CapId, CapRights, ChildSpec,
65 Delivery, Mailbox, Flow, FlowHandle, FlowId, FlowMetrics, FlowOutcome, FlowState,
66 RestartPolicy, Runtime, RuntimeConfig, RuntimeError, RuntimeMetrics,
67 RuntimeMetricsSnapshot, RuntimeSpawner, SendError, SpawnError, Supervisor,
68 SupervisorConfig, DEFAULT_QUANTUM,
69};
70pub use vm::{
71 expect_arg, expect_bool, expect_int, expect_message, expect_u64, Fault, NativeFn,
72 NativeResult, NativeTable, NativeTableBuilder, Vm, VmResult, MAX_CALL_DEPTH,
73};
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn std_native_map_has_stable_indices() {
81 let map = std_native_map();
82 assert_eq!(map["print"], 0);
83 assert_eq!(map["now_ms"], 1);
84 assert_eq!(map["make_msg"], 2);
85 assert_eq!(map["msg_payload"], 6);
86 assert_eq!(map["msg_reply_cap"], 7);
87 }
88
89 #[test]
90 fn chunk_builder_with_std_natives() {
91 let mut b = ChunkBuilder::new("std-natives-demo");
92 b.begin_function("main", 0, 2);
93 b.emit_load_imm(0, 42);
94 b.emit_call_native(0, 0, 1);
95 b.emit_call_native(1, 1, 0);
96 b.emit_return(1);
97
98 let chunk = b.finish();
99 verify(&chunk).expect("verify");
100
101 let rt = Runtime::with_natives(chunk, std_native_table()).expect("runtime");
102 let outcome = rt.spawn(0, &[]).expect("spawn").join();
103 rt.shutdown();
104
105 match outcome {
106 FlowOutcome::Completed(Value::Int(ms)) => assert!(ms >= 0),
107 other => panic!("expected Completed(Value::Int(_)), got {other:?}"),
108 }
109 }
110}