Your simple syndication (RSS) notifier for macOS
A lightweight menu bar app that notifies you when your favorite RSS feeds have new content and keeps them available until you dismiss them.
- π° Menu Bar App - Lives in your macOS menu bar, stays out of the way
- π Native Notifications - Get macOS notifications for new posts
- π Quick Access - Click notifications or history items to open posts in your browser
- βοΈ Easy Config - Simple JSON configuration for feeds
- π Auto-Check - Periodically checks feeds in the background
- πΎ Smart Tracking - Remembers what you've seen to avoid duplicate notifications
- π₯ Persistent Inbox - Keeps notifications until you dismiss them
- π§ History View - Maintains a local openable history of recent notifications
- π‘οΈ Safer Polling - Prevents overlapping checks and uses conditional HTTP requests when feeds support them
- π Standalone - Bundles into a native
.appwith no Python required
- Download
Syndi-vX.X.X-macos.zipfrom Releases - Unzip and drag
Syndi.appto your Applications folder - On first launch, right-click β Open (to bypass Gatekeeper since app is not signed)
- Configure your feeds in
~/.syndi/config.json
# Clone the repository
git clone https://github.com/eliezergh/Syndi.git
cd Syndi
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Run the app
python src/syndi.py# Install dependencies
pip install -r requirements.txt
# Build the app
cd setup
python setup.py py2app
# The app will be in setup/dist/Syndi.app
open dist/Syndi.appConfiguration is stored in ~/.syndi/config.json. On first run, a default config is created automatically.
{
"feeds": [
{
"name": "Hacker News",
"url": "https://news.ycombinator.com/rss",
"enabled": true
},
{
"name": "GitHub Blog",
"url": "https://github.blog/feed/",
"enabled": true
}
],
"check_interval_seconds": 300,
"request_timeout_seconds": 10,
"notification_enabled": true,
"show_preview": true,
"startup_silent_sync": true,
"max_recent_items": 50,
"max_stored_notifications": 500,
"max_menu_notifications": 12
}| Option | Description | Default |
|---|---|---|
feeds |
Array of RSS feeds to monitor | - |
feeds[].name |
Display name for the feed | - |
feeds[].url |
RSS/Atom feed URL | - |
feeds[].enabled |
Set to false to disable a feed |
true |
check_interval_seconds |
How often to check feeds (in seconds) | 300 (5 min) |
request_timeout_seconds |
Timeout for each feed request | 10 |
notification_enabled |
Enable/disable notifications | true |
show_preview |
Show article preview in notifications | true |
startup_silent_sync |
Prime a feed silently on first successful fetch | true |
max_recent_items |
Number of items shown in history | 50 |
max_stored_notifications |
Maximum number of stored notifications before old dismissed items are trimmed | 500 |
max_menu_notifications |
Number of active notifications shown in the Notifications menu | 12 |
Click the menu bar icon to access:
- Check now - Manually check all feeds immediately
- Status / Last check - Shows whether a check is running and when the last one started
- Notifications - Active notifications with per-item open, mark-as-read, and dismiss actions
- History - Quick access to recently detected items
- Feeds - Shows feed health and whether each feed has synced successfully
- Options
- Preferences⦠- Update polling, notification, and display settings in-app
- Test notification - Send a test notification
- Open config - Edit config.json
- Open log - Open the local log file
- Reload config - Reload configuration without restarting
- Dismiss all notifications - Clear the active inbox without losing history
- Clear seen data - Reset all seen items, notifications, and feed sync state
- About Syndi - View app information
- Quit Syndi - Exit the application
Syndi stores its data in ~/.syndi/:
~/.syndi/
βββ config.json # Your feed configuration
βββ data.json # Seen items, inbox/history, and feed sync metadata
βββ syndi.log # Runtime log for feed and notification errors
Syndi/
βββ .github/
β βββ workflows/
β βββ tests.yml # Lint, format, test, and coverage CI
β βββ build-release.yml # Release build and publishing workflow
β βββ codeql.yml # CodeQL security analysis
βββ src/
β βββ syndi.py # Main macOS menu bar application
β βββ core.py # Feed polling, config, and inbox business logic
β βββ preferences.py # Native preferences dialog
β βββ config.json # Default configuration
β βββ media/
β βββ menubar_icon.png # Menu bar icon
β βββ syndi.icns # App icon
βββ setup/
β βββ setup.py # py2app build script
βββ tests/ # Unit tests
βββ requirements.txt # Runtime/build dependencies
βββ requirements-dev.txt # Test and lint tooling
βββ pyproject.toml # Ruff configuration
βββ README.md
βββ DEVELOPER_GUIDE.md
For contributor setup, linting, formatting, test commands, and contribution expectations, see DEVELOPER_GUIDE.md.
Right-click the app β Open β Open (this bypasses Gatekeeper for unsigned apps)
- Check System Settings β Notifications β Syndi is allowed
- Verify
notification_enabled: truein config.json - Remember that
startup_silent_sync: truesuppresses notifications on the first successful fetch for a feed
- Click "Check now" to manually trigger
- Check feed URLs are valid RSS/Atom feeds
- Open
~/.syndi/syndi.logto inspect feed errors - Run from source to see error messages:
python src/syndi.py
rm -rf setup/build setup/dist
cd setup && python setup.py py2app- rumps - Menu bar app framework
- feedparser - RSS/Atom feed parsing
- requests - HTTP requests
- py2app - macOS app bundling
MIT License - see LICENSE for details.
Built with β€οΈ (and AI) by EliezerGH