This is the official repository for the service www.analytodon.com.
Analytodon provides analytics and insights for Mastodon accounts, helping users understand their engagement and growth on the Fediverse.
π€ Contributions are welcome.
π Self-hosting is explicitly allowed.
This project is a monorepo using pnpm workspaces containing:
- π Backend: A NestJS application providing the API services (
apps/backend) - π» Frontend: A Remix application for the user interface (
apps/frontend) - π οΈ CLI: Command-line tools for data management and maintenance (
apps/cli) - π REST Client: Auto-generated TypeScript client for the API (
packages/rest-client)
- π¦ Node.js (v20+)
- ποΈ MongoDB database
- π pnpm v10.5.2 or later (for package management)
- Clone the repository:
git clone https://github.com/blazer82/analytodon.git
cd analytodon- Install dependencies:
pnpm install- Start local services:
pnpm run docker:upThis starts:
- MongoDB on
localhost:27017 - Mailpit SMTP on
localhost:1025, Web UI at http://localhost:8025
-
Configure environment variables:
- Copy
.env.exampleto.envinapps/backend,apps/frontend, andapps/cli - The example files ship with working defaults for local development
- Copy
-
Seed the development database (optional):
pnpm run db:seedThis creates test users with 90 days of realistic analytics data:
dev@analytodon.local/password(account-owner with connected Mastodon account)admin@analytodon.local/password(admin)
To reset and re-seed: pnpm run db:seed:reset
- Start the development servers:
# Start both frontend and backend in development mode
pnpm run dev
# Or start them individually
pnpm --filter @analytodon/backend run start:dev
pnpm --filter @analytodon/frontend run devThe monorepo includes several useful scripts:
# Start local services (MongoDB, Mailpit)
pnpm run docker:up
# Stop local services
pnpm run docker:down
# Seed the dev database with test data
pnpm run db:seed
# Reset and re-seed the dev database
pnpm run db:seed:reset
# Build all applications
pnpm run build
# Run linting across all packages
pnpm run lint
# Run tests across all packages
pnpm run test
# Check code formatting
pnpm run prettier:check
# Fix code formatting
pnpm run prettier:write
# Generate API client from OpenAPI spec
pnpm run codegenThe backend provides RESTful APIs for account management, authentication, and analytics processing.
# Run backend tests
pnpm --filter @analytodon/backend run test
# Run backend in production mode
pnpm --filter @analytodon/backend run start:prodThe frontend provides the user interface for interacting with Analytodon.
# Build the frontend for production
pnpm --filter @analytodon/frontend run build
# Start the frontend in production mode
pnpm --filter @analytodon/frontend run startThe CLI provides command-line tools for data management, maintenance, and automation tasks.
# Build the CLI
pnpm --filter @analytodon/cli run build
# Run a CLI command
pnpm --filter @analytodon/cli run analytodon-cli [command]
# See available commands
pnpm --filter @analytodon/cli run analytodon-cli helpAnalytodon runs as three Docker containers -- a backend API (NestJS), a frontend web app (Remix), and a CLI cron worker -- plus a MongoDB database. The recommended way to self-host is with Docker Compose.
- Docker and Docker Compose
- A reverse proxy (Caddy, nginx, or Traefik) for TLS termination
- An SMTP server or transactional email service (for verification and notification emails)
- A domain name with DNS pointing to your server
Generate three secrets before starting. The ENCRYPTION_KEY encrypts Mastodon OAuth tokens stored in the database -- it must be identical in the backend and CLI containers.
# ENCRYPTION_KEY β 64-character hex string (32 bytes for AES-256)
openssl rand -hex 32
# JWT_SECRET β used by the backend to sign authentication tokens
openssl rand -base64 48
# SESSION_SECRET β used by the frontend to encrypt session cookies
openssl rand -base64 48Warning: Never change
ENCRYPTION_KEYafter initial setup -- existing Mastodon tokens would become undecryptable.
Create a docker-compose.prod.yml in the repository root:
services:
mongodb:
image: mongo:8
restart: unless-stopped
environment:
MONGO_INITDB_ROOT_USERNAME: analytodon
MONGO_INITDB_ROOT_PASSWORD: <db-password>
volumes:
- mongodb_data:/data/db
backend:
build:
context: .
dockerfile: deploy/docker/backend.Dockerfile
restart: unless-stopped
ports:
- "127.0.0.1:3001:3000"
depends_on:
- mongodb
environment:
DB_CLIENT_URL: mongodb://analytodon:<db-password>@mongodb:27017/analytodon?authSource=admin
ENCRYPTION_KEY: <your-encryption-key>
JWT_SECRET: <your-jwt-secret>
JWT_EXPIRES_IN: 1h
JWT_REFRESH_TOKEN_EXPIRES_IN: 7d
FRONTEND_URL: https://<your-domain>
MASTODON_APP_NAME: Analytodon
MARKETING_URL: https://<your-domain>
EMAIL_HOST: <smtp-host>
EMAIL_PORT: "587"
EMAIL_USER: <smtp-user>
EMAIL_PASS: <smtp-password>
EMAIL_SECURE: "false"
EMAIL_FROM_NAME: Analytodon
EMAIL_FROM_ADDRESS: <noreply@your-domain>
frontend:
build:
context: .
dockerfile: deploy/docker/frontend.Dockerfile
restart: unless-stopped
ports:
- "127.0.0.1:3002:3000"
depends_on:
- backend
environment:
API_URL: http://backend:3000
SESSION_SECRET: <your-session-secret>
MARKETING_URL: https://<your-domain>
SUPPORT_EMAIL: <your-email>
cli:
build:
context: .
dockerfile: deploy/docker/cli.Dockerfile
restart: unless-stopped
depends_on:
- mongodb
environment:
MONGODB_URI: mongodb://analytodon:<db-password>@mongodb:27017/analytodon?authSource=admin
MONGODB_DATABASE: analytodon
ENCRYPTION_KEY: <your-encryption-key>
APP_URL: https://<your-domain>
LOG_LEVEL: info
volumes:
mongodb_data:A few notes:
API_URL(frontend) uses Docker-internal networking -- the frontend calls the backend server-side, never from the browser- Backend and frontend bind to
127.0.0.1so they are only reachable through the reverse proxy - The CLI container runs a cron daemon in the foreground -- it handles all scheduled data fetching, aggregation, emails, and cleanup automatically
- Set
EMAIL_SECUREto"true"for port 465 (implicit TLS) or"false"for port 587 (STARTTLS)
Build and start:
docker compose -f docker-compose.prod.yml up -d --buildOnly the frontend needs to be publicly accessible. The backend API is called server-side by the frontend (Remix SSR) and by the CLI container, both over Docker's internal network.
Example with Caddy (automatic HTTPS):
your-domain.com {
reverse_proxy localhost:3002
}
nginx, Traefik, or any other reverse proxy works equally well -- just proxy all traffic to the frontend on port 3002.
- Start the stack:
docker compose -f docker-compose.prod.yml up -d --build - Verify all containers are running:
docker compose -f docker-compose.prod.yml ps - Open
https://your-domain.comand create an account - Connect a Mastodon account -- the CLI will begin fetching initial stats within one minute
Initial data population can take several hours depending on the account's history. Hourly cron jobs then keep data up to date.
Optional settings you may want to adjust:
| Variable | Where | Description |
|---|---|---|
DISABLE_NEW_REGISTRATIONS |
backend + frontend | Set to true to close signups after creating your account |
MASTODON_APP_NAME |
backend | Name shown to users during Mastodon OAuth (default: Analytodon) |
LOG_LEVEL |
cli | Logging verbosity: debug, info, warn, error (default: info) |
JWT_EXPIRES_IN |
backend | Access token lifetime (default: 1h) |
JWT_REFRESH_TOKEN_EXPIRES_IN |
backend | Refresh token lifetime (default: 7d) |
Pull the latest changes and rebuild:
git pull
docker compose -f docker-compose.prod.yml up -d --buildGPL-3.0-only