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
  • AI Classification: AI-powered text-promotion and image real-cat checks with review badges and filters (no auto-reject by default)
  • Manual Review Interface: Approve/reject posts with AI classification badges; 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)
  • 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
DEBUG false Enable debug logging
AI_API_BASE_URL https://api.moonshot.ai/v1 OpenAI-compatible AI API base URL
AI_API_KEY (none — required) AI API key
AI_TEXT_MODEL kimi-k2.6 Model for the text-promotion check
AI_VISION_MODEL kimi-k2.6 Model for the image real-cat check
AI_BATCH_HOUR 2 Local hour the nightly image batch runs

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-3.0.0.tar.gz (431.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-3.0.0-py3-none-any.whl (460.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: fenliu-3.0.0.tar.gz
  • Upload date:
  • Size: 431.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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-3.0.0.tar.gz
Algorithm Hash digest
SHA256 e9236744d82d09e5dfc8115479907164291f1e4502c4d9bac99e8dfc74d6145d
MD5 b5fddc1e7e50dfe9ff9c938e2f4d4805
BLAKE2b-256 b49596867938cccb8106be23e306c02f91c18e1d716de5cffc462dc52bbbe640

See more details on using hashes here.

File details

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

File metadata

  • Download URL: fenliu-3.0.0-py3-none-any.whl
  • Upload date:
  • Size: 460.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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-3.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f614b65e3681db42d0b33d49d74bbeba98de5e454287df567adebb99aca8569e
MD5 853a708c8b4f4be02267c241d74049a9
BLAKE2b-256 68c26cab0a936362d109df37a09e332985292213b68ce02c80bb28414a9bfd17

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

3.0.0 This release

2 files

2.0.0

2 files

1.3.5

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