{"id":49767725,"url":"https://github.com/umbgtt10/faction","last_synced_at":"2026-05-11T11:08:20.551Z","repository":{"id":356732524,"uuid":"1219062527","full_name":"umbgtt10/faction","owner":"umbgtt10","description":"A no_std, 0-unsafe Mealy machine for two-phase cluster readiness coordination.","archived":false,"fork":false,"pushed_at":"2026-05-09T12:57:57.000Z","size":955,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-09T14:38:38.679Z","etag":null,"topics":["bootstrapping","discovery","distributed-systems","no-std","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/umbgtt10.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"docs/ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-23T13:45:02.000Z","updated_at":"2026-05-09T12:58:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/umbgtt10/faction","commit_stats":null,"previous_names":["umbgtt10/faction"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/umbgtt10/faction","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/umbgtt10%2Ffaction","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/umbgtt10%2Ffaction/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/umbgtt10%2Ffaction/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/umbgtt10%2Ffaction/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/umbgtt10","download_url":"https://codeload.github.com/umbgtt10/faction/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/umbgtt10%2Ffaction/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32892015,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-10T13:40:02.631Z","status":"online","status_checked_at":"2026-05-11T02:00:05.975Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bootstrapping","discovery","distributed-systems","no-std","rust"],"created_at":"2026-05-11T11:08:19.572Z","updated_at":"2026-05-11T11:08:20.538Z","avatar_url":"https://github.com/umbgtt10.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# faction\n\n**A `no_std`, 0-unsafe Mealy machine for distributed cluster bootstrapping.**\n\nEvery distributed system needs to answer one question before it can do anything else:\n*\"Is the cluster ready to proceed?\"*\n\nMost systems answer it with ad-hoc coordination, custom timeouts, heuristics,\nmagic numbers tuned by intuition, and startup sequences that were never tested in\nisolation. When they break, they break silently, under load, in production.\n\nConsensus protocols don't help. IBFT, PBFT, and Tendermint assume the cluster\nalready exists — they consume a static validator set and answer \"what's the next\nblock?\" without ever asking \"are we a cluster yet?\" Raft tries harder but fuses\nbootstrapping directly into leader election and log replication. When a Raft\ncluster won't form, you face a single opaque failure mode indistinguishable from\na network partition, a slow peer, or a config mismatch. Both families of protocol\nskip the same gate: is the set of peers actually alive and in agreement before\nconsensus begins?\n\n`faction` replaces that with a **formally specified, fully tested Mealy state machine**\nthat answers the question deterministically, observably, and provably.\n\n---\n\n## What faction does\n\n`faction` tracks participation and readiness signals across a known set of peers and\nemits a deterministic exit decision — either **Bootstrapped** or **TimedOut**.\n\nIt does exactly this. Nothing more.\n\nNo network I/O. No consensus algorithm. No opinion on what transport you use, what\n\"ready\" means, or how your protocol works. The caller owns all of that. `faction` owns\nthe state transitions.\n\n---\n\n## Why faction?\n\n**The problem with ad-hoc bootstrapping:**\n\nBootstrapping logic is typically written once, tested never, and debugged in production.\nIt interacts with failure detection, membership management, and consensus in ways that\nare hard to reason about in isolation. The bugs it hides surface under Byzantine\nconditions — partial startup, network partitions, delayed nodes — that are difficult to\nreproduce and expensive to diagnose.\n\n**What faction provides instead:**\n\n- **Deterministic** — `output = F(state, input)`. Same inputs always produce the same\n  outputs. Any execution is replayable from its input log.\n- **Verifiable** — every `(state, command)` pair is explicitly tested. No untested\n  paths exist. The test suite is a proof, not a sample.\n- **Embeddable** — `no_std + alloc`, zero `unsafe`. Runs on bare metal, WASM, embedded\n  RTOS, and cloud without modification.\n- **Observable** — every transition reaches a trait-based `Observer`. Wire it to\n  telemetry, audit logs, or test assertions. No instrumentation surprises.\n- **Queryable** — probe the machine at any time for the current cluster view and the\n  set of admissible commands. Zero side effects.\n- **Slim by construction** — each state carries only its active data. Terminal states\n  carry no heap allocation beyond what they received.\n\n---\n\n## How it works\n\n### The state machine\n\nThe machine progresses through five states:\n\n```\nInitial → Pinging → Collecting → Bootstrapped\n                         ↓\n                      TimedOut\n```\n\n| State | Meaning | Carries |\n|---|---|---|\n| `Initial` | Freshly created, no action taken | Nothing — unit struct |\n| `Pinging` | Collecting participation signals from peers | In-flight participation and readiness sets |\n| `Collecting` | Local participation complete, collecting readiness | In-flight readiness and completed participation sets |\n| `Bootstrapped` | Quorum reached — cluster is ready (terminal) | Final peer sets at time of exit |\n| `TimedOut` | Deadline expired before quorum (terminal) | Peer sets at time of expiry |\n\n### Commands and outcomes\n\nFive commands drive the machine:\n\n| Command | Meaning |\n|---|---|\n| `ParticipationObserved { peer_id }` | A peer signalled participation |\n| `ReadyObserved { peer_id }` | A peer signalled readiness |\n| `LocalParticipationCompleted` | The local node finished its own participation |\n| `DeadlineExpired` | External deadline timer fired |\n| `Probe` | Query current state without mutation |\n\nEvery command produces a structured result — `Accepted`, `Rejected`, or `Probed` —\nwith full outcome detail and an updated cluster view. The machine never panics, never\nreturns an opaque error, and never silently ignores input.\n\n### Two-phase design\n\nThe machine enforces a deliberate two-phase protocol:\n\n**Phase 1 — Pinging:** nodes observe each other's participation signals. A node stays\nin this phase until it has signalled its own participation locally.\n\n**Phase 2 — Collecting:** the node collects readiness signals from peers. Quorum is\nonly checked in this phase — preventing premature exit before local participation\nis complete.\n\nThis separation eliminates an entire class of race conditions where a node could\ndeclare quorum before confirming its own participation.\n\n### Validation harness\n\n`faction` ships with a multi-layered validation harness:\n\n| Harness | What it tests |\n|---|---|\n| `core/` unit tests | Every `(state, command)` pair — 145 tests |\n| `core-validation/` | Multi-node deterministic scenarios — 23 tests |\n| `protocol/` | Message translation and protocol runtime — 33 tests |\n| `protocol-validation/` | In-process protocol cluster — 9 tests |\n| `system-tests/` convergence | (10 spawn/transport × 2 quorum scenarios) — 20 tests |\n| `system-tests/` infrastructure | TCP, gRPC, channels, in-memory plumbing — 44 tests |\n\nThe system test matrix covers every valid combination of spawn model and transport:\n\n| Spawn | Transport | Timer |\n|---|---|---|\n| Task | In-memory, Channels, TCP, gRPC | Real, In-memory |\n| Thread | In-memory, Channels, TCP, gRPC | Real, In-memory |\n| Process | TCP, gRPC | Real |\n\nThese are not simulated tests. Process-based cases spawn real OS processes communicating\nover real TCP and gRPC connections. The machine's correctness is verified end-to-end\nacross all combinations.\n\n---\n\n## Quick start\n\n```toml\n[dependencies]\nfaction = \"0.3\"\n```\n\n```rust\nuse faction::command::Command;\nuse faction::config::Config;\nuse faction::faction::Faction;\nuse faction::no_op_observer::NoOpObserver;\nuse faction::process_result::ProcessResult;\nuse faction::quorum_policy::QuorumPolicy;\n\nextern crate alloc;\n\nlet config = Config::new(\n    0,\n    alloc::vec![0, 1, 2, 3, 4],\n    QuorumPolicy::new(4),\n);\n\nlet mut machine = Faction::new(config, Box::new(NoOpObserver));\n\nmachine.process(Command::ParticipationObserved { peer_id: 1 });\nmachine.process(Command::ParticipationObserved { peer_id: 2 });\nmachine.process(Command::LocalParticipationCompleted);\nmachine.process(Command::ReadyObserved { peer_id: 1 });\nmachine.process(Command::ReadyObserved { peer_id: 2 });\nmachine.process(Command::ReadyObserved { peer_id: 3 });\n\n// Quorum of 4 reached → Bootstrapped.\nlet result = machine.process(Command::ReadyObserved { peer_id: 4 });\nif let ProcessResult::Accepted { cluster_view, .. } = result {\n    assert!(cluster_view.is_concluded());\n}\n```\n\nThe caller owns the network. `faction` owns the state.\n\n---\n\n## Project status\n\n| Metric | Value |\n|---|---|\n| Productive LOC | 1,165 |\n| Total tests | 275 |\n| Code coverage (productive) | 100% |\n| `(state, command)` matrix | [transition_matrix_tests.rs](./core/tests/transition_matrix/state_transition_matrix_tests.rs) |\n| Crappy functions (CRAP score) | 0 |\n| Unsafe code | 0 — enforced by `#![deny(unsafe_code)]` |\n| `no_std` | Verified |\n| System test combinations | 10 (Task/Thread/Process × Memory/Channels/TCP/gRPC) |\n| Published | [crates.io](https://crates.io/crates/faction) |\n\n---\n\n## Design principles\n\n- **Pure Mealy** — `output = F(state, input)`. No side effects inside the machine.\n  The machine computes. The caller acts.\n- **Explicit state ownership** — states carry only what they mutate. Nothing is\n  inherited silently.\n- **No dead code** — terminal states return `false` from `accept()`, making `step()`\n  structurally unreachable. The compiler enforces this.\n- **Observer, not logger** — the `Observer` trait receives every transition, query,\n  and rejection. Wire it to anything. The machine does not care.\n- **Protocol-agnostic** — `faction` does not know what a peer is, what the network\n  looks like, or what your protocol does. It knows state transitions.\n- **One struct per file** — each step, state, and policy is its own file. Navigation\n  is O(1).\n- **No `\u0026mut` parameters** — prefer return values over in-place mutation.\n\n---\n\n## Non-goals\n\n`faction` deliberately does not:\n\n- Perform network I/O — the caller sends and receives messages\n- Implement failure detection — that is Phase 2\n- Manage dynamic membership — that is Phases 1–6\n- Know about consensus — protocols build on top of `faction`, not inside it\n- Provide a runtime — async, threading, and process management are the caller's concern\n\n---\n\n## Quality gates\n\n```powershell\npowershell -File scripts\\run_stage_1.ps1   # format, clippy, no_std checks, full test suite\npowershell -File scripts\\run_stage_2.ps1   # CRAP score and file risk analysis\n```\n\nBoth gates must pass before any commit lands.\n\n---\n\n## Roadmap\n\n`faction` is building toward full dynamic membership — node joining, failure detection,\nsingle-node addition and removal, and Byzantine-tolerant reconfiguration across six\nincremental phases.\n\nEach phase is a strict superset of the previous; Phase 0 tests pass unchanged at Phase 6.\nNo phase begins until the previous phase has 100% `(state, command)` coverage.\n\nSee [ROADMAP.md](./docs/ROADMAP.md) for the full plan and [ARCHITECTURE.md](./docs/ARCHITECTURE.md)\nfor the complete technical specification.\n\n---\n\n## Workspace\n\n| Crate | Role | Tests |\n|---|---|---|\n| `core/` | State machine — 13 source files | 145 |\n| `core-validation/` | Deterministic multi-node scenario harness | 23 |\n| `protocol/` | Message translator and protocol runtime | 33 |\n| `protocol-validation/` | In-process protocol cluster | 9 |\n| `system-tests/` convergence | (10 spawn/transport × 2 quorum scenarios) | 20 |\n| `system-tests/` infrastructure | TCP, gRPC, channels, in-memory plumbing | 44 |\n\n---\n\n## License\n\nMIT. See [LICENSE](./LICENSE).\n\n---\n\n## Links\n\n- [ARCHITECTURE.md](./docs/ARCHITECTURE.md) — complete technical specification\n- [ROADMAP.md](./docs/ROADMAP.md) — phased development plan\n- [CHANGELOG.md](./CHANGELOG.md) — version history\n- [DONATE.md](./DONATE.md) — support the project\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fumbgtt10%2Ffaction","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fumbgtt10%2Ffaction","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fumbgtt10%2Ffaction/lists"}