PiFeed

README.md at tip
Login

File README.md from the latest check-in


PiFeed

PiFeed is a single-file microblogging server and RSS aggregator built with Python, Bottle, and SQLite. The entire application (routes, templates, static rendering, database models, and background feed processing) lives in pifeed.py.

Features

Architecture and Runtime Modes

PiFeed stores everything in a local SQLite database (pifeed.db) and local directories for file uploads and static cache.

It supports two operational modes:

  1. Standalone Server: Run directly from the terminal or managed by systemd/supervisor. In server mode, PiFeed uses a multithreaded WSGI server and starts an internal background thread that refreshes external feeds every 15 minutes.
  2. CGI Application: Run behind Apache, lighttpd, or BusyBox httpd. When GATEWAY_INTERFACE is present in the environment, PiFeed processes the incoming request and exits immediately. Feed fetches in CGI mode are scheduled via cron.

Building

PiFeed can run straight from pifeed.py with dependencies installed, or packaged into a deployment artifact using ./deps.sh.

Prerequisites: Python 3 and uv.

Run ./deps.sh to run tests and compile artifacts:

./deps.sh          # Runs audit, check, test, zipapp, and nuitka

Option 1: Python Zipapp (pifeed.pyz)

A standalone zipapp bundles application code and locked dependencies into an executable zip archive that runs on any system with a compatible Python 3 interpreter:

./deps.sh zipapp

This generates pifeed.pyz.

Option 2: Native Compiled Binary (pifeed.bin)

Using Nuitka, PiFeed compiles into a self-contained ELF executable with an embedded Python interpreter. This eliminates interpreter startup overhead, making it well-suited for CGI environments:

./deps.sh nuitka

This generates pifeed.bin. Compile on the same OS family and architecture as your deployment server.

Deployment

Standalone Server

To run directly on port 3141:

# Using source
python3 pifeed.py

# Using the zipapp
python3 pifeed.pyz

# Using the compiled binary
./pifeed.bin

When started for the first time, PiFeed creates pifeed.db, initializes tables, generates an admin user, and outputs a one-time password to standard error:

*** BOOTSTRAP: Admin user created. Username: 'admin' | Password: '<generated-password>' ***

To run behind a reverse proxy like Nginx or Caddy, proxy traffic to http://127.0.0.1:3141.

CGI Application (Apache)

CGI mode hosts PiFeed on shared hosting or Apache without running a persistent daemon process.

Installation

Copy either pifeed.pyz or pifeed.bin to your web root, rename it to index.cgi, and grant execution permissions:

cp pifeed.bin /var/www/html/index.cgi
chmod +x /var/www/html/index.cgi

Web Server Configuration (.htaccess)

Place an .htaccess file in your document root to route requests into index.cgi, protect the database file, and optionally serve static cache files directly:

Options +ExecCGI -Indexes
AddHandler cgi-script .cgi
DirectoryIndex index.cgi

RewriteEngine On
RewriteBase /

# Static cache shortcuts (serve public feeds directly from disk if cached)
RewriteCond %{HTTP_COOKIE} !session= [NC]
RewriteCond %{QUERY_STRING} ^$
RewriteCond %{DOCUMENT_ROOT}/cache/public.html -f
RewriteRule ^public/?$ cache/public.html [L]

RewriteCond %{HTTP_COOKIE} !session= [NC]
RewriteCond %{QUERY_STRING} ^$
RewriteCond %{DOCUMENT_ROOT}/cache/public.xml -f
RewriteRule ^public\.xml$ cache/public.xml [L]

# Route all non-file requests into index.cgi
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.cgi/$1 [L,QSA]

# Block direct web downloads of the SQLite database
<Files "pifeed.db">
    Require all denied
</Files>

<Files "index.cgi">
    Require all granted
</Files>

Scheduled Feed Polling (Cron)

Because CGI processes terminate after responding, background threads do not run. Schedule external feed fetching and cache culling every 15 minutes using cron:

*/15 * * * * /var/www/html/index.cgi --task-fetch >/dev/null 2>&1

Subdirectory Hosting

When served from a subdirectory (such as /cgi-bin/pifeed.cgi), PiFeed detects SCRIPT_NAME and rewrites links and redirects automatically. To override detection for reverse-proxy setups, set the PIFEED_BASE_PATH environment variable:

export PIFEED_BASE_PATH=/my-prefix

Configuration

PiFeed is configured through environment variables and the web admin UI:

Environment Variables

Variable Default Purpose
PORT 3141 Server listen port
PIFEED_DB ./pifeed.db SQLite database file location
PIFEED_UPLOADS ./uploads Directory for image, PDF, and HTML uploads
PIFEED_CACHE ./cache Directory for generated static feed caches
PIFEED_PODCASTS ./podcasts Directory for downloaded podcast episodes
PIFEED_BASE_PATH (detected) URL prefix override when running behind a prefix proxy
PIFEED_ALLOW_LOCAL_REQS 0 Set to 1 to allow RSS fetching from private or loopback IP ranges

Admin Interface

Log in as admin and visit /admin to configure instance settings: - Toggle public timelines, circles, and RSS aggregation. - Adjust session durations and feed retention culling rules. - Set instance-wide themes and visitor Message of the Day (MOTD). - Generate, copy, regenerate, or delete private registration invite links. - Reset user passwords and rotate RSS security tokens.

If you ever lose the admin password, run:

python3 pifeed.py --reset-admin

This outputs a new temporary password directly to the console.

Development and Testing

Run unit and integration tests using pytest:

pytest test_pifeed.py pifeed_tests.py

Tests run against isolated temporary SQLite databases and mock upload directories without modifying your production data.