A native Bluesky client for Linux, built with Rust, GTK4, and Libadwaita. Learn more at hangar.blue.
Technical Preview: Hangar is in early development. The foundations are being built feature by feature. Expect rough edges, missing functionality, and breaking changes.
Hangar is a desktop Bluesky client designed specifically for Linux and the GNOME desktop environment. No Electron, no web views, no hybrid UI—just native GTK4 with Libadwaita for a fast, integrated experience.
- Purpose-built: Built for Linux, feels at home in GNOME, with each feature being meticulously crafted
- Performance: Instant startup, smooth scrolling, efficient memory usage, optimized networking
- Full Bluesky support: Timeline, feeds, posts, interactions, notifications, and DMs
This is a technical preview. The app is functional but incomplete. Development follows a slice-by-slice methodology. One feature at a time, end-to-end.
- Authentication: OAuth (PKCE + DPoP) with persistent sessions, app password fallback
- Timeline: Home feed with infinite scroll, cursor-based pagination, pull-to-refresh
- Custom Feeds: Feed selector with Following, Discover, and pinned feeds
- Live Updates: Background polling with seamless new post insertion
- Rich Embeds: Image grids (1–4+), external link cards, video thumbnails, quote posts
- Interactions: Like/unlike, repost/unrepost, quote, reply
- Compose: Rich text highlighting (mentions, hashtags, URLs), mention autocomplete, image attachments (up to 4 with alt text), link card preview (Open Graph), language selection, per-post content warnings, interaction settings, thread composer
- Navigation: Home, Mentions, Activity, Chat, Profile, Likes, Search tabs with drill-down views
- Thread View: Full thread with parent posts and replies
- Profile View: Own profile with banner, bio, follower/following/post counts; drill-down profiles from clicking avatars
- Notifications: Mentions tab with filtered notifications; Activity tab with badge overlays and embedded post cards
- Chat: Conversation list with unread badges and last message preview
- Search: Search tab with results list and interactions
- Settings: Display (post text size, color scheme), Accessibility (reduce motion), Account (content safety link, clear cache)
- Caching: SQLite cache for posts, feeds, profiles, images, notifications with per-user isolation and automatic eviction
- Accessibility: Accessible roles and labels, keyboard shortcuts (F5/Ctrl+R), reduced motion support, focus ring visibility, theme variable usage, non-color state indicators
- Follow/unfollow
- Chat message thread view and sending
- Keyboard shortcuts for navigation and post actions
- Enhanced screen reader support (composite labels, live regions, focus management)
- Image lightbox, loading skeletons
- Full profile view on drill-down, profile editing
- Internationalization (i18n) and Flatpak distribution
- Bookmarks, moderation tools, desktop notifications, multi-account support
- Lots of polish
Hangar uses OAuth (PKCE + DPoP) for authentication. When you sign in, a browser window opens to complete the authorization flow via your Bluesky PDS. Sessions are persisted locally and automatically refreshed.
App password authentication is also supported as a fallback:
- Go to bsky.app/settings/app-passwords
- Create a new app password
- Use that password to log in to Hangar
App passwords can be revoked at any time without affecting your main account password.
OAuth sessions are stored in a local file-based session store. App password sessions are stored securely using libsecret (the GNOME keyring). If libsecret/D-Bus is unavailable, app password session persistence will fail gracefully and you'll need to log in each time.
Download the latest AppImage from the Releases page, make it executable, and run:
chmod +x Hangar-x86_64.AppImage
./Hangar-x86_64.AppImageThe AppImage supports delta updates via zsync and is also available through the AM AppImage package manager:
am -i hangarDownload the .flatpak bundle from the Releases page and install:
flatpak install hangar.flatpakFlathub submission is planned but not yet available.
Fedora/RHEL:
sudo dnf install gtk4-devel libadwaita-devel gcc pkg-configUbuntu/Debian:
sudo apt install libgtk-4-dev libadwaita-1-dev build-essential pkg-configArch:
sudo pacman -S gtk4 libadwaita base-develcargo build --release
cargo run --release┌─────────────────────────────────────────────┐
│ UI Layer │
│ GTK4 + Libadwaita widgets │
│ (window, sidebar, post_row, dialogs) │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Application Layer │
│ Orchestrates login, data fetching, │
│ navigation, state management │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ AT Protocol Layer │
│ HangarClient wraps atrium │
│ Converts atrium types → app types │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ External Crates │
│ atrium-api, reqwest, tokio │
└─────────────────────────────────────────────┘
GTK runs on the main thread. Network I/O runs on background threads with a shared Tokio runtime. Results are sent back to the main thread via glib::timeout_add_local polling or glib::spawn_future_local. A semaphore limits concurrent API requests to 4.
| Component | Technology |
|---|---|
| Language | Rust (2024 edition) |
| UI Toolkit | GTK4 + Libadwaita |
| AT Protocol | atrium-api + atrium-oauth |
| HTTP Client | reqwest (rustls-tls) |
| Async Runtime | Tokio |
| Cache | rusqlite (SQLite, bundled) |
| Secrets | secret-service (libsecret bindings) |
| Serialization | serde |
src/
├── main.rs # Entry point
├── app.rs # Application lifecycle, data fetching, navigation
├── config.rs # Constants (APP_ID, PDS URL)
├── runtime.rs # Shared Tokio runtime
├── atproto/
│ ├── client.rs # HangarClient — AT Protocol wrapper
│ ├── facets.rs # Rich text facet parsing (mentions, links, hashtags)
│ └── types.rs # Post, Profile, Session, Notification types
├── cache/
│ ├── db.rs # SQLite database setup
│ ├── schema.rs # Table definitions
│ ├── posts.rs # Post cache operations
│ ├── feeds.rs # Feed cache operations
│ └── profiles.rs # Profile cache operations
├── state/
│ ├── oauth.rs # OAuth flow (PKCE + DPoP, localhost callback)
│ ├── session.rs # App password session persistence via libsecret
│ ├── session_store.rs # File-based OAuth session store
│ └── settings.rs # AppSettings + FontSize (persistent JSON)
└── ui/
├── window.rs # Main window, stack navigation, settings page
├── sidebar.rs # Navigation rail with avatar menu
├── post_row.rs # Post widget with embeds and actions
├── compose_dialog.rs # Rich compose (posts, replies, quotes, threads)
├── login_dialog.rs # Sign-in dialog
├── avatar_cache.rs # Image loading with LRU + SQLite caching
└── style.css # Custom CSS styles
If you find Hangar useful, consider supporting its development:
Hangar is open source under the MPL-2.0 license. Contributions are welcome.
Before contributing:
- Run
cargo fmt(required) - Address
clippywarnings - Follow existing code patterns
- Keep changes focused—one feature per PR
Resources: