FlashKraft ⚡
A lightning-fast, lightweight OS image writer built entirely in Rust. Choose your interface:
flashkraft (GUI) |
flashkraft-tui (TUI) |
|
|---|---|---|
| Framework | Iced 0.13 | Ratatui 0.30 |
| Input | Mouse + keyboard | Keyboard only |
| Themes | 21 built-in Iced themes | Multiple themes via tui-file-explorer |
| Best for | Desktop users | SSH / headless / minimal setups |
No Electron, no shell scripts, no external tooling — pure Rust from UI to block device.
Preview
GUI (Iced desktop)
TUI (Ratatui terminal)
Full workflow

Flash progress (tui-slider)

File explorer & theme switcher

Note: Demo GIFs are stored with Git LFS. Run
git lfs install && git lfs pullafter cloning if the images appear broken.
Features
Shared (both interfaces)
- ⚡ Pure-Rust flash engine — no
dd, no bash scripts; writes directly to the block device usingstd::fs,nixioctls, andsha2verification - 🔒 Write verification — SHA-256 of the source image is compared against a read-back of the device after every flash
- 🔄 Partition table refresh —
BLKRRPARTioctl ensures the kernel picks up the new partition layout immediately, so the USB boots first time - 🧲 Lazy unmount — all partitions are cleanly detached via
umount2(MNT_DETACH)before writing - 📁 Multiple image formats — ISO, IMG, DMG, ZIP, and more
- 💾 Automatic drive detection — removable drives refreshed on demand
- 🛡️ Safe drive selection — system drives flagged, oversized drives warned, read-only drives blocked
- 🎯 Real-time progress — stage-aware progress bar with live MB/s speed display
- 🪶 Tiny footprint — Rust-compiled binary, C-like memory usage, no Electron runtime
GUI extras
- 🎨 21 beautiful Iced themes to choose from, persisted across sessions via
sled - 🖱️ Native file picker — powered by
rfdfor OS-native open dialogs
TUI extras
- ⌨️ Fully keyboard-driven — vim-style
j/knavigation,b/Escto go back - 📂 Built-in file explorer — browse and pick ISO files without leaving the terminal (
Tab/Ctrl+F) - 📊 Pie-chart drive overview — storage breakdown rendered inline using
tui-piechart - ✅ Checkbox confirmation screen — safety checklist before every flash via
tui-checkbox - 🎚️ Slider progress bar — smooth flash-progress widget via
tui-slider - 🎨 Multiple file-explorer themes — switchable live with
t/[, panel toggled withT, powered bytui-file-explorer - 🖥️ Works over SSH — no display server required
How flashing works
FlashKraft uses a self-elevating pure-Rust helper pattern. When you click/confirm Flash:
Main process (GUI or TUI)
└─ pkexec /path/to/flashkraft[−tui] --flash-helper <image> <device>
└─ Runs as root, pure Rust, no shell
1. UNMOUNTING — reads /proc/mounts, calls umount2(MNT_DETACH) per partition
2. WRITING — streams image → block device in 4 MiB chunks, emits PROGRESS lines
3. SYNCING — fsync(fd) + sync() to flush all kernel write-back caches
4. REREADING — BLKRRPART ioctl so the kernel sees the new partition table
5. VERIFYING — SHA-256(image) == SHA-256(device[0..image_size])
6. DONE — UI shows success
The same binary is re-executed with elevated privileges via pkexec — no separate helper binary needs to be installed. All output (progress, logs, errors) is written to stdout as structured lines that the UI reads in real time.
Why not dd?
dd approach |
FlashKraft | |
|---|---|---|
| Shell dependency | ✗ requires bash, coreutils | ✓ pure Rust |
| Progress format | \r-terminated, locale-dependent |
✓ structured PROGRESS:bytes:speed |
| Speed unit handling | ✗ kB/s / MB/s / GB/s mixed | ✓ always normalised to MB/s |
| Write verification | ✗ none | ✓ SHA-256 read-back |
| Partition table refresh | ✗ not done | ✓ BLKRRPART ioctl |
| Error reporting | ✗ exit code only | ✓ ERROR:<message> on every failure path |
The Elm Architecture (GUI)
The GUI crate is built using The Elm Architecture (TEA), which Iced embraces as its natural pattern for interactive applications.
Core Concepts
1. Model (State)
2. Messages
3. Data Flow
User Action → Message → Update → State
↓
Task/Subscription
↓
Async Result → Message → Update → State
↓
View → UI
TUI Screen Flow
The TUI is a multi-screen application driven entirely by keyboard input:
SelectImage ──(Enter/confirm)──► SelectDrive ──(Enter)──► DriveInfo
▲ ▲ │
│ (Esc/b) │ (Esc/b) (f/Enter)
│ │ ▼
│ SelectDrive ConfirmFlash
│ │
│ (y — flash)
│ ▼
│ Flashing
│ (c — cancel)
│ │
└──────────────────(r — reset)────────── Complete / Error
TUI Key Bindings
| Screen | Key | Action |
|---|---|---|
| SelectImage | i / Enter |
Enter editing mode |
| SelectImage | Tab / Ctrl+F |
Open built-in file browser |
| SelectImage | Esc / q |
Quit |
| BrowseImage | j / ↓ |
Move cursor down |
| BrowseImage | k / ↑ |
Move cursor up |
| BrowseImage | Enter |
Descend into directory / select file |
| BrowseImage | Backspace |
Ascend to parent directory |
| BrowseImage | Esc / q |
Dismiss without selecting |
| SelectDrive | j / ↓ |
Scroll drive list down |
| SelectDrive | k / ↑ |
Scroll drive list up |
| SelectDrive | Enter / Space |
Confirm selected drive |
| SelectDrive | r / F5 |
Refresh drive list |
| SelectDrive | Esc / b |
Go back |
| DriveInfo | f / Enter |
Advance to ConfirmFlash |
| DriveInfo | Esc / b |
Go back |
| ConfirmFlash | y / Y |
Begin flashing |
| ConfirmFlash | n / Esc / b |
Go back |
| Flashing | c / Esc |
Cancel flash |
| Complete | r / R |
Reset to start |
| Complete | q / Esc |
Quit |
| Error | r / Enter |
Reset to start |
| Error | q / Esc |
Quit |
| Any | Ctrl+C / Ctrl+Q |
Force quit |
Building and Running
Prerequisites
- Rust 1.70 or later
pkexec(part ofpolkit, available on all major Linux distributions)- For GUI: a running display server (X11 or Wayland)
- For TUI: any terminal emulator (works over SSH)
Build
# Build everything
# Build only the GUI
# Build only the TUI
Run
# Launch the GUI
# Launch the TUI
Development
# Debug builds (faster compilation)
# Run all tests across the workspace
# Check without building
# Lint
# With backtraces
RUST_BACKTRACE=1
Usage
GUI
- Select Image — click the
+button and choose an ISO, IMG, or DMG file - Select Drive — pick the target USB or SD card from the detected drives list
- Flash — click Flash!; authenticate with
pkexecwhen prompted - Wait — the progress bar shows live stage, bytes written, and MB/s
- Done — verification passes automatically; safely remove the drive
TUI
- Select Image — press
ito start typing a path, orTab/Ctrl+Fto open the file browser - Select Drive — use
j/kto scroll,rto refresh,Enterto confirm - Review Drive Info — inspect the storage pie-chart, then press
fto proceed - Confirm — read the safety checklist and press
yto flash, orbto go back - Wait — the slider progress bar shows live stage, bytes written, and MB/s
- Done — press
rto reset orqto quit
Project Structure
This is a Cargo workspace with three crates:
flashkraft/ ← workspace root
├── Cargo.toml ← workspace manifest (shared dep versions)
│
├── crates/
│ │
│ ├── flashkraft-core/ ★ shared logic — no GUI/TUI deps
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── flash_helper.rs ★ privileged flash pipeline (pkexec)
│ │ ├── flash_writer.rs ★ wire-protocol parser & speed normaliser
│ │ ├── domain/
│ │ │ ├── drive_info.rs
│ │ │ ├── image_info.rs
│ │ │ └── constraints.rs drive/image compatibility checks
│ │ ├── commands/
│ │ │ └── drive_detection.rs async /sys/block enumeration
│ │ └── utils/
│ │ └── logger.rs debug_log!, flash_debug!, status_log! macros
│ │
│ ├── flashkraft-gui/ Iced desktop application
│ │ ├── examples/
│ │ │ ├── basic_usage.rs
│ │ │ ├── custom_theme.rs
│ │ └── vhs/ GUI VHS tapes
│ │ ├── demo-basic.tape
│ │ ├── demo-build.tape
│ │ ├── demo-quick.tape
│ │ └── generated/ output GIFs (Git LFS)
│ │ └── src/
│ │ ├── main.rs entry point + --flash-helper dispatch
│ │ ├── lib.rs
│ │ ├── view.rs view orchestration
│ │ ├── core/ Elm Architecture
│ │ │ ├── state.rs Model + TEA methods
│ │ │ ├── message.rs all Message variants
│ │ │ ├── update.rs state transition logic
│ │ │ ├── storage.rs sled-backed theme persistence
│ │ │ ├── flash_subscription.rs Iced Subscription — streams FlashProgress
│ │ │ └── commands/
│ │ │ └── file_selection.rs async rfd file dialog
│ │ └── components/ UI widgets
│ │ ├── animated_progress.rs
│ │ ├── device_selector.rs
│ │ ├── header.rs
│ │ ├── progress_line.rs
│ │ ├── selection_panels.rs
│ │ ├── status_views.rs
│ │ ├── step_indicators.rs
│ │ └── theme_selector.rs
│ │
│ └── flashkraft-tui/ Ratatui terminal application
│ ├── examples/
│ │ ├── headless_demo.rs
│ │ ├── tui_demo.rs
│ │ ├── flash_progress_demo.rs ← tui-slider progress showcase
│ │ └── theme_demo.rs ← file-explorer theme switcher showcase
│ └── vhs/ TUI VHS tapes
│ ├── tui-demo.tape
│ ├── tui-headless.tape
│ ├── flash-progress.tape
│ ├── theme-switcher.tape
│ └── generated/ output GIFs (Git LFS)
│ └── src/
│ ├── main.rs entry point + --flash-helper dispatch
│ ├── lib.rs
│ ├── tui/
│ │ ├── app.rs App state + all screen transitions
│ │ ├── ui.rs ratatui Frame rendering (all screens)
│ │ ├── events.rs keyboard event handler per screen
│ │ ├── flash_runner.rs async pkexec supervisor + line parser
│ │ └── mod.rs
│ └── file_explorer/
│ └── mod.rs built-in keyboard-driven file browser
│
├── scripts/
│ ├── bump_version.sh
│ └── check_publish.sh
│
└── .github/workflows/
├── ci.yml
└── release.yml
Items marked ★ form the flash pipeline and are described in detail above.
Dependencies
flashkraft-core
| Crate | Version | Purpose |
|---|---|---|
sysinfo |
0.30 | Drive enumeration |
nix |
0.29 | umount2, BLKRRPART ioctl, fsync |
sha2 |
0.10 | SHA-256 write verification |
tokio |
1 | Async runtime |
futures / futures-timer |
0.3 / 3.0 | Async channel primitives |
sled |
0.34 | Embedded key-value store |
dirs |
5.0 | XDG data directory resolution |
anyhow |
1 | Error handling |
flashkraft-gui
| Crate | Version | Purpose |
|---|---|---|
iced |
0.13 | Cross-platform GUI framework (Elm Architecture) |
iced_aw |
0.12 | Additional Iced widgets |
iced_fonts |
0.1 | Bootstrap icon font |
rfd |
0.15 | Native file/folder dialogs |
flashkraft-tui
| Crate | Version | Purpose |
|---|---|---|
ratatui |
0.30 | Terminal UI framework |
crossterm |
0.29 | Cross-platform terminal control |
tui-slider |
git | Flash-progress slider widget |
tui-piechart |
git | Drive storage pie-chart widget |
tui-checkbox |
git | Drive-list and confirm-screen checkboxes |
Architecture Highlights
- Shared core crate —
flashkraft-corecontains all flash logic; both UIs are thin frontends over the same engine - Pure-Rust flash engine — zero shell scripts or external binaries
- Self-elevating helper — single binary per UI, no install-time setup beyond
polkit - Elm Architecture (GUI) — unidirectional data flow, pure
update/viewfunctions - Screen-based state machine (TUI) — each
AppScreenvariant owns its event handler and render function - 0 warnings — clean
cargo build --workspaceandcargo test --workspace
Demo GIFs & Git LFS
All generated GIFs under examples/vhs/generated/ are tracked by Git LFS (see .gitattributes).
# One-time setup after cloning
&&
# Regenerate all demos (requires vhs installed)
# Regenerate TUI demos only (output: crates/flashkraft-tui/examples/vhs/generated/)
# Regenerate GUI demos only (output: crates/flashkraft-gui/examples/vhs/generated/)
# Render a single tape by name
# List all available tapes and generated GIFs
| Tape | What it shows |
|---|---|
tui-demo |
Full keyboard-driven wizard: image → drive → flash → complete |
tui-headless |
Headless state-machine demo (no TTY required) |
flash-progress |
Animated tui-slider progress bar during a simulated write |
theme-switcher |
Live file-explorer theme cycling (t / [) and theme panel (T) |
demo-basic |
GUI basic usage |
demo-build |
GUI build walkthrough |
demo-quick |
GUI quick-start |
Install VHS:
To run Rust examples from the workspace root:
# Core examples
# GUI examples (crates/flashkraft-gui/examples/)
# TUI examples (crates/flashkraft-tui/examples/)
Contributing
Contributions are welcome! Please:
- Keep all flash logic in
flashkraft-core— neither the GUI nor the TUI crate should contain flash pipeline code - Follow the Elm Architecture pattern in the GUI — all state changes via
update - Follow the screen-state-machine pattern in the TUI — screen transitions via
Appmethods - Keep functions pure where possible
- Add unit tests for any new logic, especially in
flash_helper.rs,flash_writer.rs,app.rs, andevents.rs - Run
cargo test --workspaceandcargo clippy --workspacebefore opening a PR
Learning Resources
- Iced Documentation
- Ratatui Documentation
- The Elm Architecture Guide
- Iced Examples
- Ratatui Examples
- The Rust Book
- nix crate — POSIX ioctls and syscalls from Rust
License
MIT — see LICENSE for details.
Acknowledgments
- GUI built with Iced
- TUI built with Ratatui
- Follows The Elm Architecture
- Flash pipeline design inspired by Balena Etcher
- Terminal widgets: tui-slider, tui-piechart, tui-checkbox