Analyze accounts/statuses and file reports via API so human moderators act in Mastodon's admin UI. No auto-enforcement.
- ✅ Error Handling: Comprehensive API error responses with structured logging and request IDs
- ✅ Health Checks: Robust health monitoring with proper HTTP status codes (503 for service unavailability)
- ✅ Security: Webhook signature validation, API key authentication, and security scanning
- ✅ Database: Foreign keys, performance indexes, reliable migrations
- ✅ Monitoring: Prometheus metrics, structured JSON logging, and detailed analytics
- ✅ Frontend: settings interface with error states and real-time configuration
- ✅ Testing: Comprehensive edge case test coverage (22 test scenarios)
- ✅ CI/CD: Automated testing, static analysis, and code formatting
- ✅ API Compliance: Automated validation against mastodon.py library (see docs/api-compliance-checker.md)
- ✅ Audit Logs: Enforcement actions recorded with rule context and API responses
- ✅ User Notifications: Warnings and suspensions include messages sent through Mastodon
- ✅ Media-aware Scanning: Analyzes attachments for alt text, MIME types, and URL hashes
- ✅ Context-aware Status Analysis: Each new status is checked alongside the account's latest public posts. Unlisted posts don't affect rate rules but are still scanned for shady links.
To get a working stack, run:
docker compose upFor local development use the setup script. It launches a mock Mastodon server unless .env.local is present.
./scripts/setup-dev.sh📖 For complete environment variable documentation, see: docs/ENVIRONMENT.md
- Copy the environment template:
cp .env.example .env - Edit
.envwith your actual values - For production: Set required tokens and secrets
- For development: The override file provides safe defaults
INSTANCE_BASE: your Mastodon instance base URLMASTODON_CLIENT_SECRET: token withwrite:reportsscopeMASTODON_CLIENT_SECRET: token with admin read scopesAPI_KEY: secure random string for API authenticationWEBHOOK_SECRET: secure random string for webhook signature validationDATABASE_URL: PostgreSQL connection stringREDIS_URL: Redis connection stringUSER_AGENT: user agent for Mastodon requests (default:MastoWatch/<VERSION> (+moderation-sidecar))HTTP_TIMEOUT: seconds before Mastodon requests time out (default:30)VERSION: application version (default:0.1.0)SKIP_STARTUP_VALIDATION:trueto skip startup checks (for testing only)UI_ORIGIN: origin for the dashboard UIMIN_MASTODON_VERSION: minimum supported Mastodon version (default:4.0.0)POLL_ADMIN_ACCOUNTS_INTERVAL: seconds between remote admin polls (default:30)POLL_ADMIN_ACCOUNTS_LOCAL_INTERVAL: seconds between local admin polls (default:30)QUEUE_STATS_INTERVAL: seconds between queue metrics snapshots (default:15)VITE_API_URL: API base URL for the frontend
To send notifications to Slack, set SLACK_WEBHOOKS to a JSON object mapping event names to webhook URLs.
This application uses direct access tokens rather than OAuth2 client credentials. You need to create two applications in your Mastodon instance:
- Go to your Mastodon instance → Settings → Development
- Click "New Application"
- Configure:
- Application name:
MastoWatch Admin - Scopes: Select
admin:readandadmin:write
- Application name:
- Click "Submit"
- Copy the "Your access token" → use as
MASTODON_CLIENT_SECRETin.env
- Create another new application
- Configure:
- Application name:
MastoWatch Bot - Scopes: Select
readandwrite:reports
- Application name:
- Click "Submit"
- Copy the "Your access token" → use as
MASTODON_CLIENT_SECRETin.env
Note: You only need the access tokens, not the client key/secret shown in the application details.
- Create a third application for OAuth login
- Configure:
- Application name:
MastoWatch OAuth - Scopes: Select
read:accounts(for user verification) - Redirect URI: Your callback URL (e.g.,
https://your.domain/admin/callback)
- Application name:
- Click "Submit"
- Copy the "Client key" → use as
OAUTH_CLIENT_IDin.env - Copy the "Client secret" → use as
OAUTH_CLIENT_SECRETin.env
MastoWatch uses the official mastodon.py library for all Mastodon API operations:
- Official library: Community-maintained with full Mastodon API support
- Built-in features: Rate limiting, pagination, error handling
- Synchronous architecture: Direct synchronous calls, FastAPI handles concurrency via threadpool
- Centralized access: All API calls go through
MastodonServicewrapper
See docs/mastodon-py-integration.md for complete integration details.
GET /healthz- Health check with service status (returns 503 if services unavailable)GET /metrics- Prometheus metrics for monitoring
GET /config- Return non-sensitive configuration detailsPOST /config/dry_run?enable=true|false- Toggle dry run modePOST /config/panic_stop?enable=true|false- Emergency stop all processing
GET /rules- List all rulesPOST /rules- Create a rulePUT /rules/{id}- Update a ruleDELETE /rules/{id}- Delete a rulePOST /rules/{id}/toggle- Enable or disable a rule
Rules may combine two patterns using boolean_operator (AND or OR) with a secondary_pattern.
GET /analytics/overview- System analytics overview with account/report metricsGET /analytics/timeline?days=N- Timeline analytics for the past N days (1-365)GET /logs- Enforcement audit log entries
GET /admin/login- Initiate OAuth login flow for admin accessGET /admin/callback- OAuth callback handlerPOST /admin/logout- Clear admin sessionGET /api/v1/me- Get current user information
POST /dryrun/evaluate- Test rule evaluation (body:{"account": {...}, "statuses": [...]}) returns{"score": float, "hits": [[rule, weight, evidence], ...]}
POST /webhooks/status- Webhook endpoint for Mastodon status updates (requires signature validation)
All API endpoints return structured error responses with:
- Request IDs for tracing and debugging
- Detailed error messages with context
- Proper HTTP status codes (400/401/404/422/500/503)
- Structured logging with JSON format for monitoring
MastoWatch uses RQ (Redis Queue) for background job processing:
- Simple: Jobs are plain Python functions - no decorators needed
- Observable: Built-in web dashboard and REST API for monitoring
- Synchronous: Matches the synchronous nature of mastodon.py
- Recurring jobs: RQ Scheduler handles cron-like scheduling
- Dashboard: Access at
http://localhost:9181in development
See docs/rq-migration.md for complete job system documentation.
- RQ Scheduler manages recurring jobs (poll_admin_accounts, queue_stats, etc.) with configurable intervals via environment variables.
- All endpoints use structured JSON logging with request IDs for troubleshooting.
- Alembic migrations run via the
migrateservice. Note thatalembic.inileavessqlalchemy.urlempty; theDATABASE_URLenvironment variable is used instead. - Add Prometheus to scrape
/metricsas desired. - Foreign keys ensure data integrity; performance indexes optimize common queries.
- RQ Dashboard provides real-time monitoring of job queues, status, and history.
Every page shows a footer linking to goingdark.social. The app refuses to run without it. Don't remove or rename this credit; it's part of the license.