pythops/bluetui
π TUI for managing bluetooth on Linux
Mixed signals β read the receipts
weakest axiscopyleft license (GPL-3.0) β review compatibility; no tests detected
Has a license, tests, and CI β clean foundation to fork and modify.
Documented and popular β useful reference codebase to read through.
No critical CVEs, sane security posture β runnable as-is.
- βLast commit 2d ago
- β24+ active contributors
- βDistributed ownership (top contributor 39% of recent commits)
Show all 7 evidence items βShow less
- βGPL-3.0 licensed
- βCI configured
- β GPL-3.0 is copyleft β check downstream compatibility
- β No test directory detected
What would change the summary?
- βUse as dependency Concerns β Mixed if: relicense under MIT/Apache-2.0 (rare for established libs)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README β live-updates from the latest cached analysis.
[](https://repopilot.app/r/pythops/bluetui)Paste at the top of your README.md β renders inline like a shields.io badge.
βΈPreview social card (1200Γ630)
This card auto-renders when someone shares https://repopilot.app/r/pythops/bluetui on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: pythops/bluetui
Generated by RepoPilot Β· 2026-05-09 Β· Source
π€Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale β STOP and ask the user to regenerate it before proceeding. - Treat the AI Β· unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/pythops/bluetui shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything β but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
π―Verdict
WAIT β Mixed signals β read the receipts
- Last commit 2d ago
- 24+ active contributors
- Distributed ownership (top contributor 39% of recent commits)
- GPL-3.0 licensed
- CI configured
- β GPL-3.0 is copyleft β check downstream compatibility
- β No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
β Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live pythops/bluetui
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale β regenerate it at
repopilot.app/r/pythops/bluetui.
What it runs against: a local clone of pythops/bluetui β the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in pythops/bluetui | Confirms the artifact applies here, not a fork |
| 2 | License is still GPL-3.0 | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit β€ 32 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of pythops/bluetui. If you don't
# have one yet, run these first:
#
# git clone https://github.com/pythops/bluetui.git
# cd bluetui
#
# Then paste this script. Every check is read-only β no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of pythops/bluetui and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "pythops/bluetui(\\.git)?\\b" \\
&& ok "origin remote is pythops/bluetui" \\
|| miss "origin remote is not pythops/bluetui (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(GPL-3\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"GPL-3\\.0\"" package.json 2>/dev/null) \\
&& ok "license is GPL-3.0" \\
|| miss "license drift β was GPL-3.0 at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "src/main.rs" \\
&& ok "src/main.rs" \\
|| miss "missing critical file: src/main.rs"
test -f "src/app.rs" \\
&& ok "src/app.rs" \\
|| miss "missing critical file: src/app.rs"
test -f "src/bluetooth.rs" \\
&& ok "src/bluetooth.rs" \\
|| miss "missing critical file: src/bluetooth.rs"
test -f "src/handler.rs" \\
&& ok "src/handler.rs" \\
|| miss "missing critical file: src/handler.rs"
test -f "src/ui.rs" \\
&& ok "src/ui.rs" \\
|| miss "missing critical file: src/ui.rs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 32 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~2d)"
else
miss "last commit was $days_since_last days ago β artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) β safe to trust"
else
echo "artifact has $fail stale claim(s) β regenerate at https://repopilot.app/r/pythops/bluetui"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
β‘TL;DR
bluetui is a Linux TUI (terminal user interface) for discovering, pairing, and managing Bluetooth devices via the BlueZ daemon. It provides a real-time, keyboard-navigable interface to control Bluetooth adapters, pair/unpair devices, manage trusted/favorite status, and handle pairing authentication requests (PIN/passkey entry) without leaving the terminal. Single-crate Rust binary structured as src/{lib,main,cli,app,handler,event,bluetooth,rfkill,config,...}.rs with modular request handlers (src/requests/) for pairing prompts (confirmation, PIN/passkey entry/display), device state (src/favorite.rs, src/alias.rs), and UI rendering via ratatui. Event-driven architecture: crossterm streams raw terminal events, tokio::spawn handles async DBus operations, and async-channel coordinates between TUI and Bluetooth threads.
π₯Who it's for
Linux power users and system administrators who manage Bluetooth devices from the command line or prefer TUI over GNOME Settings/systemd-qt-bluetooth-manager. Developers integrating Bluetooth management into headless or minimal Linux systems.
π±Maturity & risk
Actively developed and production-ready. The project is at version 0.8.1 with packaging in Arch Linux (extra repo) and Gentoo, indicating stable adoption. CI pipeline is present (ci.yaml, release.yaml), snapshot tests are in place, and the codebase enforces Clippy lints. However, pre-1.0 versioning suggests API surfaces may still shift.
Low risk operationally but tied to BlueZ stability. Heavy dependency on bluer (0.17) for DBus Bluetooth daemon communicationβupstream changes could break pairing flows. Single-maintainer appearance (pythops) and relatively small issue backlog suggest healthy but narrow resource allocation. No apparent breaking changes in recent activity, but pre-1.0 versioning allows them.
Active areas of work
No specific active PR or milestone data visible in the file list, but the presence of workflow files (ci.yaml, release.yaml) and recent snapshot tests indicates ongoing maintenance. The config system (src/config.rs, src/cli.rs) supports keybinding customization and layout modes, suggesting the focus is on UX polish and extensibility rather than major feature work.
πGet running
git clone https://github.com/pythops/bluetui
cd bluetui
cargo build --release
./target/release/bluetui
Or install via Arch: pacman -S bluetui, or crates.io: cargo install bluetui. Requires BlueZ installed on the system (bluez package).
Daily commands:
cargo run --release
Or after building: ./target/release/bluetui. No dev server; this is a native TUI that runs as a foreground process. Must have BlueZ daemon (bluetoothd) running and user must be in bluetooth group or have dbus permissions.
πΊοΈMap of the codebase
src/main.rsβ Application entry point; initializes the async runtime, TUI setup, and coordinates the main event loop between UI and Bluetooth backend.src/app.rsβ Core application state machine; manages all UI screens, navigation, and application-level state transitionsβessential for understanding control flow.src/bluetooth.rsβ Bluetooth adapter and device management via thebluercrate; abstracts all D-Bus interactions with BlueZ daemon and is critical for all device operations.src/handler.rsβ Event handler that bridges user input (keyboard/mouse) to app state mutations; essential for understanding how the TUI responds to user actions.src/ui.rsβ Ratatui widget rendering layer; responsible for converting app state into terminal UI componentsβevery visual change flows through here.src/agent.rsβ Agent service spawns and manages long-lived async tasks for device discovery, pairing, and D-Bus signal handling; critical concurrency layer.Cargo.tomlβ Project manifest with all dependencies;bluer,ratatui,tokio, andcrosstermare the pillars of this TUI Bluetooth manager.
π οΈHow to make changes
Add a new TUI screen (e.g., Device Settings)
- Add a new variant to the
Screenenum insrc/app.rs(e.g.,DeviceSettings) (src/app.rs) - Add a corresponding render function in
src/ui.rswith patternrender_device_settings()that returns aFramewidget (src/ui.rs) - Add input handling in
src/handler.rsto capture keys/input for the new screen and mutate app state accordingly (src/handler.rs) - Add help text in
src/help.rsdocumenting keybindings for the new screen (src/help.rs)
Add a new Bluetooth device operation (e.g., Set Device Volume)
- Add a new method to
struct Adapterinsrc/bluetooth.rs(e.g.,pub async fn set_device_volume()) that calls the appropriate BlueZ D-Bus method via bluer (src/bluetooth.rs) - Add an
Eventvariant insrc/event.rsto represent task completion (e.g.,BluetoothVolumeSet) (src/event.rs) - Spawn the async operation in
src/handler.rsorsrc/agent.rsdepending on whether it's user-initiated or a daemon-like task (src/handler.rs) - Handle the event result in the main loop (in
src/main.rsor event matching logic) to update app state and render feedback (src/main.rs)
Add a new D-Bus request type (e.g., AuthorizeService)
- Create a new request handler file
src/requests/authorize_service.rswith a struct implementing the request callback interface (src/requests/authorize_service.rs) - Register the handler in
src/agent.rsby adding a variant to the request enum and hooking it into the D-Bus signal listener (src/agent.rs) - Add a screen variant in
src/app.rs(e.g.,RequestAuthorizeService) and a corresponding render function insrc/ui.rs(src/app.rs) - Add input handling in
src/handler.rsto approve/deny the request and send the response back via D-Bus (src/handler.rs)
Add a new configuration option
- Add a field to the config struct in
src/config.rswith serde derive annotations (src/config.rs) - Load and apply the config in
src/app.rsduring initialization or insrc/handler.rson config reload (src/app.rs) - If user-configurable, add a UI screen/dialog in
src/ui.rsand input handling insrc/handler.rs(src/ui.rs)
π§Why these technologies
- Tokio async runtime β Non-blocking I/O for D-Bus calls and Bluetooth device discovery; allows responsive TUI while waiting for BlueZ daemon responses.
- Bluer crate β High-level Rust bindings to BlueZ D-Bus API; eliminates manual D-Bus serialization and provides type-safe Bluetooth abstractions.
- Ratatui β Declarative TUI widget library; simplifies terminal rendering and enables responsive, themeable UI without raw ANSI codes.
- Crossterm β Cross-platform terminal event handling; provides keyboard/mouse input and raw mode management without platform-specific code.
- async-channel β Thread-safe event channels for decoupling TUI event loop from async Bluetooth tasks; ensures clean separation of concerns.
βοΈTrade-offs already made
-
Single-threaded async event loop with Tokio runtime
- Why: Simplifies state management and avoids complex locking patterns in the TUI.
- Consequence: Long-running synchronous operations (if any) would block the entire UI; must ensure all I/O is async or delegated to agent tasks.
-
D-Bus agent pattern for PIN/passkey requests
- Why: BlueZ requires a registered D-Bus agent service to handle pairing callbacks; this is the only way to intercept and present pairing dialogs.
- Consequence: Adds complexity in signal listening and D-Bus method response; requires careful error handling if the TUI crashes during a pairing flow.
-
Stateful UI state machine (Screen enum) in
app.rs- Why: Centralizes navigation logic and prevents inconsistent state; makes testing and state debugging easier.
- Consequence: Adding new screens or state transitions requires coordinated changes across
app.rs,handler.rs, andui.rs; no automatic state derivation.
-
RFKill integration for Bluetooth toggle
- Why: Provides system-level radio control; more reliable than D-Bus-only control on some hardware.
- Consequence: Requires elevated privileges or udev rules; failure to access
/sys/class/rfkillwill silently prevent Bluetooth toggle.
π«Non-goals (don't propose these)
- Cross-platform
πͺ€Traps & gotchas
- BlueZ daemon must run: bluetui will fail silently if
bluetoothdisn't active; no clear user-facing error. 2) DBus permissions: User must be inbluetoothgroup or have explicit dbus policy; errors manifest as DBus permission denied, not caught gracefully. 3) Nerd font required: Icons render as garbage without nerd fonts installed; README notes this but doesn't auto-detect. 4) Config path is hardcoded:$HOME/.config/bluetui/config.tomlor-cflag; no env var override like$BLUETUI_CONFIG. 5) Pairing prompt context: src/agent.rs hooks into BlueZ agent callbacks; if bluetui crashes mid-pairing, the device may be left in a pending state requiring manualbluetoothctlcleanup.
ποΈArchitecture
π‘Concepts to learn
- BlueZ Agent Protocol (DBus) β bluetui implements the BlueZ Agent interface (src/agent.rs) to intercept pairing callbacks; understanding RequestConfirmation, DisplayPasskey, etc. is critical to modify auth flows.
- Async-aware Event Channels (async-channel, tokio::mpsc) β bluetui uses async-channel and tokio channels to decouple terminal input (crossterm thread) from Bluetooth DBus callbacks; misunderstanding this leads to deadlocks or dropped events.
- Snapshot Testing (insta crate) β Test assertions in src/requests/ use insta snapshots instead of manual assertions; modifying UI will auto-regenerate .snap files; important to understand before changing request prompts.
- DBus Introspection & Method Calls β src/bluetooth.rs uses
bluerto make async DBus calls to org.bluez services; understanding DBus paths (e.g., /org/bluez/hci0/dev_XX) helps debug device discovery. - Terminal State Machines (Ratatui App Pattern) β src/app.rs models the TUI as a state machine with screens (Adapters, PairedDevices, NewDevices, Requests); understanding StatefulWidget trait and focus management prevents UI regressions.
- TOML Configuration Deserialization β src/config.rs uses serde + toml to parse keybindings; adding new config fields requires understanding serde derive macros and TOML nesting.
- Cross-platform Terminal Control (crossterm) β bluetui uses crossterm::event::EventStream for non-blocking keyboard input; understanding RawMode, event queue, and terminal size queries helps with edge cases like window resize.
πRelated repos
pythops/impalaβ Sister project for WiFi management on Linux; same author, same TUI patterns with ratatui and tokio, complementary to bluetuibluez/bluezβ Official BlueZ repository; source of truth for DBus APIs and agent protocol that bluetui implements via bluer cratefujiapple852/trippyβ Reference TUI codebase also using ratatui + crossterm + tokio; shows patterns for event-driven terminal UI at scaleratatui/ratatuiβ Core TUI rendering framework used by bluetui; understanding its widget API (Block, List, Paragraph) is essential to modify UI layoutzacchaeus/bluerβ Thebluercrate that wraps BlueZ DBusβbluetui's direct bridge to Bluetooth; inspect this for available device/adapter methods
πͺPR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for src/bluetooth.rs module
The bluetooth.rs module is a core component handling BlueZ D-Bus interactions, but there are no snapshot tests or unit tests visible in src/snapshots/ for it. Given that the project already uses insta for snapshot testing (see requests/snapshots and snapshots directories), adding tests for bluetooth device discovery, pairing, connection state management, and adapter operations would improve reliability and catch regressions early.
- [ ] Create unit tests in src/bluetooth.rs using #[cfg(test)] for device discovery and filtering logic
- [ ] Add snapshot tests using insta crate for Bluetooth adapter state representations
- [ ] Test edge cases: no adapters available, device pairing failures, connection timeouts
- [ ] Run 'cargo test' and verify snapshots are generated in src/snapshots/
Add unit tests for src/config.rs TOML parsing and validation
The config.rs module handles user configuration parsing from TOML files (as seen in Cargo.toml dependencies with 'toml = 0.9'), but no tests are visible for config validation, default values, or invalid configuration handling. This is critical for user experience when configs are malformed.
- [ ] Create test cases in src/config.rs for valid/invalid TOML configurations
- [ ] Add tests for default configuration fallback behavior
- [ ] Test parsing of configuration file paths via dirs crate (seen in dependencies)
- [ ] Add snapshot tests for configuration structure validation errors
Create missing help/UI snapshot tests for remaining screens (Adapter, NewDevices, PairedDevices)
The project has extensive snapshot tests for request screens (confirmation, passkey entry, etc.) at multiple terminal widths (80, 81, 120, 121), but src/snapshots/ shows help tests only exist for some screens. The file list shows missing test snapshots for complete coverage of Adapter, NewDevices, and PairedDevices screens at all width variants.
- [ ] Review src/help.rs to identify all Screen variants needing help text tests
- [ ] Add snapshot tests for each missing screen variant at widths: 80, 81, 120, 121
- [ ] Run snapshot tests: 'cargo test --lib help::tests' and commit .snap files
- [ ] Verify all Screen enum variants have corresponding snapshot files in src/snapshots/
πΏGood first issues
- Add unit tests for src/alias.rs similar to snapshot tests in src/requests/snapshots/βcurrently no dedicated test file for device renaming logic.
- Implement env var override for config path (e.g.,
$BLUETUI_CONFIG) in src/config.rs to improve portability on non-standard home layouts. - Add a
--listor--jsonoutput mode to CLI (src/cli.rs) for scripting device discovery without spawning the TUI; extends usability for automation.
βTop contributors
Click to expand
Top contributors
- @pythops β 39 commits
- @sermuns β 37 commits
- @fereidani β 2 commits
- @Dolphindalt β 2 commits
- @soenkehahn β 1 commits
πRecent commits
Click to expand
Recent commits
de101c1β fix(nix): update flake inputs (#115) (soenkehahn)63b3b18β refactor: use single-threaded tokio and only pull in features we use (sermuns)9d18fe7β refactor(favorite): stop using async fs operations, simplify and optimize (sermuns)0e3118fβ fix(handler): make Tab reset devices state like BackTab (#136) (ozkucukemre)1435e67β fix: fallback to selecting first controller during refresh, if None selected (sermuns)45ced96β chore: remove unnecessary comments (sermuns)601c289β refactor(app): break out rendering every main block into its own method (sermuns)d6ada7fβ refactor(app): use shorthands for styles in new_devices_table (sermuns)239658dβ refactor(app): use shorthands for styles and creating rows for paired_devices_table (sermuns)fdf01ffβ chore: remove newline (sermuns)
πSecurity observations
The bluetui project demonstrates generally good security practices as a TUI application with limited external attack surface. However, it has one critical build configuration issue (invalid Rust edition) that must be fixed immediately. The main security concerns revolve around D-Bus interactions with the underlying Bluetooth system, which require careful handling of privileged operations. The project uses reasonably secure dependencies but lacks explicit security documentation, dependency auditing in CI/CD, and documented guidelines for secure usage. Recommendations focus on fixing the build configuration, adding security documentation, implementing input validation, and setting up automated dependency scanning.
- High Β· Invalid Rust Edition in Cargo.toml β
Cargo.toml. The Cargo.toml specifies edition = "2024" which is not a valid Rust edition. Valid editions are 2015, 2018, and 2021. This will cause build failures and suggests the project has not been properly tested or built, potentially masking other security issues. Fix: Change edition to a valid value: edition = "2021" (or "2018" for older compatibility) - Medium Β· Potential Unsafe DBus Interaction β
Cargo.toml (libdbus-sys dependency), src/bluetooth.rs, src/agent.rs. The codebase uses bluer (Bluetooth library) with libdbus-sys as a dependency. The libdbus-sys is a low-level FFI binding to the D-Bus C library. Improper handling of D-Bus messages could lead to security issues like privilege escalation, information disclosure, or denial of service attacks. Fix: Ensure all D-Bus method calls are properly validated. Use allowlists for authorized D-Bus calls. Implement proper error handling for D-Bus responses. Consider using higher-level safe wrappers where available. - Medium Β· Vendored C Library Dependencies β
Cargo.toml (libdbus-sys feature). The libdbus-sys dependency uses vendored = true for the C library. While this improves portability, it means the project bundles an external C library (libdbus) which may not receive regular security updates and could lag behind upstream patches. Fix: Regularly monitor upstream libdbus releases for security patches. Consider using system libdbus when possible. Document the vendored approach in security documentation. - Medium Β· Insufficient Input Validation in CLI/Config β
src/cli.rs, src/config.rs. The project uses clap for CLI parsing and toml for configuration files. Without explicit validation in src/cli.rs and src/config.rs, user-supplied input from both CLI arguments and configuration files could potentially be processed unsafely, leading to unexpected behavior or vulnerabilities. Fix: Implement strict input validation for all CLI arguments and config file values. Define expected formats and ranges. Use type-safe parsing. Validate file paths to prevent directory traversal attacks. - Low Β· Missing Security Headers in Documentation β
Readme.md, src/main.rs documentation. The README and project documentation do not explicitly mention security considerations, prerequisites for secure usage (e.g., running with minimal privileges), or known limitations regarding Bluetooth security. Fix: Add a security section to the README documenting: required permissions, security considerations for Bluetooth operations, how the tool handles sensitive data (PINs, passphrases), and recommendations for secure deployment. - Low Β· No Dependency Auditing Configured β
.github/workflows/ci.yaml. No evidence of dependency auditing tools (cargo-audit, cargo-deny) configured in CI/CD pipeline (.github/workflows/ci.yaml). This means vulnerable dependencies might not be caught during development. Fix: Configure cargo-audit or cargo-deny in the CI/CD pipeline to automatically check for known vulnerabilities in dependencies. - Low Β· Potential Information Disclosure in Error Messages β
src/lib.rs, src/main.rs, error handling throughout codebase. Using anyhow for error handling without explicit sanitization could lead to verbose error messages being displayed to users, potentially leaking system information or internal paths. Fix: Implement custom error types with user-friendly messages. Log detailed errors server-side only. Ensure error messages don't expose file paths, system information, or internal implementation details to end users.
LLM-derived; treat as a starting point, not a security audit.
πWhere to read next
- Open issues β current backlog
- Recent PRs β what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals β see the live page for receipts. Re-run on a new commit to refresh.