All apps · 0 apps
Sylo-Fluxer
Docker app from Ferdinand99's Repository
Overview
Readme
View on GitHub
Sylo-Fluxer
A port of Sylo — the multi-function bot with a MEE6-style web dashboard — to Fluxer. Self-host it with Docker.
[!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 · How it differs from Sylo
- Commands · Features
- Self-hosting · Local development
- Project structure · Releases & CI · Tests
- Adding another game · Legal · License
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. SetFLUXER_TIMESTAMPS=textfor 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 @memberand!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 —
!namecommands 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:
- In the same Fluxer application, copy Secrets & tokens → Client secret.
- Add a Redirect URI:
<your dashboard URL>/auth/fluxer/callback, e.g.https://sylo.example.com/auth/fluxer/callback. It must match exactly. - Set these in
.envand 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 tomainpublishesghcr.io/ferdinand99/sylo-fluxer:mainand:sha-<short>; pull requests only build.release-please.yml— keeps a release PR open from Conventional Commits (feat:,fix:, …). Merging it tagsvX.Y.Z, writes the GitHub Release and CHANGELOG.md, and publishes:latest,:X.Y.Zand:X.Yto 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
- Create
src/adapters/games/<game>.jsexporting an adapter withid,titles(),platformsFor(title)andgetPlayerStats(username, platform, { title })— throw the typed errors fromgameAdapter.jsfor failures. - Register it in
src/adapters/games/index.js. - Add a
gamechoice (<adapter>:<title>) toGAME_CHOICESinsrc/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
Categories
Related apps
Explore more like this
Explore allDetails
ghcr.io/ferdinand99/sylo-fluxer:latestRuntime arguments
- Web UI
http://[IP]:[PORT:3000]/- Network
bridge- Shell
sh- Privileged
- false
Template configuration
Host port for the Sylo dashboard. Only change the host side; the container always listens on 3000.
- Target
- 3000
- Default
- 3000
- Value
- 3000
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 bot token (User Settings -> Developer -> Applications -> your app -> Bot token).
Fluxer Application ID (top of the same page).
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.
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).
Optional, self-hosted Fluxer only. Public API origin of your instance. Leave blank for hosted Fluxer (fluxer.app).
Optional, self-hosted Fluxer only. Web app origin of your instance. Leave blank for hosted Fluxer.
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.
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.
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.
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.
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
Optional. Cloudflare Turnstile site key — enables the Verification module's captcha mode. Free at dash.cloudflare.com -> Turnstile. Needs both keys and DASHBOARD_URL set; otherwise captcha mode falls back to reacting with ✅ to verify.
Optional. Cloudflare Turnstile secret key — pairs with TURNSTILE_SITE_KEY for the Verification captcha.
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.
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.
Optional. Twitch application client secret, paired with TWITCH_CLIENT_ID for the Twitch alerts module.
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.
Optional. Kick application client secret, paired with KICK_CLIENT_ID for the Kick alerts module.
How long (minutes) to cache a stats lookup before hitting the API again.
- Default
- 5
- Value
- 5
Base URL of the Battlefield stats API.
- Default
- https://api.gametools.network
- Value
- https://api.gametools.network
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
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
How many database snapshots to keep in Data Directory/backups. Older ones are pruned automatically.
- Default
- 14
- Value
- 14
Optional. Directory (inside the container) for database snapshots. Leave blank to use Data Directory/backups; must sit inside the Data Directory mount if set.
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.
Optional. Username for BACKUP_WEBDAV_URL (HTTP basic auth). Use an app password, not your account password.
Optional. Password / app password for BACKUP_WEBDAV_URL.
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).
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 environment.
- Default
- production
- Value
- production
Log verbosity: debug, info, warn or error. debug also emits one line per HTTP request.
- Default
- info
- Value
- info
Log line format: text or json (LOG_JSON=1 is shorthand for json).
- Default
- text
- Value
- text
IANA timezone name, used for timestamps in ticket transcripts and logs. Defaults to Europe/Oslo.
- Default
- Europe/Oslo
- Value
- Europe/Oslo