quickheadlines v0.5.2
Quick Headlines
Quick Headlines is an easily configurable and deployable recent feed dashboard. It allows you to organize your favorite RSS/Atom feeds and software releases into tabs for a clean, categorized view.
I wanted it to be as simple as dropping an executable and a YAML file with feeds in it. The aim is to have sane defaults, so you can get up and running quickly without fighting with it. It works great as a local dashboard or a hosted service.
Features
- Tabbed Interface: Group feeds into logical categories (e.g., "Tech", "Dev", "News").
- Timeline View: Chronological view of all feed items with day grouping and time stamps.
- Software Release Tracking: Monitor releases from GitHub, GitLab, and Codeberg in a unified view.
- Adaptive UI: Automatically extracts colors from site favicons to style feed headers.
- Dark Mode: Built-in support with a toggle, including high-contrast scrollbars and scroll-indicators for Safari compatibility.
- Live Updates: Automatically refreshes feeds in the background and updates the UI without a page reload.
- Hybrid Clustering: Groups similar stories together using a two-pass algorithm (LSH + Jaccard similarity) with stop-word filtering for high precision.
- Clustering Status: Visual indicator in the UI shows when background clustering is active.
- Authentication: Support for Basic, Bearer token, and API Key authentication for private feeds.
- Configurable Caching: SQLite-based caching with configurable retention and size limits.
- Health Monitoring: Built-in health monitoring with CPU spike detection and error logging.
- Lightweight: Single binary deployment with embedded frontend assets (Svelte 5).
Deployment
Deployments only need the application executable and feeds.yml. The Svelte 5 frontend is embedded in the binary via BakedFileSystem - no separate frontend files needed.
Screenshots
| Mobile (Dark Mode) | Mobile (Light Mode) |
|---|---|
![]() |
![]() |
| Desktop (Dark Mode) | Desktop (Light Mode) |
|---|---|
![]() |
![]() |
13 Themes Available: QuickHeadlines now includes 11 custom themes beyond Light and Dark. View all themes →
Timeline View
| Mobile Timeline (Dark Mode) | Mobile Timeline (Light Mode) |
|---|---|
![]() |
![]() |
| Desktop Timeline (Dark Mode) | Desktop Timeline (Light Mode) |
|---|---|
![]() |
![]() |
Installation
Download the associated binary for your operating system from the Releases page. There are builds for Linux (arm64/amd64), FreeBSD (amd64), and macOS (arm64). You will also need to have the feeds.yml file in the same folder as the executable. Note for macOS users: You must have OpenSSL 3 installed (brew install openssl@3) to run the binary.
Building from Source
Prerequisites
- Crystal (>= 1.18.2)
- Node.js (>= 18) and pnpm - for building the Svelte 5 frontend
- SQLite3 development libraries
- OpenSSL development libraries
The frontend is built with Svelte 5 and embedded in the binary using BakedFileSystem. No separate deployment of frontend files is needed.
Platform-Specific Setup
Ubuntu / Debian
# Install Crystal compiler
curl -fsSL https://crystal-lang.org/install.sh | sudo bash
# Install system dependencies
sudo apt-get update
sudo apt-get install -y libsqlite3-dev libssl-dev pkg-config nodejs npm
# Install pnpm
npm install -g pnpm
# Clone and build
git clone https://github.com/kritoke/quickheadlines.git
cd quickheadlines
just build
Fedora / RHEL
# Install Crystal compiler
curl -fsSL https://crystal-lang.org/install.sh | sudo bash
# Install system dependencies
sudo dnf install -y sqlite-devel openssl-devel pkg-config nodejs npm
# Install pnpm
npm install -g pnpm
# Clone and build
git clone https://github.com/kritoke/quickheadlines.git
cd quickheadlines
just build
Arch Linux
# Install Crystal and dependencies
sudo pacman -S crystal sqlite openssl pkg-config nodejs npm
# Install pnpm
npm install -g pnpm
# Clone and build
git clone https://github.com/kritoke/quickheadlines.git
cd quickheadlines
just build
macOS
# Install Crystal and dependencies via Homebrew
brew install crystal openssl@3 node pnpm
# Clone and build
git clone https://github.com/kritoke/quickheadlines.git
cd quickheadlines
just build
FreeBSD
# Install Crystal and dependencies
pkg install crystal shards sqlite3 openssl node npm gmake
# Install pnpm
npm install -g pnpm
# Clone and build
git clone https://github.com/kritoke/quickheadlines.git
cd quickheadlines
gmake build
FreeBSD Jail Deployment (Bastille)
For automated FreeBSD jail deployment, the project includes:
- misc/Bastillefile - Bastille template for automated jail creation and configuration
- misc/quickheadlines - rc.d script for service management and supervision
These files set up the service user, cache directory at /var/cache/quickheadlines, and proper TLS certificates for feed fetching.
Build Commands
- Production Mode:
just build- Builds Svelte frontend and compiles optimized binary tobin/quickheadlines - Development Mode:
just run- Runs with development settings - Check Dependencies:
just check-deps- Verify all required dependencies are installed - Clean Build:
just clean && just build- Remove all build artifacts and rebuild - Run Tests:
cd frontend && npm run test- Run Vitest tests for Svelte components
Running the Application
# Run the compiled binary
./bin/quickheadlines
# Or use development mode
just run
The application will:
- Auto-download
feeds.ymlfrom GitHub if missing - Create SQLite cache database on first run
- Start listening on port 3030 on localhost unless you changed the port in the
feeds.ymlfile.
Cache Directory Configuration
QuickHeadlines stores feed data in an SQLite database for better performance. The cache directory location is determined by the following priority:
- Environment variable
QUICKHEADLINES_CACHE_DIR - Config file option
cache_dirinfeeds.yml - Platform-specific default:
- FreeBSD/Linux:
/var/cache/quickheadlines(if writable), otherwise XDG or~/.cache/quickheadlines - macOS:
~/Library/Caches/quickheadlines - Fallback:
./cachein the current directory
- FreeBSD/Linux:
Setting the Cache Directory
Via environment variable:
export QUICKHEADLINES_CACHE_DIR=/var/cache/quickheadlines
./quickheadlines
Via feeds.yml:
cache_dir: /var/cache/quickheadlines
For production/Docker/jails: Use /var/cache/quickheadlines and ensure the directory is writable by the application user. The included FreeBSD Bastille template sets this up automatically.
For development: The default ~/.cache/quickheadlines location works well for local development.
Cache Retention & Size Limits
QuickHeadlines automatically manages the SQLite cache database with configurable retention and size limits:
- Default retention: 336 hours (14 days) - feeds not fetched within this period are automatically cleaned up
- Warning threshold: 50MB - logs a warning when database exceeds this size
- Hard limit: 100MB - automatically removes oldest entries when database exceeds this size
- DB Fetch Limit: 500 items - controls how many items are fetched and stored per feed for "Load More" and clustering
Configuring Cache Retention
Add cache_retention_hours to your feeds.yml:
cache_retention_hours: 336 # 14 days (default)
# cache_retention_hours: 720 # 30 days
# cache_retention_hours: 24 # 1 day
The application will:
- Log database size on startup
- Clean up feeds older than the retention period on each refresh
- Automatically remove oldest entries if database exceeds 100MB
- Log cleanup actions with details
Note: The cache retention setting is backwards compatible - if not specified, it defaults to 336 hours (14 days).
Usage
Edit the feeds.yml file to add your own content. It only requires a feed title and URL; other properties have sane defaults.
Global Configuration Options
refresh_minutes: 10 # Refresh interval in minutes (default: 10)
item_limit: 10 # Default number of items per feed (default: 10)
server_port: 3030 # HTTP server port (default: 3030)
page_title: "Quick Headlines" # Page title (default: "Quick Headlines")
cache_retention_hours: 336 # Cache retention in hours (default: 336 = 14 days)
db_fetch_limit: 500 # Items to fetch/store for timeline/clustering (default: 500)
max_cache_size_mb: 100 # Max cache size in MB before auto-cleanup (default: 100)
debug: false # Enable verbose debug logging (default: false)
HTTP Client Configuration
Configure global HTTP client settings:
http_client:
timeout: 30 # Read timeout in seconds (default: 30)
connect_timeout: 10 # Connection timeout in seconds (default: 10)
user_agent: "QuickHeadlines/0.3" # Custom User-Agent header
Feed-Specific Configuration
Each feed can override global settings:
tabs:
- name: "Tech"
feeds:
- title: "Hacker News"
url: "https://news.ycombinator.com/rss"
header_color: "orange" # Header background color
item_limit: 20 # Override global item limit
max_retries: 5 # Retry attempts on failure (default: 3)
retry_delay: 3 # Base delay between retries in seconds (default: 5)
timeout: 45 # Request timeout in seconds (default: 30)
Authentication
Feeds can require authentication. Supported types: basic, bearer, apikey:
tabs:
- name: "Private"
feeds:
- title: "Internal Feed"
url: "https://example.com/private/feed.xml"
auth:
type: "basic" # Authentication type
username: "user" # Username for Basic auth
password: "pass" # Password for Basic auth
# OR for Bearer/API Key:
# type: "bearer"
# token: "your-token-here"
# header: "Authorization" # Custom header name (default)
# prefix: "Bearer " # Value prefix (default: "")
Software Releases
Monitor releases from GitHub, GitLab, and Codeberg:
software_releases:
title: "Software Releases" # optional, defaults to Software Releases
repos:
- "crystal-lang/crystal" # Defaults to GitHub
- "inkscape/inkscape:gl" # :gl for GitLab
- "supercell/luce:cb" # :cb for Codeberg
Docker Image
You should be able to use the following docker image ghcr.io/kritoke/quickheadlines:latest to get the latest package.
Performance & Memory Tuning
For long-running instances, especially in resource-constrained environments like Docker containers or FreeBSD jails, you can tune the Garbage Collector (Boehm GC) using environment variables to maintain a flat memory footprint:
GC_MARKERS=1: Limits the number of parallel markers. Recommended for systems with low CPU core counts to reduce thread overhead.GC_FREE_SPACE_DIVISOR=20: Makes the GC more aggressive about reclaiming memory and returning it to the OS. Increasing this value (default is ~3) helps prevent slow memory growth over time.
Setting Variables
Add them to your docker-compose.yml or docker run command (these are already made on the included docker/docker-compose files):
GC_MARKERS=1
GC_FREE_SPACE_DIVISOR=20
Contributing
- Fork it (https://github.com/kritoke/quickheadlines/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
Contributors
- kritoke - creator and maintainer
License
This project is licensed under the GNU Affero General Public License v3.0 - see the LICENSE file for details.
quickheadlines
- 2
- 0
- 0
- 0
- 7
- about 12 hours ago
- December 14, 2025
GNU Affero General Public License v3.0
Wed, 04 Mar 2026 19:41:53 GMT







