# BEAM → Byteflow (mental model)
Guide for Erlang/Elixir developers reading this codebase. Byteflow is **not**
BEAM — but many ideas rhyme once you map terminology.
## Units
| process | **flow** | `FlowId`, `FlowHandle`, `FlowOutcome` |
| `pid()` | **`hop_sender(msg)`** | Identity inside a *delivered* hop |
| `pid()` as address | **`Value::Cap`** | `SelfPid` / `Spawn` return Caps, not Pids |
| registered name | **`Runtime::register_name` / `whereis`** | Stores a **Cap**, not a FlowId. Swept on exit. |
**Key difference:** on BEAM, a Pid is both identity and delivery address. In
Byteflow, **Cap = address**, **Pid = identity** inside `Message.sender`.
```text
BEAM: send(Pid, Term)
Byteflow: send(Cap, Message) // Message envelope required
```
See [`atomic-hop.md`](atomic-hop.md) and [`security.md`](security.md) (S1, S6).
## Messaging
| `spawn(fun)` | `Fn::spawn(fn, argc)` | `Spawn` → Cap |
| `Pid ! Msg` | `Fn::send(cap, hop)` | `Send` |
| `receive` | `Fn::receive()` | `Receive` |
| selective receive (pattern) | `Fn::receive_match_imm(tag)` | `ReceiveMatchImm` — **tag u16 only** |
| `gen_server:call` | `Fn::ask(cap, hop)` / `Fn::ask_timeout` | `Ask` / `AskTimeout` |
| `gen_server:cast` | `Fn::send(cap, hop)` | fire-and-forget |
| reply to caller | `Fn::send_reply(req, tag, payload)` | uses `msg_reply_cap` |
| build message | `Fn::hop(req_id, tag, payload)` | scheduler stamps sender |
| unpack message | `Fn::hop_payload(msg)` etc. | std natives |
### Message shape
BEAM messages are arbitrary terms. Byteflow **Atomic Hop** is a fixed envelope:
```text
Message { sender, reply_cap, request_id, tag, payload }
```
- `payload` is a nested [`Value`](../src/bytecode/value.rs) (ABI v5) — scalars,
`Str` / `Bytes`, or another hop.
- `sender` is **authenticated by the runtime** on bytecode `Send` / `Ask`.
`make_msg` has no sender operand (3-arg: request_id, tag, payload). A
leftover 4-arg encoding is ignored — see
[`samples::forged_sender_send`](../src/samples.rs).
### Typical server loop (BEAM-style)
```rust
use byteflow::{Program, samples::TAG_REQ, samples::TAG_REP};
let mut p = Program::new("server");
f.bind(loop_lbl);
let req = f.receive_match_imm(TAG_REQ as u16);
let payload = f.hop_payload(req);
f.add_imm(payload, 1);
f.send_reply(req, TAG_REP, payload);
f.jump(loop_lbl);
});
```
Sample: [`samples::server_loop`](../src/samples.rs).
## Lifecycle (mapped)
| **links** | `Runtime::link` / `Fn::link` — abnormal exit kills the peer |
| **monitors** `{'DOWN', ...}` | `Runtime::monitor` / `Fn::monitor` → `TAG_SYS_DOWN` hop |
| **OTP supervisor strategies** | Host `Supervisor` + `RestartStrategy` (`OneForOne` / `OneForAll` / `RestForOne`) |
| **`register` / `whereis`** | `Runtime::register_name` / `whereis` (Cap, not FlowId) |
| **`exit(Pid, kill)`** | `Runtime::kill` (cooperative) or `Runtime::admin_kill` (ADMIN Cap) |
| **capability pass** | `Fn::delegate` / `Cap::attenuate` (AND of rights + native mask) |
| **confined spawn** | `Fn::spawn_confined` (child rights `NONE`) |
## What BEAM has that Byteflow does not (yet)
| **distribution** | Single process, in-memory |
| **pattern matching receive** | Tag-based selective receive only |
| **process dictionary** | No |
| **ETS** | No |
| **`trap_exit`** | No — links always kill on abnormal exit |
Failures surface as `FlowOutcome::Failed` on `join`, not as mailbox messages.
## Backpressure
| `Runtime::send(FlowId, …)` | `Err(SendError::MailboxFull)` |
| bytecode `Send` / `Ask` | sender parks (`WAITING_SEND`); one waiter per freed slot |
## Host Rust vs bytecode
| spawn top-level flows | `Runtime::spawn` |
| trusted host send | `Runtime::send(FlowId, Value::Message)` |
| restart policy | `Supervisor` (Rust) |
| protocol in flows | `Program` / `Fn` bytecode |
Supervisor trees are **not** expressed as bytecode flows today — plan host
Rust for OTP-style supervision.
## Quick equivalence cheat sheet
```text
self() → hop_sender on received msg; self_address() for Cap
! → send(cap, hop(...))
receive → receive() / receive_match_imm(TAG)
call → ask(cap, hop(...)) / ask_timeout(cap, hop, ms)
reply → send_reply(req, TAG_REP, payload)
spawn → spawn(fn) → Cap; spawn_confined(fn) → Cap with rights NONE
register/whereis → Runtime::register_name / whereis; ChildSpec.name also registers
link/monitor → Fn::link / Fn::monitor (need LINK / MONITOR on the addressing Cap)
delegate → Fn::delegate(cap, rights) → weaker Cap
```
## Further reading
- [`atomic-hop.md`](atomic-hop.md) — protocol details
- [`mailbox.md`](mailbox.md) — bounded queues, overflow
- [`security.md`](security.md) — S1 authenticated sender, S6 FlowCap, S7 native mask
- [`samples.rs`](../src/samples.rs) — runnable specs