Skip to main content

Overview

Annie Mei only depends on a few infrastructure categories:
  • PostgreSQL for persistent storage
  • Redis for caching
  • Secret injection for environment variables
  • Sentry for error tracking
  • A Linux host or container platform to run the bot and, for full self-hosted account linking, the auth service
For complete self-hosted deployments, plan for two processes: the Discord bot and the AniList OAuth auth service. The bot needs PostgreSQL and Redis. The auth service needs PostgreSQL. The hosted Annie Mei bot currently uses Supabase, Upstash, and Doppler. Those providers are not self-hosting requirements; you can use any compatible PostgreSQL, Redis, secrets workflow, and compute provider.

Database: PostgreSQL

What Annie Mei needs

  • A PostgreSQL connection string in DATABASE_URL
  • A database user with read/write permissions for the bot and auth service schemas
  • TLS enabled if your database is remote
  • Optional connection pooling if your provider offers it

Setup

  1. Provision a PostgreSQL database with version 17 or later.
  2. Create a database and user for Annie Mei.
  3. Get the connection string:
    • Format: postgres://user:password@host:5432/database
    • Add ?sslmode=require if your provider requires TLS
    • Set it as DATABASE_URL
  4. Start the bot. The bot connects to PostgreSQL and expects the schema to already be in place.

Connection Pooling

If your provider offers a pooled connection string, prefer that in production:
DATABASE_URL=postgres://user:[email protected]/annie_mei?sslmode=require
Provider-specific examples are references, not requirements. Any PostgreSQL provider works if the connection string is reachable from both services.

Schema

The auth service owns the current OAuth schema migrations. The schema is defined in:
  • auth/migrations/ (SQLx migration files in the auth service repo)
The bot repo’s migrations/ directory contains bot-owned migrations for settings tables in the annie_mei schema. It is not used to prepare the OAuth schema. The bot reads from the auth-service-owned oauth_credentials table. The database stores:
  • OAuth credentials linking Discord accounts to AniList accounts
  • AniList usernames and AniList user IDs for registered Discord accounts
  • OAuth session state for in-progress and completed registration flows

Cache: Redis

What Annie Mei needs

  • A Redis connection string in REDIS_URL
  • Standard Redis or TLS Redis support
  • Enough memory for cached API responses

Setup

  1. Provision a Redis instance close to where the bot runs.
  2. Get the connection string:
    • Standard Redis: redis://host:6379
    • TLS Redis: rediss://default:password@host:port
    • Set it as REDIS_URL
  3. Enable TLS if your provider supports or requires it.

Caching Strategy

Annie Mei caches:
  • AniList API responses (anime/manga data)
  • MyAnimeList theme song data
  • Spotify track lookups
The current implementation uses one fixed cache TTL of 5 hours for cached responses.

Monitoring

Monitor Redis usage through whatever tooling your host provides:
  • Request count
  • Memory usage
  • Cache hit rate

Secrets management

What Annie Mei needs

  • All required environment variables available at process start
  • A way to keep secrets out of git
  • A repeatable way to inject secrets into production and CI

Setup

You can provide secrets in several ways:
  • A local .env file for development
  • A systemd EnvironmentFile
  • Docker or Kubernetes secrets
  • A hosted secrets manager such as Doppler, 1Password, AWS Secrets Manager, or similar
If you use Doppler, a simple workflow looks like this:
curl -Ls https://cli.doppler.com/install.sh | sh
doppler login
doppler setup
doppler run -- ./annie-mei

CI/CD Integration

Doppler can also sync secrets to GitHub Actions if that matches your workflow:
  1. Generate a service token in Doppler
  2. Add as DOPPLER_TOKEN in GitHub repository secrets
  3. Use in workflows:
    - name: Install Doppler CLI
      uses: dopplerhq/cli-action@v3
    
    - name: Run with secrets
      run: doppler run -- ./annie-mei
      env:
        DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }}
    

Monitoring: Sentry

Setup

  1. Create a Sentry account at sentry.io
  2. Create a project:
    • Platform: Rust
    • Copy the DSN
    • Set as SENTRY_DSN environment variable
  3. Configure trace sampling:
    SENTRY_TRACES_SAMPLE_RATE=0.1  # 10% of traces
    

Privacy Features

Annie Mei implements privacy-focused monitoring:
  • User ID hashing: Discord user IDs are hashed before sending to Sentry using USERID_HASH_SALT
  • Credential redaction: Database and API credentials in URLs are automatically stripped from error logs
  • Hash lookup utility: Use ./annie-mei hash <user_id> to find a user’s hashed ID in Sentry
See src/main.rs:146-173 for implementation details.

Debug Symbols

The CI/CD pipeline automatically uploads debug symbols to Sentry for symbolicated stack traces:
# From .github/workflows/build-release.yml:44-56
- name: Upload debug symbols to Sentry
  run: |
    curl -sL https://sentry.io/get-cli/ | bash
    sentry-cli debug-files upload --include-sources target/release/
  env:
    SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
    SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
    SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
Set these GitHub secrets:
  • SENTRY_AUTH_TOKEN: Create in Sentry settings
  • SENTRY_ORG: Your Sentry organization slug
  • SENTRY_PROJECT: Your project slug

Compute / VM

Deployment target

Annie Mei can run on any Linux host that can execute the release binary and reach Discord, PostgreSQL, Redis, and the external APIs. The current production deployment uses Oracle Cloud ARM64 VMs and GitHub Actions, so the examples in this section reflect that setup.

Server Setup

  1. Create a Linux VM or host with network access to your services.
  2. Install system dependencies:
    sudo apt-get update
    sudo apt-get install -y libpq-dev
    
  3. Create the application directory:
    sudo mkdir -p /opt/annie-mei
    sudo chown $USER:$USER /opt/annie-mei
    
  4. Create a systemd service (/etc/systemd/system/annie-mei.service):
    [Unit]
    Description=Annie Mei Discord Bot
    After=network.target
    
    [Service]
    Type=simple
    User=annie
    WorkingDirectory=/opt/annie-mei
    ExecStart=/opt/annie-mei/annie-mei
    Restart=always
    RestartSec=10
    
     # Load environment variables from your chosen secrets source
     EnvironmentFile=/opt/annie-mei/.env
    
    [Install]
    WantedBy=multi-user.target
    
  5. Enable and start the service:
    sudo systemctl daemon-reload
    sudo systemctl enable annie-mei
    sudo systemctl start annie-mei
    

Health Checks

The auth service owns HTTP health checks for the stack:
curl http://127.0.0.1:8000/healthz
curl http://127.0.0.1:8000/readyz
Use /healthz for liveness and /readyz for dependency-aware readiness in load balancers, monitoring systems, and deployment verification. The Discord bot process does not expose an HTTP health server.

CI/CD: GitHub Actions

Annie Mei uses GitHub Actions for automated building, testing, and deployment.

Workflows

1. Clippy Analysis (.github/workflows/rust-clippy.yml)

Runs on every PR and push to main:
  • Lints code with cargo clippy
  • Uploads results to GitHub Security
  • Annotates PRs with warnings

2. Build and Deploy (.github/workflows/build-release.yml)

Runs on:
  • Version tags (v*.*.*)
  • Manual workflow dispatch
Steps:
  1. Build ARM64 binary in Rust Docker container
  2. Upload debug symbols to Sentry
  3. Create GitHub release with binaries
  4. Deploy to the target host:
    • Copy binary via SCP
    • Validate binary (executable, dependencies, files)
    • Backup current binary
    • Restart systemd service
    • Verify service started successfully
    • Rollback on failure

Required GitHub Secrets

Set these in your GitHub repository settings: Current host deployment example:
  • ORACLE_HOST: VM hostname or IP
  • ORACLE_USER: SSH username
  • ORACLE_SSH_KEY: Private SSH key
Sentry Integration:
  • SENTRY_AUTH_TOKEN: Sentry API token
  • SENTRY_ORG: Organization slug
  • SENTRY_PROJECT: Project slug

Manual Deployment

To deploy a specific branch manually:
gh workflow run build-release.yml -f ref=your-branch-name

Deployment Process

The automated deployment (.github/workflows/build-release.yml:102-139):
  1. Validates the new binary
  2. Checks library dependencies with ldd
  3. Verifies required files (e.g., mei.jpg)
  4. Backs up the current binary
  5. Stops the service
  6. Replaces the binary
  7. Starts the service
  8. Verifies the service started successfully
  9. Rolls back if startup fails

Cost planning

Your costs depend on the providers you choose, the number of Discord servers using the bot, and how much API traffic you generate. Typical cost buckets are:
  • PostgreSQL hosting
  • Redis hosting
  • Compute or container runtime
  • Error monitoring
  • Optional secrets management
If you self-host PostgreSQL or Redis, those costs shift from managed-service pricing to your own infrastructure and maintenance time.

Security Best Practices

Network Security

  1. Restrict database access to the bot host or private network where possible
  2. Use TLS for PostgreSQL and Redis whenever your provider supports it
  3. Lock down inbound ports so only required management and health-check access is exposed

Secrets Management

  1. Never commit secrets to git
  2. Rotate credentials regularly
  3. Use a secrets manager if you need centralized secret management
  4. Limit secret access to only necessary users and services

Monitoring

  1. Enable Sentry alerts for error spikes
  2. Monitor database and Redis resource usage in your provider dashboards
  3. Set up uptime monitoring for the HTTP health check endpoint
  4. Review Sentry issues regularly

Next Steps

  • Review Configuration for environment setup
  • See Environment Variables for complete variable reference
  • Set up monitoring alerts in Sentry
  • Set up your preferred secret injection workflow for deployments