Sylo-Fluxer

Sylo-Fluxer

Docker app from Ferdinand99's Repository

Overview

BETA: Sylo-Fluxer runs on Fluxer and every module is ported, but it started life as a Discord bot and some features don't work as expected yet - see "Beta status" in the project README before relying on it. Sylo-Fluxer is a multi-function bot for Fluxer (https://fluxer.app) with a web dashboard - a port of Sylo. It has a per-community module system: moderation with warning thresholds and a numbered case log, server logging, tickets/modmail answered from the dashboard, reaction roles & autoroles, verification (react to verify, or a Cloudflare Turnstile captcha), welcome/leave messages, birthdays, sticky messages, auto-moderation, counting, custom commands, an autoresponder, reminders, leveling (text + voice XP), AFK, server statistics, server insights, free games, ban appeals, temporary voice channels, a starboard, an invite tracker, polls, giveaways, game stats, Twitch / YouTube / Kick / RSS alerts, scheduled channel cleanup and a message/embed creator. Commands are typed with a prefix (! by default, changeable per community). State is stored in a single SQLite file under /app/data and survives updates; it is snapshotted automatically into /app/data/backups. Setup: in the Fluxer app open User Settings -> Developer -> Applications, create an application, then set FLUXER_TOKEN (Bot token) and FLUXER_CLIENT_ID (Application ID). Invite the bot with https://web.fluxer.app/oauth2/authorize?client_id=YOUR_APPLICATION_ID&scope=bot&permissions=1100469103831 Dashboard: runs open (no login) until you turn on "Log in with Fluxer" - use it only on a trusted LAN until then. To restrict it to community owners / admins, set FLUXER_CLIENT_SECRET (Client secret, same page), DASHBOARD_URL (the URL you open the dashboard on), SESSION_SECRET and OWNER_IDS, and add DASHBOARD_URL/auth/fluxer/callback to the application's Redirect URIs.
Sylo-Fluxer

Sylo-Fluxer

A port of Sylo — the multi-function bot with a MEE6-style web dashboard — to Fluxer. Self-host it with Docker.

Status: beta Built for Fluxer Test Release GHCR Node License: MIT

[!WARNING] Sylo-Fluxer is in beta. It runs on Fluxer and every module has been ported, but it started life as a Discord bot and several features do not work as expected yet. Read Beta status before you rely on it.

Sylo-Fluxer is Sylo rebuilt on the Fluxer SDK (@fluxerjs/core): the same 34 per-community modules, the same web dashboard, and the same data model — with commands typed as !help instead of slash commands, and reactions in place of buttons and menus, because Fluxer has neither. Everything runs in one Node process, one container, with a single SQLite file for state.

Looking for the Discord bot? That's Sylo (sylobot.com). This repository is Fluxer only.

The 34 modules

moderation · logging · tickets · reaction roles · verification · welcome · welcome channel · birthdays · sticky messages · auto-moderation · honeypot · counting · custom commands · autoresponder · auto-react · reminders · leveling · AFK · server statistics · server insights · free games · ban appeals · temporary voice channels · starboard · invite tracker · polls · giveaways · game stats · Twitch alerts · YouTube alerts · Kick alerts · RSS alerts · channel cleanup · GitHub alerts

Contents

Beta status

What is known not to work as expected yet:

  • Temporary voice channels. A move done by a bot drops the member's voice connection on Fluxer (they end up in the call but without a registered voice state), and it never works on the community owner. Sylo therefore creates the channel and sends the member a DM with a link to join it, instead of moving them.
  • Timestamps. Relative times in bot messages use the <t:…> markup; whether every Fluxer client renders it is not confirmed. Set FLUXER_TIMESTAMPS=text for plain UTC times if they show up raw.
  • Most modules haven't been exercised on Fluxer yet. Verification, temporary voice channels, the dashboard and "Log in with Fluxer" have been checked on a live community; the commands and the other modules are covered by the test suite but not yet used for real.
  • The Fluxer SDK is young. It's pinned to an exact version (3.1.0) and Fluxer's API still changes — expect the occasional fix after a Fluxer update.

Found something broken? Open an issue.

How it differs from Sylo

Sylo (Discord) Sylo-Fluxer
Commands Slash commands (/warn) Prefix commands (!warn), prefix configurable per community; mentioning the bot works too
Private replies Ephemeral messages Replied in the channel and deleted after 15 s, or sent by DM (!mydata, !forget, …)
Buttons & menus Role buttons / selects, Verify button, giveaway Enter button, /help menu Reactions (role reactions, ✅ to verify, 🎉 to enter), numbered replies, !help <topic>
Auto-moderation Sylo's filters + optional sync to Discord's native AutoMod Sylo's own filters only (Fluxer has no native AutoMod)
Channel types Text, voice, announcement, forum, stage, threads Text, voice and categories
Gateway intents Server Members / Message Content toggles None — Fluxer has no intents
Hosted instance sylobot.com None yet — self-host only

Several of these are Fluxer gaps rather than design choices. Fluxer's 2026 roadmap plans slash commands, modals, buttons and other components, and interactions (#7), and threads and forums (#6). Sylo-Fluxer keeps its command definitions in the slash-command shape (options, types, choices, default permissions) precisely so it can move to native slash commands — and back to buttons and menus — once Fluxer ships them.

Commands

Commands start with ! by default. Change it per community with !prefix <new> (needs Manage Server), or mention the bot instead of typing a prefix. !help lists every command; !help <command> shows its usage.

Arguments go in order (!warn add @member spamming the chat), and the last text argument takes the rest of the line. Typed arguments — members, channels, roles, numbers, durations like 1d — are recognised wherever they appear, so !ban @member 1d spam and !ban @member spam 1d both work. Any argument can also be given by name: !ban @member delete_messages:1d.

  • General — !help, !ping, !about, !version, !prefix, !stats (Battlefield-series and RuneScape player lookups, with the Game stats module)
  • Moderation — !kick, !ban, !unban, !timeout, !untimeout, !purge, !slowmode, !lock, !unlock, !lockdown, !warn, !modlog, and a numbered case log with !history @member and !case view|reason|delete|note
  • Community — !rank, !leaderboard, !afk, !birthday, !poll, !poll-end, !giveaway, !freegames, !invites, !inviter, !invites-leaderboard
  • Temporary voice — !voice-claim, !voice-transfer, !voice-rename, !voice-limit, !voice-lock, !voice-unlock, !voice-hide, !voice-reveal, !voice-kick, !voice-ban, !voice-unban, !voice-owner, !voice-clean
  • Privacy — !mydata (DMs you a JSON copy of your data) and !forget (deletes it)

Moderation commands check the member's permissions when they're run. Every command can be disabled or limited to channels / roles per community from the dashboard.

Features

  • Moderation — warning thresholds that auto-timeout / kick / ban, a numbered case log, role-hierarchy checks, optional DM to the target, a ban manager.
  • Auto-moderation — bad words, repeated text, invite links (Fluxer and Discord), links, caps, emojis, spoilers, mentions, zalgo and anti-spam, each Disabled / Delete / Delete + Warn / Delete + Timeout; immunity roles.
  • Honeypot — trap channels and trap messages that instantly punish raid bots and scrapers.
  • Verification — members react ✅ on the verify message to get a role, or pass a Cloudflare Turnstile captcha on the dashboard; optional auto-kick of members who don't verify.
  • Reaction roles & autoroles — dashboard-built role messages; members react to pick roles (exclusive and reverse modes), and roles are given on join.
  • Welcome, welcome channel, birthdays, sticky messages, counting, AFK, autoresponder, auto-react, reminders — as in Sylo.
  • Custom commands — !name commands built from an ordered list of actions: reply (text or embed, or a random pick), post to another channel, add or remove a role; per-command role / channel limits and a cooldown.
  • Leveling — XP for messages and time in voice, multipliers, level-up announcements, role rewards, rank and leaderboard image cards, and a public web leaderboard.
  • Tickets (modmail) — members DM the bot; staff read and answer from the dashboard.
  • Temporary voice channels — "join to create" hubs with name templates, limits, permission sync, role gating and an optional text channel, controlled with the !voice-* commands.
  • Starboard, polls, giveaways, invite tracker, server statistics, server insights, ban appeals, channel cleanup.
  • Alerts — Twitch, YouTube, Kick, RSS / Atom (plus Reddit, Mastodon and Bluesky), free games (Epic, plus more stores with an IsThereAnyDeal key) and GitHub webhooks.
  • Web dashboard — Express + EJS with htmx and Alpine (no build step): a plugin grid, a settings panel per module, a Bot Personalizer, an embed builder, an audit log and a Health page. A new React dashboard (V2, beta) lives at /v2.
  • Operations — GET /health (JSON), GET /metrics (Prometheus), automatic SQLite snapshots with download / import / restore from the Health page, optional off-site copies (WebDAV and/or a webhook), a dev-log channel for the bot's own errors (DEV_LOG_CHANNEL_ID), and automatic removal of a community's data when the bot leaves it.

Self-hosting

1. Create the Fluxer application

In the Fluxer app open User Settings → Developer → Applications and create an application. You need:

Variable Where
FLUXER_TOKEN Secrets & tokens → Bot token
FLUXER_CLIENT_ID Application ID, at the top of the page

Invite the bot to your community with (replace the id):

https://web.fluxer.app/oauth2/authorize?client_id=YOUR_APPLICATION_ID&scope=bot&permissions=1100469103831

That permission set covers moderation, roles, channels, messages, moving members and timeouts. Put the bot's role above the roles it should manage.

Every other variable is optional — see .env.example. A self-hosted Fluxer instance is supported via FLUXER_API_URL / FLUXER_WEB_URL.

2. Run it

With Docker Compose:

cp .env.example .env      # set FLUXER_TOKEN and FLUXER_CLIENT_ID
docker compose up -d --build

The dashboard listens on port 3000. Until you set up dashboard login it runs in open mode — no login, full access for anyone who can reach it — so keep it on localhost or a trusted LAN.

Or run the prebuilt multi-arch image (linux/amd64 + linux/arm64) with the same .env and a volume for /app/data:

Tag What it is
ghcr.io/ferdinand99/sylo-fluxer:latest, :X.Y.Z, :X.Y Releases
ghcr.io/ferdinand99/sylo-fluxer:main, :sha-<short> Rolling build of main

3. Dashboard login

"Log in with Fluxer" restricts the dashboard to people who own or manage (Administrator or Manage Server) a community the bot is in. Set it up before you expose the dashboard beyond your LAN:

  1. In the same Fluxer application, copy Secrets & tokens → Client secret.
  2. Add a Redirect URI: <your dashboard URL>/auth/fluxer/callback, e.g. https://sylo.example.com/auth/fluxer/callback. It must match exactly.
  3. Set these in .env and restart:
Variable Value
FLUXER_CLIENT_SECRET The client secret — turns login on
DASHBOARD_URL The public URL, e.g. https://sylo.example.com (behind a reverse proxy this also makes the session cookie Secure)
SESSION_SECRET Any long random string, so sessions survive restarts (openssl rand -hex 32)
OWNER_IDS Your Fluxer user id(s) — only they can open the Health page and its backups

Unraid

The Unraid template lives in Ferdinand99/unraid-templates together with Sylo's. Search for Sylo-Fluxer in Apps (Community Applications); until it's listed there, add https://github.com/Ferdinand99/unraid-templates under Docker → Template repositories and pick it from Add Container. Keep the data directory on a real local disk (e.g. /mnt/cache/appdata/sylo-fluxer), not /mnt/user — SQLite in WAL mode needs working file locks. If you also run the Discord Sylo on the same server, the template already uses different names, paths and ports.

The longer guide in docs/self-hosting.md covers every variable, a self-hosted Fluxer instance, the reverse proxy, backups, upgrades and troubleshooting.

Local development

Requires Node.js 22+ and a Fluxer application (see above).

git clone https://github.com/Ferdinand99/Sylo-Fluxer.git
cd Sylo-Fluxer
npm install
cp .env.example .env     # set FLUXER_TOKEN and FLUXER_CLIENT_ID
npm test
npm run dev              # restarts on file changes

Only one process may use a bot token at a time — stop any other instance first, or the bot answers every command twice.

Project structure

src/
  index.js              Entrypoint — boots DB, bot and web in one process
  config.js             Loads / validates env vars
  runtime.js            Shared in-memory state (uptime, errors, client)
  platform/             Everything Fluxer-specific in one place:
    compat.js             discord.js-shaped aliases over the Fluxer SDK (+ REST fixes)
    voiceStates.js        voice-state tracking (the SDK keeps none)
    channels.js           fetch / create / delete guild channels
    channelTypes.js permissions.js urls.js time.js mentions.js snowflake.js
  bot/
    index.js            Fluxer client bootstrap
    framework/          prefix commands: CommandBuilder, parser, resolve,
                        MessageInteraction (interaction-shaped adapter), router, usage
    commands/           one file per command (`data` + `execute`)
    events/             ready, messageCreate (router), moduleEvents (gateway → modules),
                        dmTickets (DM → ticket), guildCreate, guildDelete (purge on leave)
    lib/ embeds/        moderation, modlog, custom commands, cards, embeds
  modules/              registry, dispatch, and one file per module
  adapters/games/       game-stats adapters (Battlefield, RuneScape)
  db/                   SQLite (or Postgres) access + migrations, one file per table group
  web/                  Express app: routes, middleware, views (EJS), public assets
web-v2/                 the V2 dashboard (React + Vite), served at /v2
test/                   node --test suite

Releases & CI

  • test.yml — lint, the test suite against SQLite and Postgres, a syntax check and an EJS compile check. Gates every image build.
  • docker-publish.yml — every push to main publishes ghcr.io/ferdinand99/sylo-fluxer:main and :sha-<short>; pull requests only build.
  • release-please.yml — keeps a release PR open from Conventional Commits (feat:, fix:, …). Merging it tags vX.Y.Z, writes the GitHub Release and CHANGELOG.md, and publishes :latest, :X.Y.Z and :X.Y to GHCR.

All publishing uses the built-in GITHUB_TOKEN; no secrets are needed. release-please reads commit messages on main, so when squash-merging a pull request, make sure the commit message is the Conventional-Commit PR title.

Tests

npm test                 # SQLite
DATABASE_URL=postgres://user:pass@host:5432/db npm test   # + the Postgres tests

The node:test suite covers the prefix-command parser and router, the Fluxer compatibility layer, module logic, every database table on both drivers, and the dashboard over HTTP. No network: external APIs are stubbed and DB tests use a throwaway database.

Adding another game

  1. Create src/adapters/games/<game>.js exporting an adapter with id, titles(), platformsFor(title) and getPlayerStats(username, platform, { title }) — throw the typed errors from gameAdapter.js for failures.
  2. Register it in src/adapters/games/index.js.
  3. Add a game choice (<adapter>:<title>) to GAME_CHOICES in src/bot/commands/stats.js, and an embed builder if the stats differ.

Legal

The privacy policy and terms of service in this repository cover the instances Ferdinand99 operates. If you run your own instance you are its operator — publish your own.

License

MIT © Ferdinand99. Sylo-Fluxer is a port of Sylo.

Requirements

A Fluxer application with a bot (User Settings -> Developer -> Applications in the Fluxer app): its Bot token and Application ID.

Related apps

Details

Repository
ghcr.io/ferdinand99/sylo-fluxer:latest
Last Updated2026-10-07
First Seen2026-10-07

Runtime arguments

Web UI
http://[IP]:[PORT:3000]/
Network
bridge
Shell
sh
Privileged
false

Template configuration

WebUI PortPorttcp

Host port for the Sylo dashboard. Only change the host side; the container always listens on 3000.

Target
3000
Default
3000
Value
3000
Data DirectoryPathrw

Where the SQLite database (sylo.db) is stored. Persists across updates/restarts.

Target
/app/data
Default
/mnt/user/appdata/sylo-fluxer/data
Value
/mnt/user/appdata/sylo-fluxer/data
FLUXER_TOKENVariable

Fluxer bot token (User Settings -> Developer -> Applications -> your app -> Bot token).

FLUXER_CLIENT_IDVariable

Fluxer Application ID (top of the same page).

FLUXER_SHARD_COUNTVariable

Optional. Number of gateway shards this one container runs. Leave blank (or 'auto') - it stays at 1 until the bot is in ~2,500+ communities. Multi-process sharding is not supported.

FLUXER_CLIENT_SECRETVariable

Optional. Set this (same page -> Client secret) to require 'Log in with Fluxer' on the dashboard, restricted to community admins. Also add http://[UNRAID-IP]:[HOST-PORT]/auth/fluxer/callback to the application's Redirect URIs. Leave blank to run the dashboard open (trusted LAN only).

FLUXER_API_URLVariable

Optional, self-hosted Fluxer only. Public API origin of your instance. Leave blank for hosted Fluxer (fluxer.app).

FLUXER_WEB_URLVariable

Optional, self-hosted Fluxer only. Web app origin of your instance. Leave blank for hosted Fluxer.

DASHBOARD_URLVariable

Optional. The public dashboard URL (e.g. https://sylo.example.com or http://[UNRAID-IP]:[HOST-PORT]). Needed behind a reverse proxy for the OAuth2 redirect URI, and to build the links members open for verification-captcha and ban appeals. Leave blank for direct IP access without those features.

SESSION_SECRETVariable

Recommended with FLUXER_CLIENT_SECRET. Any long random string (e.g. from 'openssl rand -hex 32') that signs dashboard sessions. If blank a random one is generated at start, so everyone is logged out on every restart.

OWNER_IDSVariable

Recommended with FLUXER_CLIENT_SECRET. Your Fluxer user id(s), comma-separated. Only these accounts can open the Health page (status, error log, database backup / restore) once login is on.

DEV_LOG_CHANNEL_IDVariable

Optional. A Fluxer channel id where Sylo posts its own errors and warnings. Sylo must be in that community and able to send embeds there.

FLUXER_TIMESTAMPSVariable

How times render in bot messages: native (the t:... markup, shown in each reader's timezone) or text (plain UTC). Use text if times show up as raw markup.

Default
native
Value
native
TURNSTILE_SITE_KEYVariable

Optional. Cloudflare Turnstile site key — enables the Verification module's captcha mode. Free at dash.cloudflare.com -&gt; Turnstile. Needs both keys and DASHBOARD_URL set; otherwise captcha mode falls back to reacting with ✅ to verify.

TURNSTILE_SECRET_KEYVariable

Optional. Cloudflare Turnstile secret key — pairs with TURNSTILE_SITE_KEY for the Verification captcha.

ITAD_API_KEYVariable

Optional. IsThereAnyDeal API key (free at isthereanydeal.com/apps) — broadens the Free games module beyond the Epic Games Store to Steam, GOG, Fanatical, Humble and more. Without it, Free games is Epic-only.

TWITCH_CLIENT_IDVariable

Optional. Twitch application client id for the Twitch alerts module ('go live' notifications). Free app at dev.twitch.tv/console. Needs TWITCH_CLIENT_SECRET too; without both, the module's poll loop does nothing.

TWITCH_CLIENT_SECRETVariable

Optional. Twitch application client secret, paired with TWITCH_CLIENT_ID for the Twitch alerts module.

KICK_CLIENT_IDVariable

Optional. Kick application client id for the Kick alerts module ('go live' notifications). Free app at kick.com/settings/developer. Needs KICK_CLIENT_SECRET too; without both, the module's poll loop does nothing.

KICK_CLIENT_SECRETVariable

Optional. Kick application client secret, paired with KICK_CLIENT_ID for the Kick alerts module.

STATS_CACHE_TTL_MINUTESVariable

How long (minutes) to cache a stats lookup before hitting the API again.

Default
5
Value
5
GAMETOOLS_API_BASEVariable

Base URL of the Battlefield stats API.

Default
https://api.gametools.network
Value
https://api.gametools.network
DATABASE_PATHVariable

Path to the SQLite file inside the container. Must sit inside the Data Directory mount.

Default
/app/data/sylo.db
Value
/app/data/sylo.db
BACKUP_INTERVAL_HOURSVariable

How often (hours) to write an automatic compacted database snapshot into Data Directory/backups. A snapshot is also always taken just before a schema migration, and on demand from the Health page. Set to 0 to disable the scheduled backup.

Default
24
Value
24
BACKUP_RETENTIONVariable

How many database snapshots to keep in Data Directory/backups. Older ones are pruned automatically.

Default
14
Value
14
BACKUP_DIRVariable

Optional. Directory (inside the container) for database snapshots. Leave blank to use Data Directory/backups; must sit inside the Data Directory mount if set.

BACKUP_WEBDAV_URLVariable

Optional. Off-site backups: a WebDAV folder URL (e.g. a Nextcloud path like https://cloud.example.com/remote.php/dav/files/me/sylo-backups). After every local snapshot a gzipped copy is PUT to url/name.db.gz. Best-effort and logged; never blocks the local backup.

BACKUP_WEBDAV_USERVariable

Optional. Username for BACKUP_WEBDAV_URL (HTTP basic auth). Use an app password, not your account password.

BACKUP_WEBDAV_PASSVariable

Optional. Password / app password for BACKUP_WEBDAV_URL.

BACKUP_WEBHOOK_URLVariable

Optional. Off-site backups: a webhook URL. Each gzipped snapshot is uploaded as an attachment (skipped when it is over ~8 MiB — use WebDAV for a larger database).

WEB_PORTVariable

Port the app listens on inside the container. Leave at 3000 unless you also change the container side of the WebUI Port mapping.

Default
3000
Value
3000
NODE_ENVVariable

Node environment.

Default
production
Value
production
LOG_LEVELVariable

Log verbosity: debug, info, warn or error. debug also emits one line per HTTP request.

Default
info
Value
info
LOG_FORMATVariable

Log line format: text or json (LOG_JSON=1 is shorthand for json).

Default
text
Value
text
TZVariable

IANA timezone name, used for timestamps in ticket transcripts and logs. Defaults to Europe/Oslo.

Default
Europe/Oslo
Value
Europe/Oslo