Skip to main content

FenLiu (分流)

Created by marvin8 with assistance from Claude and DeepSeek AI assistants.

⚠️ DISCLAIMER / PROVISO: This project is a work in progress with major changes still happening. It is in no way anywhere close to finished and is only borderline useful for actual production use. Expect breaking changes, incomplete features, and significant architectural evolution as development continues.

Divide the Fediverse content flow

FenLiu is a web application that monitors Fediverse hashtags, filters spam, allows human review, learns from feedback, and exports quality content for boosting. Inspired by the ancient Chinese Dujiangyan irrigation system (256 BC) that separated silt from water, FenLiu applies 2,300-year engineering wisdom to modern digital content streams.

Current Status — v0.7.1

Fully functional spam filtering and content management system with complete Curated Queue integration, flexible pattern-based user blocking, automated queue lifecycle management, production-ready containerization, and ML training data collection.

Latest (v0.7.1): Random post selection for the Curated Queue API. 620 tests passing.

Documentation

📚 Live Docs: https://fenliu.marvin8.zone

The docs/ folder contains full MkDocs documentation covering installation, API reference, pattern blocking, Curated Queue integration, and more.

mkdocs serve   # serve locally with hot reload
mkdocs build   # build static site

Features

Core Functionality

  • Hashtag Monitoring: Monitor multiple Fediverse hashtags with customizable instance sources and scheduling
  • Spam Scoring: Rule-based detection (0-100 scale) with 7 intelligent detection rules
  • Manual Review Interface: Approve/reject posts with scoring; pagination, bulk actions, and back-to-top link
  • Curated Queue Export: Reliable API-driven queue with ack/nack/error pattern

Reblog Controls (Export Filters)

  • Pattern-Based User Blocking: exact, suffix, prefix, and contains matching modes
  • Hashtag Blocklist: Exclude posts containing blocked hashtags
  • Attachments-Only Mode: Export only posts with media attachments
  • Auto-Reject on Fetch: Automatically reject blocked content before review
  • Blocklist Refresh: Apply Settings changes to the review page instantly

Web Interface

  • Dashboard, Streams Management, Review Workflow, Pattern Blocking Settings, Queue Preview, Statistics
  • Responsive design — no external JavaScript dependencies

REST API

  • Hashtag streams, posts, curated queue, reblog controls, statistics, health
  • API key authentication for queue endpoints

Technical Quality

  • 620 tests, 100% passing
  • Comprehensive type hints; zero type errors under ty check
  • All functions pass ruff and complexipy checks
  • Alembic migrations run automatically on startup

Quick Start

Prerequisites: Python 3.12+, uv package manager

uv sync -U --all-groups
fenliu --reload --debug

Open http://localhost:8000, create a hashtag stream, fetch posts, and review them.

Container Deployment

podman build -t fenliu -f Containerfile .
cp .env.example .env   # edit with your settings
podman run -d -p 8000:8000 --env-file .env \
  -v fenliu-data:/app/data -v fenliu-logs:/app/logs fenliu

See the Container Deployment guide for full details.

API Endpoints

All curated queue endpoints require X-API-Key header (generate in Settings).

Streams & Posts:

  • GET /api/v1/streams — List streams
  • POST /api/v1/streams — Create stream
  • GET/PUT/DELETE /api/v1/streams/{id} — Stream operations
  • POST /api/v1/streams/{id}/fetch — Fetch posts for stream
  • POST /api/v1/streams/fetch-all — Fetch all active streams
  • GET /api/v1/posts — List posts with filtering
  • PATCH /api/v1/posts/{id} — Update post (review, approve, score)
  • GET /api/v1/stats — Application statistics

Curated Queue:

  • GET /api/v1/curated/next — Next post (204 if empty); ?random=true for random selection
  • POST /api/v1/curated/{post_id}/ack — Confirm successful reblog
  • POST /api/v1/curated/{post_id}/nack — Return to queue (transient failure)
  • POST /api/v1/curated/{post_id}/error — Mark permanently failed
  • POST /api/v1/curated/{post_id}/requeue — Return errored post to queue
  • POST /api/v1/curated/cleanup — Delete old delivered posts
  • POST /api/v1/curated/trim-pending — Trim excess pending posts

Reblog Controls:

  • GET/PUT /api/v1/reblog-controls/settings — Reblog filter settings
  • GET/POST /api/v1/reblog-controls/blocked-users — Blocked users (pattern-based)
  • DELETE /api/v1/reblog-controls/blocked-users/{id} — Remove blocked user
  • GET/POST /api/v1/reblog-controls/blocked-hashtags — Blocked hashtags
  • DELETE /api/v1/reblog-controls/blocked-hashtags/{id} — Remove blocked hashtag
  • POST /api/v1/reblog-controls/reject-blocked — Bulk reject matching posts

System: GET /health, GET /info

See the API docs for full reference.

Configuration

Key environment variables (see .env.example for full list):

Variable Default Description
DATABASE_URL sqlite:///./fenliu.db Database connection
SECRET_KEY (none — required) Session key; app refuses to start with placeholder
UI_AUTH_ENABLED true Web UI login (single user: admin). Set false only for personal/private-LAN deployments
DEFAULT_INSTANCE mastodon.social Default Fediverse instance
RESERVE_TIMEOUT_SECONDS 300 Queue reservation timeout
VERY_HIGH_THRESHOLD 76 Spam score: very high
LOW_MAX_THRESHOLD 25 Spam score: low
DEBUG false Enable debug logging

Development

uv run tryke test                # run tests (from repo root: uv run tryke test --root packages/fenliu)
uv run ruff check .              # lint
uv run complexipy .              # complexity check
uv run ty check                  # type check
nox                              # full CI simulation

alembic upgrade head             # apply migrations
alembic revision --autogenerate -m "description"  # new migration

Project Structure

fenliu/
├── src/fenliu/
│   ├── main.py                  # PyView application and LiveViews
│   ├── models.py                # SQLAlchemy models
│   ├── schemas.py               # Pydantic validation
│   ├── api/                     # REST API (curated, reblog_controls, api_keys)
│   ├── services/                # Business logic (spam scoring, fediverse, scheduler)
│   ├── templates/               # HTML templates
│   └── static/                  # CSS and assets
├── alembic/                     # Database migrations
├── tests/                       # Test suite (620 tests)
└── docs/                        # MkDocs documentation

Technical Stack

  • Framework: PyView (Starlette-based LiveView)
  • Database: SQLAlchemy + SQLite, Alembic migrations
  • Validation: Pydantic v2
  • Fediverse: minimal-activitypub
  • Frontend: Jinja2 + Tailwind CSS, no external JS
  • Testing: Tryke (620 tests)
  • Tooling: ruff, ty, complexipy, uv

What's New in v0.7.1

  • Random queue selection: GET /api/v1/curated/next?random=true returns a randomly chosen eligible post instead of the oldest. If the chosen author has more than one pending post, their oldest is returned to avoid consecutive same-author posts
  • 9 new tests for random selection behaviour; 572 total

Previous Release — v0.7.0

  • Review pagination: 20 posts/page with prev/next navigation
  • Bulk actions: Approve All / Reject All for the current page
  • Auto-refresh: Empty page reloads automatically when more posts exist
  • ML training snapshots: ReviewFeedback captures 12 feature fields at review time — survives queue cleanup
  • Bug fix: Stream deletion cascade fixed (Post → ReviewFeedback)

Previous Release — v0.6.0

  • Queue Lifecycle Management: Auto-delete delivered posts (7-day retention), trim excess pending with weighted random deletion, cleanup and trim-pending API endpoints
  • Production Containerization: Multi-stage build (~207 MB), non-root user, persistent volumes, automatic migrations on startup

Previous Release — v0.5.3

  • Pattern-Based User Blocking: exact, suffix, prefix, and contains matching modes — see PATTERN_BLOCKING_FEATURE.md
  • Blocklist Refresh: Apply Settings changes to the review page without losing progress

Cultural Context

"FenLiu" (分流) means "divide the flow" in Chinese, inspired by the Dujiangyan irrigation system (256 BC). This project applies the same engineering wisdom to digital content streams.

Key Resources

License

AGPL-3.0 — see LICENSE file.

Contributing

  1. Follow existing code style (ruff, comprehensive type hints)
  2. Write tests for new functionality
  3. Run nox before submitting changes
  4. Run alembic upgrade head after pulling changes with new migrations

v0.7.1 · Phase 4 In Progress · 620 tests ✅ · Forgejo

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fenliu-1.3.5.tar.gz (450.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fenliu-1.3.5-py3-none-any.whl (483.3 kB view details)

Uploaded Python 3

File details

Details for the file fenliu-1.3.5.tar.gz.

File metadata

  • Download URL: fenliu-1.3.5.tar.gz
  • Upload date:
  • Size: 450.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fenliu-1.3.5.tar.gz
Algorithm Hash digest
SHA256 7a4fc0e6ee424ee470802ef17634667089af65a1a0d74d9e2178129433258de6
MD5 a94663c06cb053a9f0bac15696b311d3
BLAKE2b-256 d607a35aaceaa4722bba165eed9a6db0a66bd8a42c06871867f8cb346e65039d

See more details on using hashes here.

File details

Details for the file fenliu-1.3.5-py3-none-any.whl.

File metadata

  • Download URL: fenliu-1.3.5-py3-none-any.whl
  • Upload date:
  • Size: 483.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fenliu-1.3.5-py3-none-any.whl
Algorithm Hash digest
SHA256 aee43bd5ccc502b3fcf856f47cfaacddeaa34d60956b6134cf6f9760e75d38a4
MD5 338e69f4ae0eb62b00c6a5333ac7b6c0
BLAKE2b-256 2219c5c064f2c0319b597ef5d1426553165bd0764920442311350a233b1f07b5

See more details on using hashes here.

Release history Release notifications | RSS feed

3.0.0

2 files

2.0.0

2 files

This release

1.3.5 This release

2 files

1.3.4

2 files

1.3.3

2 files

1.3.2

2 files

1.3.1

2 files

1.2.0

2 files

1.1.2

2 files

1.1.1

2 files

1.1.0

2 files

1.0.2

2 files

1.0.1

2 files

1.0.0

2 files

0.13.1

2 files

0.13.0

2 files

0.12.1

2 files

0.11.0

2 files

0.10.1

2 files

0.10.0

2 files

0.9.0

2 files

0.8.0

2 files

0.7.10

2 files

0.7.9

2 files

0.7.8

2 files

0.7.5

2 files

0.7.3

2 files

0.7.2

2 files

0.7.1

2 files

0.7.0

2 files

0.6.5

2 files

0.6.4

2 files

0.6.3

2 files

0.6.2

2 files

0.5.2

2 files

0.5.1

2 files

0.4.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page