An open-source, multi-tenant Twitch bot platform. Run one deployment and serve unlimited channels — every streamer gets a fully isolated bot: custom commands, a visual pipeline engine, moderation, channel-point rewards, timers, an economy, text-to-speech, song requests, OBS overlays, and integrations (Spotify, Discord, YouTube).
You run NomNomzBot through its dashboard app, which gives you a guided setup — it connects your Twitch and bot accounts and collects everything the bot needs through proper input fields, so you never edit a config file. The bot itself is self-hostable from day one (point the dashboard at your own server — zero external infrastructure or oversight), and the same codebase powers an optional hosted (SaaS) offering. Under the hood it speaks Twitch over Helix, EventSub (WebSocket), and IRC, and exposes a versioned REST API plus real-time SignalR feeds.
- Project status
- Features
- How it works
- Getting started
- Using NomNomzBot
- Configuration
- Production deployment
- Development
- Contributing
- Security
- License
Pre-1.0, under active development. Honest snapshot:
- Backend (
server/) — the live, supported component. Built and covered by tests across identity/auth, the Twitch Helix + EventSub integration, moderation, channel-point rewards, the pipeline engine, timers, the economy, TTS, roles & permissions, server-side chat decoration (emotes, badges, cheermotes), webhooks, and the sandboxed custom-code action. - Public surfaces (overlays + song-request) — delivered by the widget system: widgets are compiled from source at build time, served by the bot, and CDN-cached for SaaS (not static files). In development.
- Dashboard (
app/) — the Kotlin Multiplatform + Compose client (one codebase for desktop and web) that provides the guided setup described below is in active development. Until it ships, the same onboarding flow runs through the bot's self-describing REST API (interactive docs at/scalar).
It has not yet been validated in a long-running production deployment, so treat it as early software.
What a connected channel gets:
- Commands — custom and built-in chat commands, each backed by a visual pipeline of actions and
conditions (send/reply, timeout/ban, shoutout, set-variable, wait, play music, and more), with 90+
template variables (
{{user.name}},{{stream.uptime}},{{args.1}}, …). - Event responses & timers — automatic reactions to follows, subs, gifts, cheers, raids, and redemptions; scheduled recurring messages.
- Moderation — bans and timeouts, AutoMod settings, blocked-term lists, Shield Mode, and a mod-action log — all mirrored from the real Twitch API.
- Channel points & live ops — custom reward management, polls, and predictions.
- Economy & games — a per-channel currency with an atomic ledger, a store, cross-channel savings jars, leaderboards, and fun-money games (with an optional, off-by-default 18+ gate).
- Music & song requests — viewer song requests (Spotify / YouTube) with a fair, rank-based queue and trust scoring, plus a public request page that needs no login.
- Rich chat — server-side message decoration: Twitch and BTTV / FFZ / 7TV emotes, badge and cheermote images, coloured mentions, and opt-in link previews, emitted ready-to-render to the dashboard.
- Text-to-speech — Azure Cognitive Services and ElevenLabs voices.
- Overlays & widgets — OBS browser-source alerts and a now-playing bar, pushed in real time over SignalR.
- Integrations — Spotify, Discord, and YouTube via OAuth, requested progressively as you enable them.
- Multi-tenant & roles — one deployment, unlimited channels, each isolated; a three-plane authorization
model (community standing, management roles, and per-action
!permitdelegation). - Extensibility — inbound and outbound webhooks, and a hardened, sandboxed custom-code action.
┌─ server/ — .NET 10 backend (the bot)
│ PostgreSQL + Redis · Twitch Helix + EventSub (WebSocket) + IRC · SignalR · Serilog
│ Versioned REST API (v1) · interactive docs at /scalar
│ Public surfaces (OBS overlays, song-request) — compiled widgets served by the bot, CDN-cached for SaaS
│
└─ app/ — Kotlin Multiplatform + Compose dashboard (in active development)
The backend is profile-agnostic and direct-connect: the dashboard just needs the backend URL and talks
REST + SignalR straight to it. Self-host points it at localhost or your own exposed URL; the hosted build
points it at the SaaS API. There is no central broker.
| Layer | Technology |
|---|---|
| Runtime | .NET 10, C# 14 |
| Framework | ASP.NET Core (Asp.Versioning) |
| ORM | EF Core 10 + Npgsql → PostgreSQL 16 |
| Cache / pub-sub | Redis 7 |
| Real-time | ASP.NET SignalR (WebSocket) |
| Auth | JWT + Twitch OAuth (Authorization Code) |
| Twitch | Helix API, EventSub (WebSocket), IRC |
| Logging | Serilog |
| Dashboard | Kotlin Multiplatform + Compose (desktop + web) |
| API docs | OpenAPI + Scalar |
There are two pieces: the dashboard, which you set the bot up and run it from, and the backend, the server the dashboard talks to. On the hosted offering the backend is run for you, so you only need the dashboard. To self-host, run your own backend (Docker) and point the dashboard at it.
The dashboard is the normal way to run NomNomzBot — no config files, no API calls by hand. You point it
at a backend URL (localhost or your own server for self-host, the hosted API for SaaS) and a setup
wizard walks you through everything, with a labelled input for each value the bot needs:
- Connect your Twitch account — sign in and authorize. Scopes are requested progressively, only as you enable the features that need them.
- Connect your bot account — authorize the account the bot posts chat from. This is an additional connection on top of your account (like linking Spotify), not a second login.
- Enter your app credentials (self-host) — paste the Twitch Client ID / Secret from your Twitch developer app into the wizard's fields. On the hosted offering this is already configured for you.
- Basics — bot prefix, default language, timezone.
- Integrations (optional) — Spotify, Discord, YouTube; enable any now or later from settings.
When you finish, you land on the dashboard home with the bot live in your channel.
In development. The Compose dashboard isn't released yet. In the meantime the exact same guided flow is available through the bot's self-describing API:
GET /api/v1/system/setup/wizardreturns each step, what it needs, and the endpoint to call, and you can run every step from the interactive docs at/scalar. See Using NomNomzBot.
Skip this section if you're using the hosted offering. To self-host, run the backend — the dashboard then connects to it.
Prerequisites: Docker, and a registered Twitch application (Twitch Developer
Console) for its Client ID / Secret. Redirect URIs are computed at
runtime from App:BaseUrl, so register exactly one callback — {App:BaseUrl}/api/v1/auth/twitch/callback
(locally, http://localhost:5080/api/v1/auth/twitch/callback).
cd server
cp .env.example .env # then set TWITCH_CLIENT_ID / TWITCH_CLIENT_SECRET / TWITCH_BOT_USERNAME
docker compose up -d # PostgreSQL, Redis, and the APIThe Twitch Client ID / Secret are the one value the backend needs to boot (self-hosters can also enter
them in the dashboard wizard once it ships). On first start the API waits for Postgres and Redis, runs
migrations, seeds reference data (TTS voices, permission presets), and connects the Twitch EventSub
WebSocket. Point your dashboard at http://localhost:5080 (or your exposed URL) and run the setup above.
For local backend development without Docker (dotnet run), see Development.
| URL | Purpose |
|---|---|
http://localhost:5080 |
API (point the dashboard here) |
http://localhost:5080/scalar |
Interactive API docs (Scalar) |
http://localhost:5080/health |
Health status (JSON) |
http://localhost:5080/health/live |
Liveness probe |
http://localhost:5080/health/ready |
Readiness probe (DB + Redis) |
http://localhost:5080/health/version |
Deployed build version |
http://localhost:8082 |
Adminer (Postgres browser) — dev override only |
Adminer is a development-only convenience:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d adminer. It is excluded from the production compose.
Day to day you drive the bot from the dashboard. While the Compose dashboard is in development, the same
operations are available through the REST API — the interactive Scalar docs at /scalar let you call
every endpoint from the browser.
The onboarding flow (the dashboard wizard, or GET /api/v1/system/setup/wizard today) is:
- Twitch application — your Client ID / Secret (entered in the wizard, or set in the backend env).
- Streamer account — authorize your own Twitch account (
GET /api/v1/auth/twitch→ approve on Twitch → back to the callback). Scopes are requested progressively as you enable features. - Bot account — authorize the account the bot posts chat from — an additional connection, not a separate login.
- Basics — bot prefix, default language, timezone.
- Integrations (optional) — Spotify, Discord, YouTube; can be added later.
Once a streamer account is configured, the credential-setup endpoints lock to admins.
Everything the dashboard surfaces is a versioned endpoint under /api/v1/ (browse them in /scalar):
- Commands, pipelines, event responses, timers
- Moderation, channel-point rewards, polls/predictions, stream info
- Economy (currency, store, jars, leaderboards, games)
- TTS config and voices, music/song-request settings
- Roles & permissions, integrations, webhooks
Responses use StatusResponseDto<T> or PaginatedResponse<T>; errors are RFC 7807 problem details.
Viewer/OBS-facing surfaces are delivered by the widget system — widgets are compiled from source at build time, served by the bot, and CDN-cached for SaaS (not static files):
- Song requests — viewers open a token-based request page and queue tracks with no login.
- OBS overlays — add your channel's overlay URL as a browser source; alerts and a now-playing bar are
pushed live over the
OverlayHub(SignalR).
This is in development; the compiled-widget pipeline (build → serve → CDN cache) is being specced.
The dashboard collects most settings for you; these are the backend's own knobs:
- Docker / deploy —
server/.env(copy fromserver/.env.example, which documents every variable). Config keys map to env vars with__, e.g.Twitch:ClientId→TWITCH_CLIENT_ID;docker-compose.ymlalso maps the friendlyAPI_BASE_URLontoApp__BaseUrl. - Local dev (
dotnet run) —server/src/NomNomzBot.Api/appsettings.json(defaults) +appsettings.Development.json(your secrets). Everything else falls back to the defaults. - Twitch redirect URI is computed at runtime from
App:BaseUrl— register only{App:BaseUrl}/api/v1/auth/twitch/callback.
Optional integrations activate when their credentials are present: Spotify, Discord, YouTube,
Azure:Tts, and ElevenLabs. See server/.env.example for the full, commented reference.
The API speaks plain HTTP inside the container (ASPNETCORE_URLS=http://+:5000); TLS is terminated by a
reverse proxy in front of it. Running the API port directly on the public internet without TLS is not
supported — OAuth tokens and JWTs would travel in cleartext. Two supported paths:
-
Cloudflare Tunnel — the bundled
cloudflaredservice (setCLOUDFLARE_TUNNEL_TOKEN) gives a public HTTPS hostname with no inbound ports opened. -
Reverse proxy — put Caddy or nginx in front (
reverse_proxy localhost:5080). Caddy provisions Let's Encrypt certificates automatically:api.example.com { reverse_proxy localhost:5080 }
The Postgres/Redis ports bind to 127.0.0.1 and Adminer is excluded from the production compose, so only
the proxy reaches the API. Set App:BaseUrl to your public HTTPS URL — host-header filtering and the
Twitch OAuth redirect URI are both derived from it. If the proxy reaches the API from a non-loopback
address (e.g. a containerised sidecar), set TRUSTED_PROXY_NETWORKS so the real client IP is trusted.
nomnomzbot/
├── server/ .NET 10 backend (Domain → Application → Infrastructure → Api), tests, Docker
└── app/ Kotlin Multiplatform + Compose dashboard (in active development)
The backend follows Clean Architecture (dependencies flow inward) with the Result<T> pattern, soft
deletes, per-request multi-tenancy, and auto-discovered DI.
cd server
docker compose up -d postgres redis # just the infrastructure
cd src/NomNomzBot.Api
dotnet run # auto-migrates and seeds on first startcd server
dotnet build NomNomzBot.slnx
dotnet testThe build treats warnings as errors, and the code is formatted with CSharpier
(dotnet csharpier format .) — both are commit gates.
Contributions are welcome. A few conventions keep the tree healthy:
- Work in small, validated vertical slices; keep the working tree clean.
- Match the surrounding style; explicit types (no
var), file-scoped namespaces,Result<T>over exceptions, async all the way. - Before committing, run
dotnet csharpier format .and ensuredotnet build+dotnet testare green (warnings are errors). Conventional commit messages (feat:,fix:,chore:) are preferred. - The default branch is
master.
Found a vulnerability? Please report it privately via a
GitHub Security Advisory rather than a
public issue. Security fixes ship as GitHub releases tagged [SECURITY] and, where warranted, as
advisories — watch this repository's releases. Check the build you are running with
GET /health/version.
AGPL-3.0-or-later. Copyright © NoMercy Labs.