Skip to content

Installation Guide

This guide covers setting up Rondo Sync from scratch on a fresh server.

  • OS: Ubuntu/Debian Linux (tested on Ubuntu 22.04)
  • Node.js: 18+ (required for built-in fetch, FormData, and Blob)
  • RAM: 1 GB minimum (Chromium browser automation needs headroom)
  • Disk: 2 GB free (for Node.js, Chromium, SQLite databases, photos, and logs)
  • Network: Outbound HTTPS access to:
    • club.sportlink.com (Sportlink Club)
    • api.laposta.nl (Laposta email marketing)
    • Your Rondo Club instance
    • Lettermint API (email delivery)
    • nikki-online.nl (Nikki contributions, if used)
    • Your FreeScout instance (if used)

You’ll need credentials for:

ServiceWhat you needWhere to get it
Sportlink ClubUsername, password, TOTP secretClub administrator
LapostaAPI key, list ID(s)Laposta dashboard -> Account -> API
Rondo ClubURL, username, application passwordWordPress admin -> Users -> Profile -> Application Passwords
LettermintAPI token, verified sender emailLettermint dashboard
FreeScout (optional)API key, instance URLFreeScout admin panel
Nikki (optional)API key, URLNikki administrator
Terminal window
# Using NodeSource (recommended)
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
# Verify
node --version # Should be 18.x or higher
npm --version

Playwright’s Chromium needs these system libraries:

Terminal window
sudo apt-get install -y \
libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 \
libcups2 libdrm2 libdbus-1-3 libxkbcommon0 \
libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 \
libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2
Terminal window
cd /home
git clone <repository-url> rondo
cd rondo
# Install Node.js dependencies
npm install
# Install Chromium for browser automation
npx playwright install chromium

Create /home/rondo/.env with your credentials:

Terminal window
# Sportlink credentials
SPORTLINK_USERNAME=[email protected]
SPORTLINK_PASSWORD=your-password
SPORTLINK_OTP_SECRET=your-totp-base32-secret
# Laposta
LAPOSTA_API_KEY=your-laposta-api-key
LAPOSTA_LIST=your-primary-list-id
LAPOSTA_LIST2= # Optional second list
LAPOSTA_LIST3= # Optional third list
LAPOSTA_LIST4= # Optional fourth list
# Rondo Club
RONDO_URL=https://your-rondo-club-site.nl
RONDO_USERNAME=your-wp-username
RONDO_APP_PASSWORD=xxxx xxxx xxxx xxxx xxxx xxxx
RONDO_PERSON_TYPE=person
# Email delivery
OPERATOR_EMAIL=[email protected]
LETTERMINT_API_TOKEN=your-lettermint-api-token
LETTERMINT_FROM_EMAIL=[email protected]
# FreeScout (optional)
FREESCOUT_API_KEY=your-freescout-key
FREESCOUT_URL=https://your-freescout-instance.com
# Nikki (optional)
NIKKI_API_KEY=your-nikki-key
NIKKI_URL=https://nikki-online.nl
# Debug (optional)
DEBUG_LOG=false

The host volume that stores .env must provide encryption at rest. Restrict the file itself to the sync service account as well:

Terminal window
chmod 600 /home/rondo/.env

The cron installer applies umask 077 and enforces this mode automatically. Cached browser sessions are stored with mode 0600 in a mode-0700 directory.

Run a test download to confirm Sportlink credentials work:

Terminal window
node steps/download-data-from-sportlink.js --verbose

This should:

  1. Launch headless Chromium
  2. Log into Sportlink Club
  3. Handle TOTP 2FA
  4. Download member data
  5. Save results to data/laposta-sync.sqlite

If it fails, check:

  • Credentials in .env are correct
  • TOTP secret is the base32 secret (not the QR code URL)
  • Chromium dependencies are installed (step 2)

Run a full sync to populate all databases and downstream systems:

Terminal window
# Run the full sync (all pipelines)
node pipelines/sync-all.js --verbose

This takes a while on first run because:

  • All members need to be created in Rondo Club (not just updated)
  • All teams and commissies need to be created
  • All photos need to be downloaded and uploaded
  • All member birthdates need to be synced

Subsequent runs are much faster due to hash-based change detection.

Set up automated scheduling:

Terminal window
npm run install-cron

Run the installer only on the sync server as rondo. It prompts for missing Lettermint configuration, imports recognized existing pipeline timings, preserves unrelated cron jobs and existing UI edits, and writes a private crontab backup. The ten editable jobs become one minute dispatcher; Twelve retains its separate hourly check. Unknown or ambiguous legacy entries stop migration.

Manage timings and enable switches through Beheer → Sync schedules. See Editable Sync Schedules for defaults, validation, migration and DST behavior; crontab -l now shows the dispatcher rather than each pipeline’s timing.

/home/rondo/
├── .env # Credentials (not in git)
├── data/ # SQLite databases (created on first run)
│ ├── laposta-sync.sqlite # Laposta sync state
│ ├── rondo-sync.sqlite # Rondo Club sync state
│ ├── nikki-sync.sqlite # Nikki sync state
│ └── freescout-sync.sqlite # FreeScout sync state
├── photos/ # Downloaded member photos
├── logs/ # Sync logs
│ └── cron/ # Cron-specific logs
├── .sync-*.lock # Flock lock files (per sync type)
├── node_modules/ # Dependencies
├── lib/ # Shared modules
├── scripts/ # Shell scripts and utilities
├── docs/ # Documentation
└── *.js # Sync scripts

To deploy code updates:

Terminal window
cd /home/rondo
git pull
npm install # Only needed if dependencies changed

The Rondo Club site needs:

  • ACF Pro plugin for custom fields and REST API integration
  • Rondo Club theme with custom post types: person, team, commissie, discipline_case
  • REST API enabled with show_in_rest on all custom post types and ACF field groups
  • Application Passwords enabled for the sync user
  • Custom endpoints provided by the Rondo Club theme:
    • GET /wp-json/rondo/v1/people/filtered (for VOG-filtered volunteers)
    • POST /wp-json/rondo/v1/people/{id}/photo (for photo uploads)
    • GET /wp-json/rondo/v1/current-season (for discipline cases)
Error: browserType.launch: Executable doesn't exist

Run npx playwright install chromium to install/reinstall the browser.

  • Ensure SPORTLINK_OTP_SECRET is the base32 secret key, not the full otpauth:// URL
  • Check that the server’s clock is synchronized (TOTP is time-sensitive):
    Terminal window
    timedatectl status

This happens when sync runs from multiple machines (each has its own SQLite database with different rondo_club_id mappings). Always sync from the production server only.

To clean up duplicates:

Terminal window
node tools/delete-duplicates.js --verbose # Dry run
node tools/delete-duplicates.js --apply # Actually delete

Check Lettermint credentials in .env and verify the sender email is verified in the Lettermint dashboard.

If a sync was interrupted (e.g., server restart), the lock file may remain:

Terminal window
rm /home/rondo/.sync-people.lock # Remove stale lock