- Rust 99.2%
- PowerShell 0.5%
- Shell 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| .github/workflows | ||
| .grit | ||
| src | ||
| .gitignore | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| install.ps1 | ||
| install.sh | ||
| LICENSE | ||
| README.md | ||
Starling TUI
This is the terminal (TUI) client for Starling — a federated peer-to-peer communications platform. For the main project, see the Starling repository.
⚠️ Early access — under active development. This is the working client, but it's young and changing fast. Expect rough edges and breaking changes between versions. Bug reports and feedback are welcome.
A federated peer-to-peer communications platform where peers — known as birds — can communicate from anywhere in the world thanks to a peer-to-peer network called the murmuration.
Starling TUI runs in the terminal and provides text chat, voice calls, and video calls — all end-to-end encrypted. Birds discover each other through the murmuration using iroh's relay and discovery infrastructure — no central server required. A room code is all a new bird needs to join a flock.
Platform support
| Feature | Windows | macOS | Linux | WSL2 |
|---|---|---|---|---|
| Text chat | ✓ | ✓ | ✓ | ✓ |
| Voice calls (mic + playback) | ✓ | ✓ | ✓ | ✓† |
| Video calls (webcam) | ✓ | ✓ | ✓ | — |
| Room codes | ✓ | ✓ | ✓ | ✓ |
| Persistent identity | ✓ | ✓ | ✓ | ✓ |
† WSL2 voice requires a one-time setup step (starling profile)
that installs the ALSA→PulseAudio bridge. See
WSL2 setup below.
WSL2 does not expose webcams by default — use a native Windows build for video calls.
Install
Recommended: install the starling launcher, then:
starling install tui
Or install standalone — no Rust or compiler needed:
Windows (PowerShell):
irm https://forgejo.hearthhome.lol/Saltfault/Starling-TUI/raw/branch/main/install.ps1 | iex
Linux / macOS:
curl -sSfL https://forgejo.hearthhome.lol/Saltfault/Starling-TUI/raw/branch/main/install.sh | bash
The installer places starling-tui in your PATH. Run starling-tui to launch.
Upgrade: re-run the same command, or starling update tui. Uninstall: install.sh --uninstall.
Configure your profile (one-time):
starling profile
This opens a setup wizard where you enter your display name, select your microphone and speaker, and get a 32-digit profile code. The code encodes your name and can be used to restore your profile on another machine. The profile is saved to disk automatically.
Run it:
starling open
The header shows a room code like ▀▄ BIRD-00CCFF-00CCFF-... with colored
half-block swatches. Share it with another bird — they join with:
starling join BIRD-00CCFF-00CCFF-...
Roosts (headless servers)
A roost is a persistent bird that stays online to keep a community's chat history and channels for late-joiners — like your own Discord server, except it runs on your own machine over the peer-to-peer murmuration.
Roost commands come from the server component
(starling-server),
which you add with starling install server. You then run them through the
same starling command:
starling install server # one-time, adds the roost commands
starling roost create my-community # mints identity + database, prints invite code
starling roost open my-community # stays online until Ctrl+C
From the TUI, you join a roost exactly like any other flock — with its invite code:
starling join BIRD-...
Once joined, the roost's channels appear in the flock rail. See the Starling-Server README for the full roost command reference.
Developing? You can also clone and run from source:
git clone https://forgejo.hearthhome.lol/Saltfault/Starling-TUI.git cd Starling-TUI cargo run -- open
Command reference
Everything runs through one command: starling (installed from the
main repo, then
starling install tui). Roost commands require starling install server.
| Command | Status | Does |
|---|---|---|
starling open |
✅ | Launch the TUI. (Bare starling prints help — use starling open) |
starling join <code> |
✅ | Launch and join a flock or roost by invite code |
starling profile |
✅ | Run the profile + audio + dependency wizard |
starling leave <code> |
✅ | Print how to leave (in the TUI, just close with Esc) |
starling list |
✅ | List roosts stored on disk |
starling doctor |
✅ | Diagnose config, identity, profile, and dependencies |
starling logs |
✅ | Print the log file location |
starling install tui |
✅ | Install (or reinstall) the terminal client |
starling update tui |
✅ | Update the client to the latest version |
starling tui uninstall |
✅ | Uninstall the client and remove its config |
starling help |
✅ | Print usage |
Inside the running app, joining more flocks, switching between them, calls, and video are all keybindings — see Keybindings below.
Platform setup
Before installing Starling, you need Rust and a C compiler. Follow the
section for your platform. Then run starling profile to configure your
profile, audio devices, and any platform-specific dependencies.
Windows
1. Install Visual Studio C++ Build Tools (provides the MSVC compiler):
Download from visualstudio.microsoft.com. In the installer, select "Desktop development with C++".
2. Install Rust:
Download and run rustup-init.exe from the
Rust website. This installs rustc, cargo, and everything needed to
build Rust projects.
3. Install Starling:
cargo install --git https://forgejo.hearthhome.lol/Saltfault/Starling.git
starling install tui
Pre-built binaries (no Rust or compiler required) are planned — see the releases page for available downloads.
4. Run:
starling open
Audio uses WASAPI (Windows Audio Session API) — works out of the box, no extra audio packages needed. Video uses Windows Media Foundation via nokhwa.
macOS
1. Install Xcode Command Line Tools (provides the C compiler):
xcode-select --install
2. Install Homebrew if you don't have it:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
3. Install Rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
4. Install Starling:
cargo install --git https://forgejo.hearthhome.lol/Saltfault/Starling.git
starling install tui
5. Run:
starling open
Audio uses CoreAudio — works out of the box, no extra audio packages needed. Video uses AVFoundation via nokhwa.
Linux (native)
1. Install system dependencies:
# Debian / Ubuntu
sudo apt install build-essential pkg-config libasound2-dev libpulse-dev libclang-dev
# Fedora
sudo dnf install gcc pkgconf-pkg-config alsa-lib-devel pulseaudio-libs-devel clang-devel
# Arch Linux
sudo pacman -S base-devel pkgconf alsa-lib pulseaudio clang
2. Install Rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
3. Install Starling:
cargo install --git https://forgejo.hearthhome.lol/Saltfault/Starling.git
starling install tui
4. Run:
starling open
Audio uses PulseAudio (with ALSA fallback) — works out of the box on most Linux desktops. Video uses V4L2 via nokhwa.
| Package | Why it's needed |
|---|---|
build-essential / base-devel |
C compiler (gcc) for native code |
pkg-config |
Locating ALSA and PulseAudio libraries at build time |
libasound2-dev |
ALSA headers — cpal compiles the ALSA backend on Linux |
libpulse-dev |
PulseAudio headers — cpal's preferred backend at runtime |
libclang-dev |
Required by nokhwa for V4L2 webcam support |
WSL2 (Windows Subsystem for Linux)
WSL2 setup is identical to Linux, with one extra step for audio.
1. Install WSL2 (if not already installed, from PowerShell):
wsl --install
2. Inside WSL, install system dependencies:
sudo apt update
sudo apt install build-essential pkg-config libasound2-dev libpulse-dev libclang-dev
3. Install Rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
4. Install Starling:
cargo install --git https://forgejo.hearthhome.lol/Saltfault/Starling.git
starling install tui
5. Run profile (configures the WSL2 audio bridge):
starling profile
starling profile installs libasound2-plugins and writes
/etc/asound.conf to route ALSA through PulseAudio. This is needed because
the pure-Rust PulseAudio crate that cpal uses can't authenticate with WSLg's
server, but the C library (libpulse) that ALSA's pulse plugin uses can.
If you skip this step, text chat works but voice calls won't.
6. Run:
starling open
If you're on an older Windows 10 build without WSLg, audio won't work in WSL2 — use a native Windows build instead.
Webcam on WSL2: Webcams are not exposed by default. To use video calls from WSL2, set up USB passthrough (run in Windows PowerShell as Admin):
winget install usbipd
usbipd list # find your camera's BUSID
usbipd bind --busid <BUSID>
usbipd attach --wsl --busid <BUSID>
Then in WSL2:
sudo apt install linux-tools-generic usbip hwdata
sudo update-usbids
ls /dev/video* # should show your camera
Alternatively, use a native Windows build for video calls.
Running Starling
Start a new flock
starling open
The app starts and the header shows a room code with color swatches:
▀▄ ▀▄ ▀▄ ▀▄ ▀▄ ▀▄ ▀▄ ▀▄ ▀▄ ▀▄ ▀▄ BIRD-00CCFF-00CCFF-00CCFF-00CCFF-00CCFF-00CCFF-00CCFF-00CCFF-00CCFF-00CCFF-00CCFF
Share this code with another bird so they can join your flock.
Join an existing flock
starling join BIRD-00CCFF-00CCFF-...
Join multiple flocks
Once inside the app, you can join additional flocks at any time by clicking
the [Join] button or typing /join <code> in the message input and
pressing Enter. Join a roost via [Menu] → Join Roost, or with
/join-roost <code>. A rail appears on the left side of the screen showing
all joined flocks and roosts. Use Alt+↑ and
Alt+↓ to switch between them. Each flock has its own message list and
end-to-end encryption key.
── flocks ── ╔════════════════════════════════════╗
╔════════════╗ ║ BIRD-00CCFF-... . 3 birds ║
║> BIRD-… ║ ║ Alice: hello! ╔════════════════╗║
║ BIRD-… ║ ║ Bob: hi there ║ birds ║║
╚════════════╝ ╚═════════════════╩══════════════════╝
── roosts ──
╔════════════╗
║ my-chat ║
║ dev-room ║
╚════════════╝
You start in your home flock automatically. Joining a new flock does not leave the current one — you remain subscribed to all of them simultaneously.
Set your name
When you start Starling for the first time, a popup asks for your display
name — the name other birds see next to your messages in the flock. Type it
and press Enter to join the murmuration. You can change it later via the
Menu button ([Menu] → Profile) or by running starling profile.
Logs
Errors are written to logs/latest.log. On each launch, the previous log is
gzipped to logs/<timestamp>.log.gz. Check this file if something isn't
working.
Interface
Starling TUI is operated through on-screen buttons and the Menu popup. Buttons are clickable with the mouse. The bottom toolbar shows the main actions:
| Button | Action |
|---|---|
[Create] |
Create a new room (flock) |
[Join] |
Join a flock by invite code |
[Menu] |
Open the action menu |
[Quit] |
Exit Starling |
Keyboard shortcuts
| Key | Action |
|---|---|
Enter |
Send typed message (or /join <code> / /join-roost <code>) |
Alt+↑ |
Switch to previous flock/roost |
Alt+↓ |
Switch to next flock/roost |
Esc |
Open the action menu |
Backspace |
Delete last character |
Menu actions
Clicking [Menu] or pressing Esc opens the action menu with these options:
| Menu Item | Action |
|---|---|
| Create Room | Open a new flock |
| Join Flock | Join an existing flock by code |
| Join Roost | Join a roost by invite code |
| Create Roost | Create a new roost (runs starling roost create) |
| Invite | Show the current flock's invite code |
| Next Peer | Cycle selection to the next bird |
| Toggle Mute | Mute or unmute the microphone |
| Toggle Video | Show or hide the webcam feed |
| Call / Hang Up | Start a voice call or hang up |
| Profile | Exit and run the profile wizard (starling profile) |
| Quit | Exit Starling |
Use arrow keys (↑/↓) to navigate the menu and Enter to activate.
Click any item with the mouse to activate it directly. Click outside the
menu to close it.
Architecture
┌──────────────────────────────────────────────────────────────────┐
│ main.rs (UI loop) │
│ keyboard → Command ──┐ │
│ AppEvent ←───────────┤──── mpsc channels ────┐ │
│ playback ← VoiceFrame│ │ │
│ video_frame ← VideoFrame │ │
└────────────────────────┊────────────────────────┊───────────────┘
▼ ▼
┌──────────────────────────────────────────────────────────────────┐
│ net.rs (network task) │
│ gossip for chat · QUIC datagrams for voice │
│ QUIC uni streams for video · flock map (many flocks) │
│ mic capture (voice.rs) → call.rs │
│ webcam capture (video.rs) → call.rs │
│ sync.rs — history backfill for late joiners │
└──────────────────────────────────────────────────────────────────┘
The TUI is a pure client — it never runs a roost. The roost/ module
here holds only the protocol types (e.g. RoostState) the client reads
off a roost's control channel to render the channel rail. The actual roost
server lives in
starling-server.
Source layout
| File | Responsibility |
|---|---|
main.rs |
Event loop, keyboard handling, subcommand dispatch |
event.rs |
Command (UI→net) and AppEvent (net→UI) types |
net.rs |
Owns the iroh endpoint, flock map, voice/video handlers |
call.rs |
Opens/accepts QUIC streams for voice datagrams and video |
voice.rs |
Mic capture: cpal input → Opus encoder → channel |
playback.rs |
Audio output: channel → Opus decoder → ring buffer → cpal output |
video.rs |
Webcam capture: nokhwa → JPEG frames → channel, terminal rendering |
opus_ffi.rs |
Safe Rust wrappers around the pre-built Opus C library |
ui.rs |
Terminal rendering and UI state (App struct) |
setup.rs |
Setup wizard TUI for profile and device configuration |
config.rs |
Profile struct, disk persistence, 32-digit code, persistent identity key |
crypto.rs |
E2E encryption (ChaCha20-Poly1305) for gossip messages |
logger.rs |
File logger with gzipped log rotation |
util.rs |
Platform utilities (stderr suppression on Unix) |
sync.rs |
History backfill: asks a peer for recent messages on join |
roost/ |
Roost protocol types the client reads to render the channel rail (the server itself lives in starling-server) |
build.rs |
Downloads pre-built Opus static libraries from shiguredo/opus-rs |
How the murmuration works
Birds connect to the murmuration through iroh's global relay network and node discovery. No central server coordinates them:
- A bird opens a flock — their persistent node ID becomes the room code
(e.g.
BIRD-00CCFF-00CCFF-...). The gossip topic and E2E encryption key are both derived from this code via SHA-256. - Other birds join by entering the same room code — they subscribe to the same gossip topic and derive the same encryption key.
- iroh's relay connects both peers on the topic automatically. No node IDs or addresses need to be exchanged beyond the room code.
- Text messages broadcast over gossip reach all birds in the mesh.
- Voice calls are direct peer-to-peer QUIC datagram streams — no relay needed if direct connectivity is available, with relay fallback.
- Video calls use QUIC unidirectional streams carrying JPEG frames.
Audio is encoded as 48 kHz stereo Opus, 20 ms frames (960 samples per channel), sent as QUIC datagrams. Playback uses a 2-second ring buffer to absorb network jitter.
All text messages are end-to-end encrypted with ChaCha20-Poly1305 using a key derived from the room code. Each flock gets its own encryption key, so messages from different flocks are isolated cryptographically. Voice and video calls are E2E encrypted via iroh's QUIC TLS 1.3. Relays and intermediaries cannot read message content.
Persistent identity
Starling saves your node's secret key to ~/.config/starling/identity.key
on first launch. This means your room code stays the same every time you
open a flock — other birds can bookmark your code and rejoin later without
you needing to share a new one.
Troubleshooting
No microphone / no audio output (WSL2)
Run the one-time audio setup:
starling profile
This installs libasound2-plugins and writes /etc/asound.conf to route
ALSA through PulseAudio. See WSL2 setup
for details.
If it still doesn't work, verify PulseAudio is running:
ls /mnt/wslg/PulseServer # should exist
echo $PULSE_SERVER # should show unix:/mnt/wslg/PulseServer
If you don't have WSLg (older Windows 10), audio won't work in WSL2 — use a native Windows build instead.
link.exe not found (Windows)
You need the Visual Studio C++ Build Tools. Reinstall them and make sure "Desktop development with C++" is selected.
libclang not found (Linux / WSL2)
sudo apt install libclang-dev # Debian/Ubuntu
sudo dnf install clang-devel # Fedora
sudo pacman -S clang # Arch
This is required by nokhwa for webcam support.
No webcam detected (WSL2)
WSL2 doesn't expose USB webcams by default. Set up USB passthrough:
In Windows PowerShell (as Admin):
winget install usbipd
usbipd list # find your camera's BUSID
usbipd bind --busid <BUSID>
usbipd attach --wsl --busid <BUSID>
In WSL2:
sudo apt install linux-tools-generic usbip hwdata
sudo update-usbids
ls /dev/video* # should show your camera
Then re-run starling profile to detect the camera.
Build is slow on first compile
The Opus codec is downloaded as a pre-built static library on the first build. Subsequent builds are cached. Expect 2–5 minutes for the initial build.
Check the logs
Errors are written to logs/latest.log. On each launch, the previous log is
gzipped to logs/<timestamp>.log.gz.
License
Apache 2.0