Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Arcadia is a comprehensive torrent platform designed to be:

  • Easy to setup - Get running quickly with minimal configuration
  • Highly configurable - Customize the platform to your needs
  • Content agnostic - Support for movies, TV shows, music, books, software, and more
  • Well organized - Clean, intuitive interface for both users and administrators

The backend is built with Rust for speed and safety. The frontend is built with Typescript and VueJS, rendered client-side.

What Arcadia Supports

Arcadia supports a wide variety of content types including:

Movies

  • Feature Film: Full-length films with rich metadata support.
  • Short Film: Shorter cinematic works.
Movie entry

Movie entry

TV Shows

  • Series and seasons with episode tracking capabilities.
TV Show - Season entry

TV Show - Season entry

TV Show - Series view

TV Show - Series view

Music

  • Album: Includes “live album” as an “edition.”
  • EP: Extended plays.
  • Single: Individual tracks.
  • Soundtrack: Music from movies, TV shows, or games.
  • Anthology: Collections of works by an artist or group.
  • Compilation: Curated collections of tracks.
  • Remix: Reimagined versions of original tracks.
  • Bootleg: Unofficial recordings.
  • Mixtape: Curated playlists or unofficial releases.
  • Concert Recording: Live performance recordings.
  • DJ Mix: Continuous mixes by DJs.
Music

Music

Software

  • Game: Video games of all genres.
  • Program: Applications and utilities.
Software - Game

Software - Game

Written Documents

  • Book: Includes hardcover, paperback, and digital formats.
  • Illustrated: Includes mangas, comics, and visual novels.
  • Periodical: Newspapers, magazines, and journals.
  • Article: Studies, theses, essays, and research papers.
  • Manual: Guides, music sheets, and instructional documents.
Book - Entry view

Book - Entry view

Book - Illustrated

Book - Illustrated

Book - Series view

Book - Series view

Collections

Collections represent a “grouping” of content to avoid multiple uploads and reduce tracker load. Examples include site dumps, full/finished series, and monthly/yearly content groupings.

Podcast collection

Podcast collection

Community

For contributions see CONTRIBUTING.md

Join our community:

Architecture

Overview

Arcadia is made of 2 main parts: the site’s API and a tracker. The site’s API is meant to be used by the frontend, while the tracker is meant to be used by torrent clients (qbittorrent, deluge, etc.).

Backend

Arcadia’s backend is a REST API written in rust with the actix framework and the sqlx database driver. It also uses PostgreSQL as its database.

Code Structure

API calls are forwarded to handlers, database requests are done by repositories, objects are defined by models. Directories with those names contain the relevant code.

A swagger for the API is available at http://localhost:8080/swagger-ui/

Frontend

Arcadia’s frontend is a SPA written in TypeScript and uses the Vue.js framework with PrimeVue components, Vite builds it.

API Schema Updates

If you make changes to structs that are listed in the swagger or the api routes, you must regenerate the typescript interfaces with this command (from the frontend directory, while the backend is running):

npx openapi-generator-cli generate -g typescript-axios -i http://127.0.0.1:8080/swagger-json/openapi.json   -o ./src/services/api-schema -t .openapi-generator/templates --config .openapi-generator/openapi-generator.config.json --global-property=apiDocs=false,modelDocs=false,skipFormModel=false

Getting Started

Arcadia can be deployed using Docker Compose (recommended) or directly on bare metal.

1. Clone Repository

git clone https://github.com/Arcadia-Solutions/arcadia.git
cd arcadia

2. Choose Deployment Method

Review the Configuration Reference for secret and environment variable details, then choose your deployment route:

  • Docker Deployment (Recommended): Containerized setup with bundled PostgreSQL, Redis, Caddy reverse proxy, and automatic migrations.
  • Bare-Metal Installation: Manual setup running directly on the host system.

Configuration

The whole project is configured by a single config.yml file at the root of the repository: the backend, tracker, and frontend build all read it. It is git-ignored. Some of those values are overridden by the Environment Variables in .env at runtime

cp config.example.yml config.yml
cp example.env .env

config.example.yml documents every key and serves as the reference. Settings are annotated with tags for each environment:

  • # Production: Settings that must be changed before deploying publicly (secrets, domain URLs, SMTP).
  • # Bare-metal: Settings requiring host-specific adjustments when running without Docker.
  • # Development: Settings useful during local testing (e.g. verbose logging).
  • # Optional: Optional integrations and external plugins.

.env is used by Docker Compose to configure passwords and tokens shared across multiple services (such as PostgreSQL, Redis, Chevereto, and Ergo IRC).

For a local test with Docker Compose, the default credentials work out of the box. For a production environment, change all secrets and URLs.

Environment Variables

Any setting in config.yml can be overridden via environment variables using the naming convention: ARCADIA_<SECTION>__<KEY> (single underscore after ARCADIA_, double underscore __ between sections and keys). Environment variables always take precedence over values in config.yml.

Note

Docker Compose vs. Host Environment: Docker Compose uses .env to interpolate ${VARIABLE} expressions defined in compose.yml. Arbitrary ARCADIA_<SECTION>__<KEY> variables placed in .env are not automatically forwarded into containers unless they are explicitly declared under environment: in compose.yml or added via compose.override.yml.

Common Environment Variable Overrides

SettingYAML KeyEnvironment Variable
Database passworddatabase.passwordARCADIA_DATABASE__PASSWORD=secret
Database hostdatabase.hostARCADIA_DATABASE__HOST=db
Redis passwordredis.passwordARCADIA_REDIS__PASSWORD=secret
Redis hostredis.hostARCADIA_REDIS__HOST=redis
API host & portapi.host, api.portARCADIA_API__HOST=0.0.0.0, ARCADIA_API__PORT=8080
Tracker host & porttracker.host, tracker.portARCADIA_TRACKER__HOST=0.0.0.0, ARCADIA_TRACKER__PORT=8081
JWT Secretapi.jwt_secretARCADIA_API__JWT_SECRET=supersecret
Tracker API Keytracker.api_keyARCADIA_TRACKER__API_KEY=anothersecret

Site Customization & Theming

For configuring the site logo, favicon, custom landing pages, and unauthenticated CSS/JS stylesheets, see Customization & Theming.

Optional Integrations

Some services bundled with Arcadia require additional configuration:

Customization & Theming

Arcadia allows administrators to personalize the visual branding, landing pages, stylesheets, and assets of their instance.

Custom Landing Page

By default, visiting the root URL of an Arcadia instance displays the login view. If you wish to present a custom HTML landing page to unauthenticated visitors:

  1. In config.yml, set:
    frontend:
      enable_custom_front_page: true
    
  2. Place your custom HTML file at public/home/index.html (inside the frontend directory or container).
  3. When enabled, non-logged-in visitors landing on / are served this page, while authenticated users are directed to the home page (HomeView.vue).

Unauthenticated Pages (Custom CSS & JS)

Pages accessible before signing in (/login, /register, /apply, /reset-password) cannot use the user-selectable CSS stylesheets configured in the database, because no user session exists.

To apply custom branding to these pages, create two optional, git-ignored files:

  • frontend/public/custom_unauth.css: Custom CSS rules loaded on unauthenticated pages.
  • frontend/public/custom_unauth.js: Custom JavaScript executed on unauthenticated pages.

Note

Creating or editing these files requires updating the frontend (docker compose up -d frontend --build)

Site Assets & Branding

You can replace the default placeholder graphics with your own site branding by replacing the following asset files:

AssetFile LocationPurpose
Site Logofrontend/src/assets/logo.svgMain navbar logo (falls back to logo.example.svg if omitted)
Faviconfrontend/public/favicon.icoBrowser tab icon
Default Avatarfrontend/public/default_user_avatar.pngFallback avatar for users who haven’t uploaded one

Custom Icons (SVG Overrides)

Arcadia allows overriding any PrimeIcon across the frontend with a custom SVG file.

To replace an icon, place your .svg file into frontend/src/assets/custom-icons/ matching the PrimeIcon name without the pi- prefix:

  • For example, to override the Bonus Points icon (pi-wallet), save your SVG as:
    frontend/src/assets/custom-icons/wallet.svg
    
  • The build automatically generates CSS mask rules that replace the icon font glyph across the entire application with your SVG, seamlessly preserving theme colors (currentColor), sizes, and hover effects.

Rebuilding

Custom icons and assets are inlined into the compiled frontend bundle. After adding or changing assets in frontend/src/assets/custom-icons/, rebuild the frontend. For example, with docker:

docker compose build frontend
docker compose up -d frontend

User Stylesheets (CSS Sheets)

Once users are logged in, Arcadia supports custom theme stylesheets. Users with the create_css_sheet and edit_css_sheet permissions can add and manage site-wide stylesheets through the Staff Dashboard:

  • Each stylesheet is stored in the database (css_sheets table) with a unique name, CSS content, and an optional preview image.
  • Users can select their preferred active theme in their account settings.
  • The instance’s default theme is defined in the site settings (default_css_sheet_name, defaults to arcadia).

Docker Deployment

This guide will help you get Arcadia running quickly using Docker Compose.

Prerequisites

  • Docker and Docker Compose installed
  • Git (to clone the repository)

If running docker compose doesn’t work, you may have an older version of the docker cli installed and may need to use docker-compose instead.

Also don’t forget to use sudo if you aren’t in the docker group!

Quick Setup

  1. Copy Configuration Files

    cp config.example.yml config.yml
    cp example.env .env
    

    Docker Compose automatically injects inter-container networking and credentials via environment variables from .env, so no manual changes to config.yml are required for a local test run.

  2. Start Services

    docker compose up -d
    

    This starts the core services:

    • PostgreSQL database (db) and automatic schema migrations (init_db)
    • Redis cache (redis)
    • Backend API and periodic task runner (backend)
    • BitTorrent tracker (tracker, listening on host port 8081)
    • Frontend UI and reverse proxy (frontend, powered by Caddy on host port 5173)
  3. Database Initialization & Initial User Setup

    Choose one of the following approaches depending on your deployment:

    Option A: Development (Load Sample Fixtures) Populate the database with demo data:

    docker compose exec -T db psql -U arcadia -d arcadia < backend/storage/migrations/fixtures/fixtures.sql
    

    Credentials of the user with all permissions:

    • Username: picolo
    • Password: test

    Option B: Production (Clean Database & First Admin Setup) If you do not load fixtures.sql, the database initializes cleanly with schema migrations alone. To set up your first administrator:

    1. Navigate to http://localhost:5173/register (or your domain) and register your desired username and password.
    2. By default, newly registered users have the newbie class with zero administrative permissions. Promote your account to have full administrator permissions:
      docker compose exec -it db psql -U arcadia -d arcadia -c "UPDATE users SET permissions = enum_range(NULL::user_permissions_enum) WHERE username = 'YOUR_USERNAME';"
      
    3. Log in to the web interface to access site administration and create user classes.
  4. Access the Application

    • Frontend Web UI: http://localhost:5173
    • Backend API: http://localhost:5173/api/ (proxied internally via Caddy)
    • Tracker Announce: http://localhost:8081/announce/<passkey> (legacy fallback: http://localhost:8081/<passkey>/announce)

Production Deployment

By default, Docker Compose uses development credentials (arcadia / password). For production deployments:

  1. Set Strong Passwords in .env:

    cp example.env .env
    

    Edit .env and replace all placeholder passwords (ARCADIA_DATABASE__PASSWORD, ARCADIA_REDIS__PASSWORD, etc.) with strong random values (e.g. generated via openssl rand -hex 32).

  2. Configure URLs and Secrets in config.yml: Update settings marked # Production: in config.example.yml (api.jwt_secret, tracker.api_key, public URLs, and smtp:). See also the Configuration Reference.

Docker Compose automatically propagates credentials from .env across the stack:

  • db: Configures PostgreSQL user, password, database, and healthcheck.
  • init_db: Injects DATABASE_URL for running schema migrations.
  • redis: Configures Redis server password authentication (--requirepass).
  • backend & tracker: Injects database and Redis credentials via environment variables, overriding values from config.yml. Leave the database: and redis: sections commented out in config.yml (any values placed there are overridden and ignored).

Note

PostgreSQL only uses POSTGRES_PASSWORD when initializing a new database cluster. If you change ARCADIA_DATABASE__PASSWORD on an existing installation after the db_data volume has already been initialized, you must also update the password inside PostgreSQL:

docker compose exec -it db psql -U arcadia -d arcadia -c "ALTER USER arcadia WITH PASSWORD 'new_password';"

Reverse Proxy & HTTPS (Production)

The frontend container serves the static frontend files and proxies access to all other containers using Caddy.
To make Arcadia accessible via HTTPS with automatic Let’s Encrypt certificates across all services (Web UI, API, BitTorrent tracker, Chevereto image host, and Grafana monitoring), copy the tracked production templates:

cp compose.override.yml.example compose.override.yml
cp Caddyfile.example Caddyfile

Edit Caddyfile with your domain names. See the Compose Override Guide for full routing and service details.

Upgrading

For routine updates:

git fetch && git pull
docker compose up -d --build

The init_db container automatically runs pending incremental database migrations before the backend starts.

Warning

Because Arcadia is under rapid development, database schema changes are often committed directly to the baseline migration (backend/storage/migrations/20250312215600_initdb.sql) rather than distributed as incremental migrations. When this happens, init_db will fail with an SQLx checksum mismatch error. When pulling updates with baseline schema changes, follow the Schema Migration Upgrade Guide to dump, recreate, and restore your database.

Troubleshooting

  • If services fail to start, check logs with: docker compose logs [service-name]
  • To rebuild images: docker compose build
  • To reset everything: docker compose down -v && docker compose up -d

Customizing with Compose Override

Docker Compose automatically detects and merges compose.override.yml with compose.yml. Use this to adjust ports, volumes, or environment variables without modifying the version-controlled compose.yml.

The repository provides ready-to-use example configurations tracked directly in git:

  • compose.override.yml.example: Configures port forwarding (80/443), Caddy volume mounts, and service tweaks.
  • Caddyfile.example: Complete reverse proxy configuration routing the Web UI, API, IRC, BitTorrent Tracker, Chevereto image host, and Grafana monitoring.

To get started:

cp compose.override.yml.example compose.override.yml
cp Caddyfile.example Caddyfile

1. Production Setup: Exposing Services via Caddy

In production, you want Caddy to terminate HTTPS and obtain automatic Let’s Encrypt certificates for your public domains, proxying each container over Docker’s internal network:

services:
  frontend:
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

volumes:
  caddy_data:
  caddy_config:

2. Development Setup: Exposing Ports for Host Debugging

services:
  db:
    ports:
      - "5432:5432"
  redis:
    ports:
      - "6379:6379"
  backend:
    ports:
      - "8080:8080"

3. Override configuration with environment variables

You can override any setting from config.yml using environment: blocks:

services:
  backend:
    environment:
      ARCADIA_API__LOG_LEVEL: "debug,sqlx=debug"
      ARCADIA_API__JWT_SECRET: "my-secure-production-secret"
  tracker:
    environment:
      ARCADIA_TRACKER__NUMWANT: "30"

Manual / Bare-Metal Installation

This page explains how to install and run Arcadia directly on your system without Docker containers.

Prerequisites

Before starting, ensure you have the following installed:

  • PostgreSQL - Database server
  • Redis - Cache for the auth
  • Rust & Cargo - Required to build the backend
  • Node.js & npm - Required to build the frontend
  • Git - To clone the repository

For development tool installation instructions, see the Developer Setup guide.

Quick Start

  1. Clone the repository and navigate to it:
    git clone https://github.com/Arcadia-Solutions/arcadia.git
    cd arcadia
    
  2. Set up PostgreSQL database and run migrations
  3. Set up Redis server
  4. Configure config.yml (uncomment database and redis, adapt internal URLs)
  5. Configure and run the backend (arcadia-api)
  6. Configure and run the frontend (cd frontend && npm run dev)
  7. Configure and run the tracker (arcadia_tracker)

Database Setup

1. Install PostgreSQL

Install PostgreSQL on your system:

Ubuntu/Debian:

sudo apt-get update
sudo apt-get install postgresql postgresql-contrib

macOS:

brew install postgresql
brew services start postgresql

Windows: Download and install from the PostgreSQL official website.

2. Create Database and User

Connect to PostgreSQL and create the database:

# Connect as postgres user
sudo -u postgres psql

# Or on Windows/macOS:
psql -U postgres

In the PostgreSQL shell:

-- Create user
CREATE USER arcadia WITH PASSWORD 'your_secure_password';

-- Create database
CREATE DATABASE arcadia OWNER arcadia;

-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE arcadia TO arcadia;

-- Exit
\q

3. Run Database Migrations

Install sqlx-cli and run migrations:

# Install sqlx-cli
cargo install sqlx-cli --no-default-features --features native-tls,postgres

# Navigate to storage directory
cd backend/storage

# Run migrations
sqlx migrate run --database-url postgresql://arcadia:your_secure_password@localhost:5432/arcadia

# Return to repository root
cd ../..

sqlx-cli only reads --database-url or DATABASE_URL, it does not know about config.yml. Use the credentials matching your PostgreSQL setup.

(If you get a “Could not find directory of OpenSSL installation” error, install pkg-config and libssl-dev / openssl-devel).

Optional: Seed Development Fixtures

If you want to populate sample categories, users, and torrents for testing:

psql -U arcadia -d arcadia -f backend/storage/migrations/fixtures/fixtures.sql

Default test credentials: picolo / test.

For a clean production installation, skip this step and see Bootstrapping the Administrator below.

Redis Setup

Arcadia uses Redis for session management and caching.

Ubuntu/Debian:

sudo apt-get install redis-server
sudo systemctl enable --now redis-server

macOS:

brew install redis
brew services start redis

By default on local systems, Redis binds to 127.0.0.1:6379 without a password. If you configure a password in Redis (requirepass <password> in /etc/redis/redis.conf), make sure to specify it in config.yml.

Configuration for Bare Metal

Create config.yml from the example (see the Configuration Reference for details on secrets and environment variable overrides):

cp config.example.yml config.yml

Important

config.example.yml defaults to Docker hostnames. For a bare-metal installation, review settings marked # Bare-metal: and # Production: in config.yml, including:

  1. Uncomment database: and redis::
    database:
      host: 127.0.0.1
      port: 5432
      user: arcadia
      password: your_secure_password
      name: arcadia
    
    redis:
      host: 127.0.0.1
      port: 6379
      password: ""
    
  2. Adjust internal URLs:
    • In tracker:, set url_internal: http://localhost:8081 (not http://tracker:8081).
  3. Set production secrets:
    • api.jwt_secret and tracker.api_key.

Backend Setup

Build and Run

From the repository root:

cargo run -p arcadia-api --release

The backend server (including the integrated periodic task scheduler) will start and listen on port 8080 (http://localhost:8080).

Frontend Setup

Build and Run

Navigate to the frontend directory, install dependencies, and start the development server:

cd frontend
npm install
npm run dev

The frontend will be accessible at http://localhost:5173. Vite proxies /api/ requests to the backend at http://localhost:8080.

Tracker Setup

Build and Run

From the repository root in a separate terminal:

cargo run -p arcadia_tracker --release

The BitTorrent tracker will start listening on port 8081.

Bootstrapping the Administrator

If you did not load the development fixtures.sql:

  1. Open the web interface at http://localhost:5173/register and create your user account.
  2. Newly created accounts default to the newbie user class. Connect to PostgreSQL to grant full administrator permissions:
    psql -U arcadia -d arcadia -c "UPDATE users SET permissions = enum_range(NULL::user_permissions_enum) WHERE username = 'YOUR_USERNAME';"
    

Upgrading

For routine updates that do not alter the database schema:

git fetch && git pull
cargo build -p arcadia-api --release
cargo build -p arcadia_tracker --release
cd frontend && npm install && npm run build && cd ..

Restart your backend and tracker services.

Warning

If upstream commits modify database schema migrations (initdb.sql), follow the Schema Migration Upgrade Guide for the data dump and schema migration procedure.

Troubleshooting

Database Issues

PostgreSQL not running:

# Ubuntu/Debian
sudo systemctl start postgresql
sudo systemctl enable postgresql

# macOS
brew services start postgresql

Connection errors:

  • Verify PostgreSQL is running on port 5432
  • Check that the database and user exist
  • Ensure the database section of config.yml is correct

Build Issues

Backend API build fails:

  • Install system dependencies listed above
  • Update Rust: rustup update
  • Clear build cache: cargo clean

Frontend build fails:

  • Check Node.js version compatibility
  • Clear npm cache: npm cache clean --force
  • Delete node_modules and run npm install again

Runtime Issues

Backend API won’t start:

  • Check the database connection
  • Verify the values in config.yml
  • Ensure migrations have been run

Frontend can’t connect to backend API:

  • Verify the backend is running on the correct port
  • Check frontend.api_base_url in config.yml, and restart the frontend: the section is inlined in the bundle at build time

Environment Variable Overrides

Any value in config.yml can be overridden via environment variables without editing the file. See the Configuration section for the full ARCADIA_<SECTION>__<KEY> syntax and common examples.

Stopping Arcadia

To stop Arcadia:

  1. Stop the frontend with Ctrl+C in its terminal
  2. Stop the tracker with Ctrl+C in its terminal and wait for its graceful shutdown
  3. Stop the backend API with Ctrl+C in its terminal
  4. Optionally stop PostgreSQL if you don’t need it for other applications

Integrations Overview

Arcadia provides optional bundled services managed through Docker Compose profiles. By default, running docker compose up -d starts only the core services (db, init_db, redis, backend, tracker, frontend).

To activate optional integrations, specify their profile:

ProfileCommandServices StartedDescription
All Servicesdocker compose --profile full up -dAll containersStarts the complete stack including IRC, Chevereto, and Grafana.
IRC Chatdocker compose --profile irc up -dergo, ergo_databaseErgo IRC daemon with KiwiIRC web client and MariaDB history.
Image Hostingdocker compose --profile images up -dchevereto_php, chevereto_databaseChevereto image hosting platform for avatars and torrent media.
Telemetrydocker compose --profile telemetry up -dotel-lgtm, hostmetricsOpenTelemetry collector and pre-built Grafana dashboards.

Profiles can be combined:

docker compose --profile irc --profile images up -d

IRC Server (Ergo & KiwiIRC)

Arcadia includes integrated IRC chat using Ergo as the IRC server and KiwiIRC as the web-based chat client. KiwiIRC is used on the home page and as a help chat for unauthenticated users, but any IRC client can be used to reach ergo.

The integration is optional and runs in dedicated containers enabled via the irc or full Docker Compose profiles.


Setup Guide

1. Copy Configuration Templates

Before starting the IRC service, create copies of the example configuration files:

cp ergo/ergo-conf.yaml.example ergo/ergo-conf.yaml
cp ergo/ergo.motd.example ergo/ergo.motd
cp kiwiirc/config.json.example kiwiirc/config.json

Important

ergo/ergo-conf.yaml and ergo/ergo.motd must exist on the host before starting the container, as compose.yml mounts them and would otherwise create empty directories.

2. Configure Tokens & Enable in config.yml

In .env, define secure random tokens:

ARCADIA_ERGO__API_BEARER_TOKEN=your_secure_bearer_token
ARCADIA_ERGO__AUTH_CALLBACK_TOKEN=your_other_secure_token

In config.yml, uncomment the ergo: block to activate IRC features:

ergo:
  api_url: http://ergo:8089

(Alternatively, provide ARCADIA_ERGO__API_URL: http://ergo:8089 in compose.override.yml under backend.environment).

Note

Arcadia’s backend uses api_bearer_token to provision IRC accounts via Ergo’s administrative HTTP API (/v1/saregister). When users connect, Ergo verifies their credentials against Arcadia via an auth callback secured by auth_callback_token. ergo needs the callback_token int the auth-script (loaded from the environment in the default setup)

3. Start IRC Services

Launch the IRC daemon and history database:

docker compose --profile irc up -d
docker compose restart backend

Services started:

  • ergo: The IRC daemon. External desktop clients (HexChat, WeeChat) can connect via plain IRC on port 6667.
  • ergo_database: MariaDB instance storing channel and direct message history.
  • frontend: Caddy automatically routes KiwiIRC assets at /kiwiirc/ and proxies WebSocket connections (/webirc/websocket → ergo:8097).

Channels & Guest Webchat

In config.yml, configure default channels that unauthenticated visitors can join from the login page:

frontend:
  irc_webchat_guest_channels:
    - "#help"

To support unauthenticated guest chat, ensure accounts.require-sasl.enabled: false in ergo/ergo-conf.yaml. Keep member-only channels restricted by setting their channel mode to +r (registered accounts only).


Local Development with Vite

When running npm run dev in the frontend directory:

  • Vite proxies /webirc/websocket to ws://localhost:8097.
  • Expose port 8097 in compose.override.yml:
    services:
      ergo:
        ports:
          - "8097:8097"
    
  • Build and extract KiwiIRC web assets locally:
    cd frontend
    npm run kiwi:setup
    

Image Hosting (Chevereto)

Arcadia bundles Chevereto to host images on your site.

The service is optional and runs in dedicated containers enabled via the images or full Docker Compose profiles.

Setup Guide

1. Configure Credentials and Start Containers

In .env, define secure passwords for the MariaDB instance:

CHEVERETO_DB_PASS=your_secure_chevereto_password
CHEVERETO_DB_ROOT_PASS=very_secure_chevereto_root_password

Launch the image hosting services:

docker compose --profile images up -d

2. Run the Initial Web Installer & Generate API Key

  1. Open your browser and navigate to http://localhost:8083 (or your configured domain).
  2. Complete the initial installation wizard to create your administrator account and initialize tables.
  3. Once logged in as administrator, navigate to Dashboard → Settings → API.
  4. Generate and copy the API v1 key.

3. Connect Arcadia to Chevereto

In config.yml, configure the image_host: section:

image_host:
  chevereto_api_url: http://chevereto_php/api/1/upload
  chevereto_api_key: your_generated_chevereto_api_key
  # Automatically rehost posters and covers retrieved by scrapers (TMDB, MusicBrainz)
  rehost_external_images: true

(Alternatively, inject the API key via environment variable: ARCADIA_IMAGE_HOST__CHEVERETO_API_KEY=your_key).

Restart the backend container to apply the configuration:

docker compose restart backend

4. Enable Image Uploader in Staff Dashboard

By default, the frontend drag-and-drop uploader is disabled. To activate it:

  1. Log in with an account having administrator permissions.
  2. In the top navbar, navigate to Staff Dashboard → Arcadia Settings.
  3. Enable Display image upload drag and drop and save changes.

The image upload widget will now appear when creating or editing torrents, editions, and artists. If you enforce an Approved Image Hosts whitelist in site settings, add your image hosting domain to the list.

Production Reverse Proxy & HTTPS

To expose Chevereto on a dedicated subdomain in production, configure CHEVERETO_HOSTNAME in compose.override.yml and enable the Chevereto block in Caddyfile (both pre-configured in compose.override.yml.example and Caddyfile.example).

Monitoring (OpenTelemetry & Grafana)

Arcadia includes deep observability instrumentation with OpenTelemetry. The backend, BitTorrent tracker, and periodic background scheduler emit structured metrics and distributed traces via OTLP gRPC.

Architecture

When running with the telemetry or full profile, Docker Compose provisions:

  • otel-lgtm: A unified container bundling Grafana (port 3000), Prometheus/Mimir (metrics), Loki (logs), and Tempo (traces) with an integrated OTLP gRPC collector on port 4317.
  • hostmetrics: An OpenTelemetry Collector container gathering system-level host metrics (CPU, RAM, disk I/O, network) from the Docker host.

Setup Guide

1. Set Grafana Password in .env

In .env, define your secure administrator password for the Grafana UI:

GF_SECURITY_ADMIN_PASSWORD=your_secure_grafana_password

2. Start the Telemetry Containers

Launch the monitoring services:

docker compose --profile telemetry up -d

3. Enable Telemetry Export in Arcadia

In config.yml, configure the telemetry: section to export OTLP data:

telemetry:
  otlp_endpoint: http://otel-lgtm:4317

Restart backend and tracker to begin exporting data:

docker compose restart backend tracker

4. Import the Pre-Built Dashboard

  1. Open your browser and navigate to http://localhost:3000.
  2. Sign in with:
    • Username: admin
    • Password: the value of GF_SECURITY_ADMIN_PASSWORD (defaults to arcadia).
  3. In the left navigation bar, go to Dashboards → New → Import.
  4. Click Upload dashboard JSON file and select opentelemetry/sample-dashboard.json from the repository.
  5. Click Import.

The imported dashboard provides real-time visibility into:

  • API endpoint request rates, response latency percentiles (p50, p95, p99), and HTTP status codes.
  • BitTorrent tracker announce and scrape request throughput.
  • Peer connection tracking, caching ratios, and active peer counts.
  • Periodic background tasks: execution intervals, durations, and rows affected.
  • Host CPU, memory pressure, and network throughput.

Production Reverse Proxy & HTTPS

To access Grafana securely in production, enable the Grafana block in Caddyfile (pre-configured in Caddyfile.example).

Upgrading Arcadia

This guide covers updating an existing Arcadia deployment for both Docker Compose and Bare-Metal installations.


1. Routine Updates (No Schema Changes)

When pulling updates that do not alter database migrations in backend/storage/migrations/:

Docker:

git fetch && git pull
docker compose up -d --build

The init_db container automatically applies any pending incremental migrations and starts the application stack.

Bare-Metal:

git fetch && git pull
cargo build -p arcadia-api --release
cargo build -p arcadia_tracker --release
cd frontend && npm install && npm run build && cd ..

Restart your backend (arcadia-api) and tracker (arcadia_tracker) processes or systemd services.


2. Upgrading Across Schema Changes

Because Arcadia is currently under rapid development, database schema changes are often committed directly to the baseline migration (backend/storage/migrations/20250312215600_initdb.sql) rather than distributed as incremental migration scripts.

When upstream commits alter 20250312215600_initdb.sql, running sqlx migrate run (or letting the Docker init_db service run) directly against an existing database will fail with a checksum mismatch error.

Use the following procedure to migrate your data into the updated schema.

Note

In the commands below, replace arcadia with your configured database username or database name if you modified defaults in .env (Docker) or config.yml (bare-metal).

Step 1: Dump Existing Data

Create a data-only SQL dump with explicit column inserts, excluding the _sqlx_migrations table:

Docker:

docker compose exec -T db pg_dump -U arcadia -d arcadia --data-only --column-inserts -T _sqlx_migrations > arcadia-data.sql

Bare-Metal:

pg_dump -U arcadia -d arcadia --data-only --column-inserts -T _sqlx_migrations > arcadia-data.sql

Important

Verify that arcadia-data.sql exists, is non-empty, and is stored in a safe backup location before proceeding.

Step 2: Pull Latest Commits

git fetch && git pull

Step 3: Recreate Database and Apply Schema

Drop and recreate the database, then run the updated schema migration:

Docker:

# Stop backend and tracker to release existing database connections
docker compose stop backend tracker

# Drop and recreate the database
docker compose exec -T db dropdb -U arcadia arcadia
docker compose exec -T db createdb -U arcadia arcadia

# Rebuild init_db image with the new migrations and apply schema
docker compose build init_db
docker compose run --rm init_db

Bare-Metal: Stop backend and tracker services and then:

# drop and recreate the database
dropdb -U arcadia arcadia
createdb -U arcadia arcadia
# run the new migrations
cd backend/storage
sqlx migrate run --database-url postgresql://arcadia:your_password@localhost:5432/arcadia
cd ../..

Step 4: Clear Pre-seeded Default Rows

The initial migration seeds default rows (arcadia_settings, system users, default user_classes, css_sheets, and initial forum_* entries). Truncate these seeded tables before restoring your dump to prevent primary key conflicts:

Docker:

docker compose exec -T db psql -U arcadia -d arcadia -c "
TRUNCATE users, user_classes, css_sheets, arcadia_settings, 
         forum_categories, forum_sub_categories, forum_threads, forum_posts CASCADE;
"

Bare-Metal:

psql -U arcadia -d arcadia -c "
TRUNCATE users, user_classes, css_sheets, arcadia_settings, 
         forum_categories, forum_sub_categories, forum_threads, forum_posts CASCADE;
"

Step 5: Restore Data

Restore the dump while temporarily disabling foreign-key triggers (session_replication_role = 'replica'). This allows foreign-keyed tables (such as hierarchical user_classes or forum relationships) to restore regardless of insert ordering:

Docker:

(echo "SET session_replication_role = 'replica';"; cat arcadia-data.sql; echo "SET session_replication_role = 'default';") | docker compose exec -T db psql -U arcadia -d arcadia -v ON_ERROR_STOP=1

Bare-Metal:

(echo "SET session_replication_role = 'replica';"; cat arcadia-data.sql; echo "SET session_replication_role = 'default';") | psql -U arcadia -d arcadia -v ON_ERROR_STOP=1

Step 6: Recompile and Restart

Start the stack with updated code and dependencies:

Docker:

docker compose up -d --build

Bare-Metal:

cargo build -p arcadia-api --release
cargo build -p arcadia_tracker --release
cd frontend && npm install && npm run build && cd ..

Restart your backend (arcadia-api) and tracker (arcadia_tracker) processes or systemd services.

Backup

Backups are made with restic: every run is an encrypted, deduplicated snapshot, so only what changed is stored and transferred.

  • backup/backup.sh runs on the arcadia server. It dumps the databases (arcadia, ergo, chevereto), then snapshots the dumps, the configuration files (config.yml, ergo/ergo-conf.yaml, ergo/ergo.motd, kiwiirc/config.json, compose.override.yml) and the volumes (docker) or data directories (bare metal) into a local repository, and prunes it.
  • backup/cron.sh runs backup.sh on the schedule of backup.cron, inside the backup_cron compose service (docker mode only).
  • backup/pull.sh runs on a separate backup host. It copies the snapshots it does not have yet over ssh, so the backup host keeps its own history, which the arcadia server cannot reach or delete.
  • backup/restore.sh restores a snapshot, on the same server or on a new one.

The database volumes are never copied raw (files of a running database are not consistent): the dumps cover them.

Requirements

  • docker mode: nothing but docker. The backup image carries restic, pg_dump and mariadb-dump (no docker CLI, no docker socket). Restoring runs on the host and uses restic from its pinned image.
  • host mode: restic 0.17 or newer, pg_dump/psql, mariadb-dump/mariadb matching the server versions, and curl (backup/restore.sh uses it to check that the backend is stopped). The scripts run as root.
  • backup host: restic 0.17 or newer, a checkout of this repository (or backup/pull.sh and scripts/config_value.sh).

Configuration

Everything is in the backup section of config.yml, documented in config.example.yml. The database credentials are read from the database section, and in docker mode the ergo and chevereto credentials come from the environment of the backup_cron service.

Environment variables named ARCADIA_<SECTION>__<KEY> (upper-cased, - replaced by _, e.g. ARCADIA_DATABASE__PASSWORD) override the values of config.yml. The ARCADIA_ prefix and __ separator keep a compose.override.yml able to set credentials without touching config.yml.

In docker mode the backup location is set only by two .env settings (see example.env); the repo, dumps and password file live inside them and config.yml does not repeat their paths (backup.repo, backup.dump_dir and backup.password_file are host mode only, ignored in docker mode):

  • BACKUP_DIR (default /var/backups/arcadia): host directory holding the repo and dumps, bind-mounted at the fixed path /var/backups/arcadia in the container. restore.sh runs on the host and reads BACKUP_DIR from .env to find the repository and dumps.
  • RESTIC_PASSWORD_FILE: host path of the password file, kept outside of BACKUP_DIR. It is bind-mounted at the fixed path /root/.arcadia-restic-password.

Create the repository password, and keep a copy somewhere safe, it is not part of the backups:

head -c 32 /dev/urandom | base64 > /root/.arcadia-restic-password
chmod 600 /root/.arcadia-restic-password

The repository is created by the first run of backup/backup.sh.

keep decides how many snapshots the server keeps. It must outlast the longest outage of the backup host, or snapshots are pruned before being pulled. remote_repo optionally copies every snapshot to other repositories: it is a space-separated list of push targets (local paths, s3:, rest:, sftp:, any restic backend).

Scheduling

backup.mode decides how: in docker mode the backup_cron service runs the job on the schedule of backup.cron, in host mode (or if you prefer the host’s own tools) a systemd timer runs it.

Docker: the backup_cron service

docker compose --profile backup up -d backup_cron

backup.cron holds the schedule in cron syntax, read in backup.cron_timezone (containers have no timezone of their own). The job is backup/backup.sh itself, so everything the sections above describe applies. The container joins the compose network and dumps db (postgres) and ergo_database/chevereto_database (mariadb) over TCP, the mariadb ones as their per-database user, not root. restic runs inside the container. There is no docker socket and no docker CLI: the image is based on postgres:18-alpine (so pg_dump matches the db service) plus restic and the mariadb client.

The service mounts the installation (read only), BACKUP_DIR, the password file and each volume of backup.volumes at /data/<name> (read only). config.yml is the configuration, together with the .env settings above.

To run a backup by hand:

docker compose --profile backup run --rm --entrypoint /arcadia/backup/backup.sh backup_cron

In docker mode backup/backup.sh cannot be run directly on the host: the database service names resolve only on the compose network.

The output of a run, and the fact that it failed, are in the container log:

docker compose logs backup_cron

systemd, on the host

/etc/systemd/system/arcadia-backup.service:

[Unit]
Description=Arcadia backup

[Service]
Type=oneshot
ExecStart=/opt/arcadia/backup/backup.sh

/etc/systemd/system/arcadia-backup.timer:

[Timer]
OnCalendar=*-*-* 03:00
Persistent=true

[Install]
WantedBy=timers.target
systemctl enable --now arcadia-backup.timer

A failing run exits with a non-zero code: use OnFailure= (or cron’s mail) to be notified.

Backup host

On the arcadia server, give the backup host’s ssh key access to a backup user that can read the repository and write its locks (restic creates new files group readable when the repository is):

useradd -m backup
chgrp -R backup /var/backups/arcadia/repo
chmod -R g+rX /var/backups/arcadia/repo
chmod g+w /var/backups/arcadia/repo/locks
find /var/backups/arcadia/repo -type d -exec chmod g+s {} +

On the backup host, write a configuration file, e.g. /etc/arcadia-backup-pull.yml:

backup_pull:
  ssh: backup@arcadia.example.com
  source_repo: /var/backups/arcadia/repo
  # command run on the arcadia server before pulling, empty when it runs its own timer, e.g.
  # sudo /opt/arcadia/backup/backup.sh (needs a sudo rule for the backup user)
  trigger: ""
  repo: /srv/backups/arcadia
  # a copy of the arcadia repository password
  password_file: /root/.arcadia-restic-password
  # retention of the backup host, longer than the server's and keeping at least what it keeps
  keep: --keep-daily 30 --keep-weekly 12 --keep-monthly 12

and schedule ARCADIA_CONFIG=/etc/arcadia-backup-pull.yml /opt/arcadia/backup/pull.sh after the server’s backup, the same way as above (Environment=ARCADIA_CONFIG=… in the service). It fails when no new snapshot was found, which also catches a server that stopped backing up. Missed days are caught up: every missing snapshot is copied. The backup host’s keep must keep at least every snapshot the server still keeps, otherwise pruned snapshots are copied again and counted as new.

Restore

backup/restore.sh            # latest snapshot
backup/restore.sh 1a2b3c4d   # a given snapshot, ids from `restic snapshots`
backup/restore.sh latest -y  # no confirmation

Stop the backup timer (and the backup host’s trigger) before restoring, otherwise a backup can run in the middle and latest changes.

Restore is run on the host, in both modes (in docker mode it uses the host’s docker and restores the volumes through the pinned restic image). It restores the configuration files, the volumes (or data directories) and the databases. In docker mode it stops the running services first and starts them again at the end. In host mode, stop arcadia, ergo and redis yourself first, and create the postgres role of the database section on a new server. Run backup/restore.sh from the deployment checkout (the same directory the stack was started from), because in docker mode the restore derives the compose project name from the directory to locate the real data volumes.

On a new server

  1. Clone the repository, cp config.example.yml config.yml: only the backup section matters, it is overwritten by the restored config.yml. dump_dir must be the one used for the backups.
  2. Put the password file in place.
  3. From the backup host, copy the repository to the new server:
    restic -r sftp:root@new-server:/var/backups/arcadia/repo init --from-repo /srv/backups/arcadia --copy-chunker-params
    restic -r sftp:root@new-server:/var/backups/arcadia/repo copy --from-repo /srv/backups/arcadia
    
  4. backup/restore.sh, then start the services.

From docker to bare metal (or back)

Not scripted. Restore a volume into a directory with restic restore latest:/data/ergo_data --target /var/lib/ergo, and load the dumps of dump_dir with psql and mariadb.

Limits

  • Paths and volume names cannot contain spaces, the crontab of the backup_cron service included: the job it schedules is one unquoted line.
  • Restoring in docker mode creates the volumes outside of docker compose, which warns that they were “not created by Docker Compose”; it is harmless.
  • backup.volumes takes unprefixed volume names, and each one must also be bind-mounted at /data/<name> in the backup_cron service of compose.yml: it is a two-place edit.
  • A volume listed in backup.volumes but never populated is backed up empty: nothing checks that it exists.
  • The mariadb dump is skipped only when the service is unreachable. A reachable service whose dump fails (bad credentials, for instance) fails the run.

Maintenance

A few tools are available in the Staff Dashboard on the frontend (once the user is granted the user_maintenance_tools permission). You might make use of them in such situations:

  • updated the source flag for .torrent files
  • manual changes in the db that caused a drift in cached counts (amount of title groups for a given artist for example)

Contributing

First, thanks for considering contributing to Arcadia!

Contributing Process

Whether you want to add a new feature or fix an existing issue, it needs to be done on your own branch:

  1. Fork this repository
  2. Clone it locally on your computer
  3. create a new branch feature-name or bug-name-fix (with the proper name)
  4. open a pull request when your contribution is done

If you are unsure about what/how to do something, don’t hesitate to open a discussion or an issue about the topic.

You can also hop on the Discord server to chat with other devs and the community.

Finding Contributions to Make

Arcadia has boards to track the existing issues and features that need to be worked on. Feel free to claim one that isn’t claimed yet before starting to work on it.

To claim a github issue, simply leave a comment on it saying that you are working on it.

You can also search for TODOs in the code and pick one of those tasks. If you decide to do this, please open an issue first and claim it before working on the task.

Backend Development Notes

  • If you make changes to/add sql queries with sqlx, you need to run cargo sqlx prepare inside the backend/storage folder before committing your changes. It reads the database from the DATABASE_URL environment variable (see Developer Setup), not from config.yml. This command will generate some files that allow the queries to be tested without a database running. Our CI pipeline relies on that, and will fail if the command hasn’t been ran. You can setup a git pre-commit hook if you want.

  • For better code quality, we use clippy in our CI pipeline. You can set your editor to run cargo clippy instead of cargo check (on file save, etc.).

Developer Setup

Development Containers (Optional)

If you don’t want to install another toolchain on your system, You can also use devcontainers instead. If you don’t know, think of isolated minimal virtual machines that come with the tools required to build Arcadia (or anything else really).

If you have Docker (recommended!) installed and use Visual Studio Code, all you need is to have the Dev Containers extension installed and “reopening your folder in a container”. You can find that option in your Command Palette, or by clicking on the new status bar item in the left bottom corner.

You can also use GitHub Codespaces to build Arcadia in the cloud without having to download anything but streams of text, although it’s not as free as a local dev container.

Open in GitHub Codespaces

It isn’t required to use them but can be useful in some cases (especially if you’re using an immutable OS).

Required Tools

You need these to make meaningful contributions to Arcadia, outside the cases of documentation for example.

  • Prettier for proper formatting of the frontend’s code.
  • sqlx-cli for managing database related stuff, including migrations.
  • Docker for setting up dependencies. Optional but HIGHLY recommended!
  • Insomnia for testing the backend’s API. You could also use any other client if you want.

Configuration Setup

Everything is configured by a single config.yml at the root of the repository. A quick way to get started is cp config.example.yml config.yml: that sample documents every key and is the reference for what each one does.

Compile-Time Requirement: DATABASE_URL

The sqlx query macros check the queries against a real database at compile time, and sqlx only reads DATABASE_URL. It is needed for cargo build, cargo clippy, and cargo sqlx prepare, never by the running services.

If you already created .env from example.env, append DATABASE_URL to it so existing credentials are not overwritten:

echo 'DATABASE_URL=postgresql://arcadia:password@localhost:5432/arcadia' >> .env

If you are running the database with Docker, port 5432 is not exposed to the host by default. See the database port mapping overrides to expose it with compose.override.yml.

Docker builds don’t need it, they build with SQLX_OFFLINE=true against the committed .sqlx caches.

Building and Running

API

# Build the backend
cargo build -p arcadia-api

# Run the backend binary
./target/debug/arcadia-api

# For optimized builds
cargo build -p arcadia-api --release
./target/release/arcadia-api

Frontend

cd frontend

# Install dependencies
npm install

# Build and run development server
npm run dev

# For production build
npm run build

Development Workflow

Backend Development

# For development with auto-rebuild on changes
cd backend/api
cargo run

# Build and test
cargo build -p arcadia-api
cargo test

# Code quality checks
cargo clippy --fix --allow-dirty
cargo fmt --all

Frontend Development

cd frontend

# Development server with hot reload
npm run dev

# Run tests
npm run test:unit

# Lint and format
npm run lint
npm run format

Optional: IRC & KiwiIRC Webchat in Local Development

When running npm run dev, Vite includes proxying for Ergo’s WebSocket endpoint (/webirc/websocket → ws://localhost:8097) and serves static KiwiIRC webchat assets under /kiwiirc/.

Both components are completely optional during development. If KiwiIRC is not built, Vite displays an informative placeholder in the chat drawer. If Ergo is not running, WebSocket connection failures are handled silently so the Vite dev server remains stable.

To enable full IRC and KiwiIRC functionality locally:

  1. Expose Ergo’s WebSocket port in Docker: By default, port 8097 is not exposed on the host. Create or add to compose.override.yml at the repository root:

    services:
      ergo:
        ports:
          - "8097:8097"
    

    Then start Ergo:

    docker compose --profile irc up -d ergo
    

    (See also Docker Compose Overrides).

  2. Populate kiwiirc/dist: KiwiIRC assets are git-ignored. You can automatically build and extract them using Docker

    cd frontend
    npm run kiwi:setup
    

    This builds KiwiIRC using Docker with the repository’s pinned commit and extracts the compiled assets into kiwiirc/dist/.

Note

Vite dynamically intercepts /kiwiirc/static/config.json (falling back to config.json.example if not present) and /kiwiirc/static/plugins/arcadia-plugin.js to serve them directly from the kiwiirc/ directory in the repository. You can modify either file and refresh the browser without rebuilding KiwiIRC.

Common Docker Commands

Auto-rebuild with Compose Watch

For live development, Compose Watch automatically rebuilds images or syncs frontend files on source changes:

docker compose up --watch

Or when running attached without -d, press W to enable watch mode.

Exporting Test Data

If you added new test data in your local container and wish to update the repository fixtures:

docker compose exec -T db pg_dump -U arcadia -d arcadia --data-only --inserts --column-inserts > backend/storage/migrations/fixtures/fixtures.sql && sed -i '/SELECT pg_catalog.set_config(\x27search_path\x27, \x27\x27, false);/d' backend/storage/migrations/fixtures/fixtures.sql

1 line generated by pgdump must be removed as it prevents the collage_entry fixtures from being inserted (the trigger somehow can’t be interprted). If someone has an explanation, please let us know/open a PR!

Manual Database Setup (if needed)

Arcadia automatically runs migrations on launch (init_db container), but if you need to manually run migrations against a running database:

cargo install sqlx-cli
DATABASE_URL=postgresql://arcadia:password@localhost:5432/arcadia cargo sqlx database setup

sqlx-cli only reads DATABASE_URL (configured in .env), it does not read config.yml. Make sure the database port is exposed.

Plugins

Plugins let an instance add custom behaviour without forking arcadia and without merging anything upstream. A plugin is a separate service that arcadia calls over HTTP, so it can be written in any language and deployed independently.

For now, plugins can add external sources (scrapers), like the built in TMDB, MusicBrainz, ISBN and Comic Vine ones.

Configuration

Declare your plugins in the scrapers section of config.yml, at the root of the repository:

scrapers:
  - id: anime
    placeholder: Anime url
    sources:
      AniDB:
        - tv_show
      MyAnimeList:
        - tv_show
        - movie
    url: http://anime-plugin:9000/scrape
    timeout_seconds: 30
FieldDescription
idIdentifier used in the route /api/external-sources/<id>. Must not clash with a built in source (tmdb, musicbrainz, isbn, comic-vine).
placeholderPlaceholder of the input field displayed in the interface, also used as the source’s display name.
sourcesThe websites the endpoint accepts links from, each with the content types it supports: movie, video, tv_show, music, podcast, software, book, live_performance, collection. A single endpoint may serve several websites, dispatching on the link it is given. They are listed in a tooltip next to the input on the upload page once there are several of them, and the source is offered for every content type at least one of them supports.
urlEndpoint of the plugin.
timeout_secondsOptional, defaults to 30.

config.yml is read once at startup. When the scrapers section is absent, no plugin is registered.

Both config.yml and compose.override.yml are git ignored. compose.yml already mounts config.yml into the backend container, so only the plugin services themselves have to be declared in compose.override.yml, which Docker Compose loads automatically on top of compose.yml:

services:
  anime-plugin:
    build: ../anime-plugin
    restart: unless-stopped

Writing a scraper plugin

The declared endpoint is called with the identifier the user typed as the url query parameter:

GET http://anime-plugin:9000/scrape?url=https://anidb.net/anime/1234

It must answer with JSON:

{
  "title_group": { "name": "...", "description": "...", "content_type": "tv_show", "...": "..." },
  "edition_group": null,
  "affiliated_artists": [
    {
      "name": "Some Person",
      "aliases": [],
      "description": "",
      "pictures": ["https://example.com/picture.jpg"],
      "external_links": ["https://example.com/person/1"],
      "roles": ["director"],
      "nickname": null
    }
  ]
}

title_group follows the UserCreatedTitleGroup schema and edition_group the UserCreatedEditionGroup one; both are documented in the OpenAPI specification. All three fields are optional.

Artists are given by name: arcadia creates them, merges the roles of an artist appearing several times, and returns real affiliated artists to the interface. Arcadia also checks beforehand whether a title group already has the submitted link, and appends that link to the scraped title group.

external_links is optional and holds the artist’s pages on public databases and other trackers.

Pictures are rehosted if image rehosting is enabled.

Anything else the plugin needs (API keys, caching, rate limiting) is its own business.

Reporting a failure

A plugin that cannot scrape answers with a status outside the 2xx range and a body holding the message meant for the uploader:

{ "error": "www.example.com answered with 503 Service Unavailable" }

Arcadia shows that message as is, and answers the interface with a 502 whatever status the plugin used. A failure with no such body, an unreachable plugin, and an answer arcadia cannot read are reported as a generic error instead, the details only being logged. Write the message for the uploader: what they can act on (a wrong url, a page holding nothing, a site that is down), never a stack trace or an internal identifier.

Testing Guide

Arcadia maintains automated test suites across both the Rust backend services and the Vue 3 frontend.


1. Backend Testing (Rust)

Backend testing is split between fast in-memory unit tests and database-backed Actix integration tests.

Unit Tests

Unit tests live directly alongside source code in #[cfg(test)] mod tests modules within each crate (shared, backend/api, backend/common, backend/storage, tracker/arcadia_tracker, and backend/periodic-tasks).

Run all unit tests across the workspace:

cargo test --lib

To run unit tests for a specific crate:

cargo test -p arcadia-shared --lib
cargo test -p arcadia_tracker --lib

Integration & API Tests

Integration tests live in backend/api/tests/. They use:

  • actix_web::test: Simulates HTTP requests against real Actix service endpoints without binding to a host network port.
  • sqlx::test: Executes tests against a test PostgreSQL database, using transaction rollbacks and SQL test fixtures (located in backend/api/tests/fixtures/).

To run integration tests, ensure a database is accessible via DATABASE_URL (or started with docker compose up -d db):

cargo test -p arcadia-api --test '*'

To run a single integration test file:

cargo test -p arcadia-api --test test_auth
cargo test -p arcadia-api --test test_torrent

2. Frontend Testing (Vue 3 & TypeScript)

The frontend uses modern testing frameworks integrated with Vite and npm scripts.

Unit & Component Testing (Vitest)

Unit tests use Vitest, a Vite-native test runner optimized for Vue 3 composables, Pinia stores, and utility functions (such as media parsers and formatters).

Run the unit test suite:

cd frontend
npm run test:unit

To run tests in watch mode during development:

cd frontend
npx vitest watch

End-to-End Testing (Playwright)

Browser-level end-to-end testing is powered by Playwright. Playwright launches headless browser engines (Chromium, Firefox, WebKit) to verify user authentication, browsing, search filters, and form submissions.

Run end-to-end tests:

cd frontend
npm run test:e2e

Linters & Formatting

cd frontend
npm run lint
npm run format

Legal Notice

This tool (Arcadia) is intended for legal use only. Users (both the ones hosting Arcadia and the ones using it) are solely responsible for the content they download and share through it. Downloading or distributing copyrighted material without proper authorization is illegal in most jurisdictions. By hosting and/or using Arcadia, you agree to abide by all applicable laws and respect intellectual property rights. The developers of Arcadia assume no responsibility for any illegal activities conducted by its hosters and users.