IO-Darts-Agent: Installation
This guide installs IO-Darts-Agent from source, on a normal computer or on a dedicated board PC next to the dartboard.
Unofficial project. IO-Darts-Agent is not affiliated with, endorsed by or supported by Autodarts.
Local network only. The hub always listens on all network interfaces (
0.0.0.0) and stores your Autodarts password in plain text in its config file. Use it in your home network only. Do not forward or expose the port to the internet.
Contents
- Requirements
- Install from source
- First configuration
- Where the hub stores its data
- Dedicated board PC (Linux)
- Updating
- Logs
- Troubleshooting
- Uninstall
- Desktop packages
Requirements
| What | Notes |
|---|---|
| Node.js 22 or newer | Required. package.json declares "engines": { "node": ">=22" } and the repository contains a .nvmrc with 22. |
| npm | Comes with Node.js. |
| git | To clone and update the repository. |
| ffmpeg | Optional. The animation generator uses it to turn generated videos into transparent WebM files. Without ffmpeg the original MP4 is kept (no transparency). |
| Autodarts account | With email and password login. The board must already be set up and connected in Autodarts. |
| Board ID | The ID of your Autodarts board. |
About the Autodarts login: The hub logs in with your Autodarts email and password against the Autodarts auth service (https://api.autodarts.com/auth/v1). Access tokens are refreshed automatically; the service rotates refresh tokens, and if a refresh is rejected the hub logs in again. For live events the hub requests a short lived ticket from https://play.ws.autodarts.com/ms/v0/tickets and opens the websocket wss://play.ws.autodarts.com/ms/v0/subscribe with it.
Two-factor authentication is not supported. The hub has no 2FA step, so an account that requires a second factor cannot log in.
Install from source
Installing from source is the supported way to run IO-Darts-Agent.
1. Clone the repository
git clone https://github.com/Castorio/io-darts-agent.git
cd io-darts-agent
If you use nvm, nvm use picks Node 22 from .nvmrc.
2. Install dependencies
npm run setup
npm run setup installs the dependencies of the hub, builds the module SDK and installs the dependencies of modules/wled, modules/darts-caller, web and modules/game-display/web.
3. Build
npm run build:app
npm run build:app builds the hub (TypeScript to dist/), the web UI (web/dist/) and the Game Display UI (modules/game-display/web/dist/).
4. Start
npm start
npm start runs node dist/src/main.js. The console shows Server running at http://localhost:3000. On the first start the hub installs the built-in modules (WLED, Darts Caller, Game Display) as links to the modules in the repository, so module updates arrive with git pull.
Open the web UI in a browser:
http://localhost:3000
From another device in the same network use the IP of the computer running the hub, for example http://192.168.1.50:3000.
First configuration
Setup wizard
As long as no configuration exists, the web UI opens the setup wizard (/setup). Until the setup is finished, the API only answers the setup routes. The wizard has three steps:
- Web login: optionally protect the web UI with a user name (at least 3 characters) and password (at least 6 characters).
- Autodarts: enter your Autodarts email, password and board ID and test the connection.
- Finish: the hub saves the configuration and connects to Autodarts.
After that the hub logs Configuration loaded - connecting to Autodarts... on every start and connects to your board. You can change the Autodarts credentials, the board ID, the port and the web UI login later under Settings in the web UI. These changes take effect after a restart of the hub: click Restart hub in the server section of the settings. Started with npm start, the hub launches a new copy of itself; as a systemd service or in the desktop app it exits with code 75 and the service or app starts it again. The page reloads once the hub answers, on the new port if you changed it.
Config file
The wizard writes dist/config/hub.config.json. The path is resolved relative to the built code in dist/src/, not relative to the current working directory. The file contains your Autodarts password in plain text, so protect it:
chmod 600 dist/config/hub.config.json
config/hub.config.example.json in the repository shows the format and can serve as a reference. The hub does not read it.
| Field | Meaning |
|---|---|
setupCompleted |
true after the wizard. With false the hub opens the wizard again. |
language |
Language of the UI: en, de or es. |
webAuth |
Optional login for the web UI (enabled, username, passwordHash). Change it under Settings; the hub stores the password as a bcrypt hash. |
autodarts.email, autodarts.password |
Your Autodarts login. |
autodarts.boardId |
The ID of your Autodarts board. |
autodarts.clientId |
Optional. Defaults to autodarts-play. Leave it out. |
server.port |
HTTP port of the hub. Default 3000. |
server.host |
Stored, but currently ignored: the hub always binds to 0.0.0.0. |
developerMode |
Turns on the developer tools in the web UI. |
API keys for AI features
Keys for AI providers (OpenAI, Anthropic, Ollama, ElevenLabs, Stability AI, Runway, Higgsfield, Replicate) are entered in the web UI under the sidebar entry AI Provider (/ai-settings). The hub stores them in dist/config/ai-providers.json and creates that file with defaults on first start. The Darts Caller (ElevenLabs voice) and the animation generator (Runway, Higgsfield) read their keys from there.
Optional: .env file
The hub loads a .env file from the current working directory. For normal operation it is not needed. Variables the server actually evaluates:
| Variable | Effect |
|---|---|
PORT |
Port used only when no config file exists (setup mode). |
IO_DARTS_DATA_DIR |
Folder for runtime data (config/, data/, media/, modules_installed/). Default: dist/. The desktop app sets it to the per-user app data folder. |
AUTODARTS_CLIENT_ID |
Overrides the OAuth client id. Not needed. |
NODE_ENV |
production turns off debug output of the intent bus. It does not affect the session cookie: the cookie gets the Secure flag only for HTTPS requests, so the web login also works over plain HTTP in the LAN. |
.env.example also lists SERVER_PORT, SERVER_HOST, AUTODARTS_EMAIL, AUTODARTS_PASSWORD and AUTODARTS_BOARD_ID. npm start ignores them; the port and the credentials come from dist/config/hub.config.json.
Where the hub stores its data
When started from the build, all runtime data lives below dist/:
| Path | Content |
|---|---|
dist/config/hub.config.json |
Main configuration including the Autodarts password |
dist/config/ai-providers.json |
AI provider settings and API keys |
dist/config/players.json |
Player database |
dist/config/animation-settings.json |
Animation engine choice |
dist/config/modules/ |
Settings of each module |
dist/data/voicepacks/, dist/data/tts-cache/ |
Darts Caller voice packs and speech cache |
dist/media/ |
Media library (animations, sounds, images) |
dist/modules_installed/ |
Installed modules (links to built-in modules and installed external modules) |
The folders config/, data/ and media/ in the repository root are not used at runtime with npm start; npm run dev (development mode) uses them, together with modules_installed/ in the repository root.
dist/ is listed in .gitignore. This keeps your data out of git, but it also means that any command that cleans ignored files deletes your configuration. Make a backup before larger changes:
tar czf ~/io-darts-agent-backup-$(date +%F).tar.gz \
-C ~/io-darts-agent dist/config dist/data dist/media dist/modules_installed
Dedicated board PC (Linux)
This section sets up a small Linux PC (for example Linux Mint or Ubuntu with Xfce) that starts the hub at boot and shows the Game Display full screen on a monitor next to the board. The repository contains the needed files in scripts/linux/:
For plain desktop use (no kiosk, no autostart) the .deb package from Desktop packages is a quicker alternative. For the kiosk setup with systemd described here, installing from source remains the way to go.
| File | Purpose |
|---|---|
io-darts-agent.service |
systemd user service. Runs /usr/bin/node dist/src/main.js in ~/io-darts-agent with NODE_ENV=production, waits until api.autodarts.com resolves in DNS before the start (the hub does not retry a failed start-up login), restarts on failure. The web login still works over plain HTTP, because the session cookie gets the Secure flag only for HTTPS requests. |
game-display-kiosk.sh |
Turns off screen blanking (X11, plus Xfce power manager and screensaver), waits until the hub answers, then opens Firefox in kiosk mode on http://localhost:3000/module-ui/game-display/. The URL can be changed with GAME_DISPLAY_URL. |
game-display-kiosk.desktop |
Autostart entry that runs the kiosk script after desktop login. |
All commands below run as your normal user, except where sudo is shown.
1. Install system packages
sudo apt update
sudo apt install -y git curl ffmpeg firefox unclutter-xfixes
unclutter-xfixes hides the mouse pointer on the display. On Ubuntu firefox may be installed as a snap. The kiosk script only needs a firefox command.
2. Install Node.js 22
If Node.js 22 or newer is already installed at /usr/bin/node (node -v), skip this step.
Option A, NodeSource (recommended for the service): installs node to /usr/bin/node, which is the path the service file uses.
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
node -v
Option B, nvm: works too, but node then lives below ~/.nvm/. In that case replace /usr/bin/node in the service file with the output of which node.
3. Install the hub
The service expects the hub in ~/io-darts-agent:
cd ~
git clone https://github.com/Castorio/io-darts-agent.git io-darts-agent
cd ~/io-darts-agent
Then build and test once by hand:
npm run setup
npm run build:app
npm start
Open http://localhost:3000, complete the setup wizard, then stop the hub with Ctrl+C.
4. Install the systemd user service
mkdir -p ~/.config/systemd/user
cp ~/io-darts-agent/scripts/linux/io-darts-agent.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now io-darts-agent
systemctl --user status io-darts-agent
So the service starts at boot even before anyone logs in:
sudo loginctl enable-linger "$USER"
5. Install the Game Display kiosk
mkdir -p ~/.local/bin ~/.config/autostart
cp ~/io-darts-agent/scripts/linux/game-display-kiosk.sh ~/.local/bin/
chmod +x ~/.local/bin/game-display-kiosk.sh
cp ~/io-darts-agent/scripts/linux/game-display-kiosk.desktop ~/.config/autostart/
The script uses its own Firefox profile in ~/.local/share/game-display-kiosk, so your normal Firefox profile stays untouched. To leave kiosk mode press Alt+F4.
If the hub runs on another port or you want a different page, set GAME_DISPLAY_URL in the Exec line of ~/.config/autostart/game-display-kiosk.desktop:
Exec=env GAME_DISPLAY_URL=http://localhost:3001/module-ui/game-display/ sh -c '"$HOME/.local/bin/game-display-kiosk.sh"'
6. Turn on automatic desktop login
The kiosk starts after a desktop login, so the board PC should log in automatically.
- Linux Mint: open Login Window, go to Users and set automatic login for your user.
- Ubuntu or Xubuntu with LightDM: create
/etc/lightdm/lightdm.conf.d/50-autologin.conf(replaceyour-userwith your user name):
[Seat:*]
autologin-user=your-user
Reboot and check that the hub starts and the Game Display appears.
Updating
Normal update (source install and board PC)
cd ~/io-darts-agent
git pull
npm run setup
npm run build:app
systemctl --user restart io-darts-agent
Without the service, stop the hub with Ctrl+C and run npm start again instead of the last line.
On a board PC set up before the restart button existed, copy the service file once more so the hub runs with IO_DARTS_SUPERVISED=1 (otherwise Restart hub cannot hand the restart to systemd):
cp scripts/linux/io-darts-agent.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user restart io-darts-agent
npm run build:app first removes the old compiled code in dist/src and dist/modules, then builds into dist/, web/dist/ and modules/game-display/web/dist/. Your data in dist/config, dist/data, dist/media and dist/modules_installed stays in place.
What you must not do
- Never delete
dist/(for examplerm -rf dist) and never rungit clean -xorgit clean -X.dist/is ignored by git and holds your configuration and data. - Never sync the whole directory with
rsync --deletefrom another computer. The target'sdist/configand the other runtime folders do not exist on the source, so--deleteremoves the runtime configuration. If you deploy with rsync, exclude the runtime folders:
rsync -a --delete \
--exclude node_modules --exclude dist \
./ board-pc:io-darts-agent/
Logs
With the systemd service:
journalctl --user -u io-darts-agent -f
Without the service the log goes to the terminal running npm start. The web UI also shows a log view under Console.
Troubleshooting
Port 3000 is already in use
The hub fails to start with an "address already in use" error, and the service keeps restarting.
ss -ltnp | grep 3000
Stop the other program or choose another port: set server.port in dist/config/hub.config.json (or under Settings in the web UI) and restart the hub. On the board PC also adjust the kiosk URL (GAME_DISPLAY_URL, see above).
Autodarts login fails
Login failed: invalid email or password: checkautodarts.emailandautodarts.passwordindist/config/hub.config.jsonor under Settings. Log in once at Autodarts in a browser with the same data.- The account uses two-factor authentication: not supported.
- No network or DNS at boot: the service waits until
api.autodarts.comresolves. Check withgetent hosts api.autodarts.com. The hub does not retry a failed start-up login by itself; after fixing the network runsystemctl --user restart io-darts-agent. - After changing credentials or the board ID in the web UI, restart the hub.
The Game Display stays black or shows nothing
- The kiosk script waits until the hub answers on the display URL. If Firefox does not open at all, the hub is not reachable: check
systemctl --user status io-darts-agentand the logs. - Open
http://localhost:3000/module-ui/game-display/by hand.Module UI not foundmeans the Game Display UI was not built: runnpm run setupandnpm run build:app. If the setup wizard was never finished, the display gets no data: openhttp://localhost:3000and complete it. - If the module list in the web UI is empty, check the log for
Installed built-in moduleorBuilt-in module not found. The hub installs the built-in modules only whendist/modules_installed/is empty. - A black area around animations instead of transparency means ffmpeg is missing:
sudo apt install ffmpeg. Without ffmpeg generated animations are stored as MP4 without transparency.
The Darts Caller reports that ElevenLabs is not configured
The Darts Caller takes the ElevenLabs API key from the AI settings. Open AI Settings (/ai-settings) in the web UI, enter the key for ElevenLabs, enable the provider, then restart the Darts Caller module (or the hub).
Uninstall
On the board PC:
systemctl --user disable --now io-darts-agent
rm ~/.config/systemd/user/io-darts-agent.service
systemctl --user daemon-reload
rm ~/.local/bin/game-display-kiosk.sh ~/.config/autostart/game-display-kiosk.desktop
rm -rf ~/.local/share/game-display-kiosk
sudo loginctl disable-linger "$USER" # only if nothing else needs it
Skip disable-linger if other user services should run without a login. This matters in particular for the Autodarts service autodarts.service on the same PC: without linger it only starts after someone logs in. Check with systemctl --user list-unit-files --state=enabled.
Remove the hub itself (this deletes the configuration and all data, make a backup first if needed):
rm -rf ~/io-darts-agent
Remove /etc/lightdm/lightdm.conf.d/50-autologin.conf or turn off automatic login in Login Window if you no longer want it. Node.js, ffmpeg and Firefox can be removed with apt if nothing else uses them.
On other systems stop the hub and delete the cloned directory.
Desktop packages
The Electron desktop app bundles the hub, the web UI and the built-in modules and needs no separate Node.js installation on the target machine.
Download
Ready-made packages for macOS, Windows and Linux are on the Releases page (opens in new tab). Each release contains:
| System | File |
|---|---|
| macOS, Apple Silicon (M1/M2/M3/…) | IO-Darts-Agent-<version>-arm64.dmg or .zip |
| macOS, Intel | IO-Darts-Agent-<version>-x64.dmg or .zip |
| Windows x64 | NSIS installer IO-Darts-Agent-<version>-x64-setup.exe or portable IO-Darts-Agent-<version>-x64-portable.exe |
| Linux x64 (desktop) | IO-Darts-Agent-<version>-x64.deb, .rpm or .AppImage |
| Linux arm64 (e.g. Raspberry Pi) | IO-Darts-Agent-<version>-arm64.deb or .AppImage |
File names follow the pattern IO-Darts-Agent-<version>-<arch>.<extension> throughout, with two exceptions on Windows: the installer and the portable build add -setup or -portable before the extension so the two don't collide. For version 1.0.0, for example:
IO-Darts-Agent-1.0.0-arm64.dmg,IO-Darts-Agent-1.0.0-x64.zipIO-Darts-Agent-1.0.0-x64-setup.exe,IO-Darts-Agent-1.0.0-x64-portable.exeIO-Darts-Agent-1.0.0-x64.deb,IO-Darts-Agent-1.0.0-x64.rpm,IO-Darts-Agent-1.0.0-arm64.AppImage
See the exact names in that release's assets.
Each release also includes SHA256SUMS.txt with checksums for every file. Download it into the same folder as the package and verify:
# Linux
sha256sum -c SHA256SUMS.txt --ignore-missing
# macOS
shasum -a 256 -c SHA256SUMS.txt --ignore-missing
Running unsigned builds
The packages are unsigned, with one exception: the macOS arm64 packages (.dmg/.zip) are signed ad hoc, but not notarized. The macOS x64 packages are completely unsigned. The OS warns on first start:
- macOS: right click the app, choose Open, confirm the dialog. Or in a terminal:
xattr -dr com.apple.quarantine /Applications/IO-Darts-Agent.app. - Windows: SmartScreen shows "Windows protected your PC". Click More info, then Run anyway.
- Linux,
.deb:sudo apt install ./<file>.deb(resolves dependencies automatically). - Linux, AppImage: make it executable and run it:
chmod +x IO-Darts-Agent-*.AppImage && ./IO-Darts-Agent-*.AppImage.
What the app does
The desktop app starts the hub in the background, opens the web UI in its own window and keeps a tray icon. Closing the window keeps the hub running; Quit in the tray menu stops it. The app does not write into its install folder. Configuration, data, media and installed modules live in the per-user app data folder:
| System | Data folder |
|---|---|
| macOS | ~/Library/Application Support/io-darts-agent/hub |
| Windows | %APPDATA%\io-darts-agent\hub |
| Linux | ~/.config/io-darts-agent/hub |
The app uses port 3000. If that port is taken, start it with the environment variable IO_DARTS_PORT set to another port. The setup wizard stores the port the hub runs on. If you later change the port in the settings and restart the hub, the app follows it automatically.
Build it yourself
Instead of downloading, you can build the app from source, on the platform you are building for (Node.js 22 required on the build machine):
npm run setup # install dependencies (root and sub-packages)
npm run build:app # compile the backend, the web UI and the Game Display UI
npm run dist:mac # or: npm run dist:linux / npm run dist:win
The packages end up in releases/. npm run dist:* runs build:app again by itself.
Creating a release (for maintainers)
- Set the version:
npm version X.Y.Z --no-git-tag-version, then commitpackage.json(andpackage-lock.json). - Push the tag
vX.Y.Z: by hand withgit tag vX.Y.Z && git push origin vX.Y.Z, or withscripts/create-release.sh X.Y.Z, which sets the version, commits, tags and pushes. - The Release workflow (
.github/workflows/release.yml) runs: checks the tag against thepackage.jsonversion, runs lint and tests, builds the packages for macOS, Windows and Linux on GitHub runners, and smoke-tests each packaged build (macOS, Windows, Linux x64). - Once it finishes, the workflow creates a draft on the Releases page (opens in new tab) with every package,
SHA256SUMS.txtand auto-generated notes. Check the draft (all files present, checksums, notes make sense), then publish it.
A workflow_dispatch run started manually from the Actions tab builds everything as a trial, without creating a release — useful to check the pipeline without pushing a new tag.