The pure engine
yggr-core is the Raft protocol. One type, one method:
#![allow(unused)]
fn main() {
pub struct Engine<C> { /* ... */ }
impl<C: Clone> Engine<C> {
pub fn step(&mut self, event: Event<C>) -> Vec<Action<C>>;
}
}
Every forward motion is an Event:
Tick— abstract time advanced one stepIncoming(msg)— a peer sent us an RPCClientProposal(cmd)— application wants to replicate somethingClientProposalBatch(cmds)— same, many at onceProposeConfigChange(change)— §4.3 membership changeTransferLeadership { target }— initiate leadership transferProposeRead { id }— §8 linearizable readSnapshotTaken { last_included_index, bytes }— host cut a snapshot
Every effect is an Action:
PersistHardState,PersistLogEntries,PersistSnapshotSend { to, message }Apply(entries)— commit to the state machineApplySnapshot { bytes }— restore the state machineRedirect { leader_hint }— we are not the leaderReadReady { id }/ReadFailed { id, reason }SnapshotHint { last_included_index }— advisory compaction
No I/O
The engine does not touch a socket, a file, or the clock. The host (either yggr or your own code) translates Actions into I/O.
No async
step is synchronous. It returns Vec<Action<C>>. The host decides how to dispatch.
Testable
Because the engine has no I/O, it runs at memory speed under the deterministic chaos harness in yggr-sim. Many seeds, thousands of steps each, in a second.