| Crates.io | fastbreak |
| lib.rs | fastbreak |
| version | 0.4.1 |
| created_at | 2026-01-12 06:58:10.753805+00 |
| updated_at | 2026-01-16 01:43:16.339932+00 |
| description | A formal methods-inspired specification language combining Alloy, TLA+, Cucumber, and Design by Contract |
| homepage | https://github.com/ryanoneill/fastbreak |
| repository | https://github.com/ryanoneill/fastbreak |
| max_upload_size | |
| id | 2037141 |
| size | 712,915 |
A formal methods-inspired specification language combining ideas from Alloy, TLA+, Cucumber, and Design by Contract.
Fastbreak provides a unified language for specifying software systems that is both formal and readable. It allows you to define:
cargo install fastbreak
Or build from source:
git clone https://github.com/ryanoneill/fastbreak.git
cd fastbreak
cargo build --release
# Initialize a new project
fastbreak init my-spec
# Check specifications for errors
fastbreak check
# Build everything (check + generate docs + diagrams)
fastbreak build
Fastbreak uses a Rust-like syntax with the .fbrk file extension.
Specifications can be organized across multiple files using modules and imports:
// In types.fbrk
module myproject.types
type UserId { value: Int }
type Email { address: String }
// In users.fbrk
module myproject.users
use myproject.types::{UserId, Email}
type User {
id: UserId,
email: Email,
}
Import aliases allow renaming imported items:
use types::{Identifier as Id, Email as E}
type User {
id: UserId,
email: Email,
status: UserStatus,
}
enum UserStatus {
Pending,
Active,
Suspended,
}
type Email = String where self.contains("@")
type PositiveInt = Int where self > 0
relation friends: User -> Set<User> {
symmetric
irreflexive
}
state AuthSystem {
users: Set<User>,
sessions: Map<SessionId, UserId>,
invariant "All sessions reference existing users" {
forall sid in sessions.keys() =>
sessions[sid] in users.map(u => u.id)
}
}
action register(email: Email) -> Result<User, RegisterError>
requires {
not exists u in users where u.email == email
}
ensures {
match result {
Ok(user) => user in users' and user.email == email,
Err(_) => users' == users,
}
}
scenario "New user registration" {
given {
users = {}
sessions = {}
}
when {
result = register("test@example.com")
}
then {
result is Ok
users.len() == 1
}
alt "Email already exists" when exists u in users where u.email == email {
then {
result is Err
users.len() == old_count
}
}
}
property "Users are unique by email" {
always {
forall u1, u2 in users where u1 != u2 =>
u1.email != u2.email
}
}
property "Parsing is deterministic" {
always {
forall source: String =>
parse(source) == parse(source)
}
}
@id("NFR-001")
quality performance "API response time" {
metric: latency,
target: < 100ms,
}
@id("NFR-002")
quality reliability "System uptime" {
metric: availability,
target: >= 99.9%,
}
@id("REQ-001")
@rationale("Users must be uniquely identifiable")
type User {
id: UserId,
name: String,
}
# Initialize a new project
fastbreak init <name> [--path <dir>]
# Check specifications for errors
fastbreak check [<file>]
# Generate markdown documentation
fastbreak doc [<file>]
# Generate Mermaid diagrams
fastbreak diagram [<file>] [--type <erd|state|sequence>]
# Build everything (check + doc + diagram)
fastbreak build
A Fastbreak project uses the following structure:
my-spec/
├── fastbreak.toml # Project manifest
├── specs/ # Specification files
│ └── main.fbrk
└── docs/ # Generated output
├── my-spec.md
├── my-spec_erd.mmd
├── my-spec_state.mmd
└── my-spec_sequence.mmd
[project]
name = "my-spec"
version = "0.1.0"
[source]
dir = "specs"
extension = "fbrk"
[output]
dir = "docs"
markdown = true
diagrams = true
[output.diagram_types]
erd = true
state = true
sequence = true
Fastbreak is specified using Fastbreak itself. The self-specification lives in spec/ and serves as both documentation and validation:
cd spec
../target/release/fastbreak build
Fastbreak includes a rich expression language supporting:
{a, b}, lists [a, b], maps {k: v}forall x in xs => pred(x) and exists x in xs where pred(x)forall x: Type => pred(x) for universal propertiesmatch expr { Pat => result, ... }|x| x + 1 for map/filter operationsuser.emaillist.len(), set.contains(x), option.unwrap()1..10Properties can use temporal operators for specifying behavior over time:
always { expr } - Expression holds in all stateseventually { expr } - Expression holds in at least one stateGenerates comprehensive documentation including:
MIT