StreamArchive monitors Twitch and Kick channels and records every live stream with streamlink. The app can also re-stream recordings to YouTube Live and send alerts and admin commands over a Telegram bot.
Live and offline signals arrive within seconds on both platforms. A poll at
monitoring_interval stays as the fallback. The poll catches missed events,
starts channels that are already live at boot, and restarts recordings that
died mid-stream. The app logs each failure, sends an alert, and retries on the
next poll cycle.
Two fast paths deliver the signals:
- Twitch. The EventSub client holds one conduit WebSocket shard for the
stream.onlineandstream.offlineevents, and authenticates with the app credentials. Recordings run through ad-block playlist proxies (the vendoredstreamlink-ttvlolplugin). Thus streams that need an ad-block workaround still record. - Kick. The app receives signature-verified
livestream.status.updatedandchat.message.sentwebhooks from the Kick Developer API. Recordings use the built-in streamlink Kick plugin, which talks to the Kick API directly.
- One settings file for both platforms. A channel name is
twitch:<name>orkick:<slug>. - Three output modes.
diskwrites recordings torecording_dir/<channel>/as.ts, or as.m4afor audio-only channels.youtubepipes the stream through ffmpeg to a YouTube broadcast.bothruns disk and youtube together. - Chat recording. The app saves Twitch IRC chat and Kick webhook chat as
TwitchDownloader-compatible JSON in
chat_dir/<platform>/<channel>/. It writes each file while the recording runs. See Chat recording. - Retention cleanup. The app deletes recordings and chat files older than
retention_daysat startup and then daily. An optionaldisk.max_total_gbcap deletes the oldest archive files or stops new recordings. - Self-healing. Recording tasks that die mid-stream restart on the next poll cycle. YouTube re-streams restart with growing delays. A rolling 24-hour budget of 10 broadcast creations guards the YouTube daily limit. YouTube quota errors fall back to disk recording.
- Telegram alerts. A live alert holds the stream title, the game, and the URL. An offline alert holds the file size and the YouTube link. Alerts also cover start failures, Kick anti-bot blocks, webhook problems, and app lifecycle messages. Repeated failure alerts are limited to one per 30 minutes for each channel.
- Telegram control. The admin manages the recorder over the bot. Commands
cover channels, retention, output mode, quality, chat recording, limits, the
Kick webhook, status, reload, and restart. Other users get no reply. The
Remote access menu holds the Kick webhook and the HTTP control API. The
app validates each change and writes it atomically to
config.json. The change applies on the next poll cycle.
The scheduler runs the poll loop, the signal handling, and the retention
cleanup. Each cycle the monitor compares the configured channels against the
Twitch Helix API and the Kick API. Then it starts, stops, or restarts recording
tasks. The recorder captures with streamlink, writes .ts/.m4a files, or
pipes the stream through ffmpeg to YouTube. When a task ends, it finalizes the
chat file and the broadcast. The notifier sends Telegram messages.
Two services feed the monitor directly. The EventSub client holds one
authenticated WebSocket for Twitch events. The Kick webhook receiver verifies
and deduplicates incoming HTTP events and keeps the subscriptions in sync. Its
listener also serves the control API under /api/v1. The Telegram bot runs
alongside as an admin-only polling bot. It validates each change on a copy,
writes config.json atomically, and applies the change on the next cycle. The
control API calls the same command layer, so both paths behave in the same way.
See Development for the module map.
- Docker with the compose plugin (Docker Engine 20.10 or later, or Docker Desktop).
- Twitch app credentials from https://dev.twitch.tv/console.
- Kick app credentials for
kick:channels. Create an app in the Kick Developer portal (client id and client secret). - A Telegram bot token from BotFather, and your user or chat id.
- A Google Cloud OAuth client (
client_secret.json) foroutput_mode: youtubeorboth. See YouTube setup. cloudflaredor Tailscale for the Kick webhook tunnel. Both ship in the image. The Tailscale funnel option also needs tailscale on the host. The app mounts the host tailscale directory into the container.
mkdir ~/stream-archive-data && cd ~/stream-archive-data
curl -LO https://github.com/giou/stream-archive/releases/latest/download/docker-compose.yml
curl -LO https://github.com/giou/stream-archive/releases/latest/download/config.json.example
cp config.json.example config.json
# fill in each key, see docs/configuration.md
docker compose up -d
docker compose logs -f # follow startupThe data directory is the folder with docker-compose.yml
(~/stream-archive-data/ in this example). It holds the settings, the
recordings, the chat files, and the tokens. To move it to another disk, put
STREAM_ARCHIVE_DATA in .env in that folder:
# ~/stream-archive-data/.env
STREAM_ARCHIVE_DATA=/mnt/bigdisk/stream-archive-dataTo update the app, pull the new image:
docker compose pull && docker compose up -dThen configure the optional features:
- YouTube setup for
output_mode: youtubeorboth. - Kick webhook for instant signals and Kick chat.
| Guide | Content |
|---|---|
| Configuration | All keys of config.json, and secrets from the environment |
| Running | Start, data directory, container identity, logs, shutdown |
| YouTube setup | OAuth client and the one-time authorization flow |
| Kick webhook | Public URL, tunnel options, receiver internals |
| Telegram control | Bot menu and command list |
| Control API | HTTP endpoints, accepted values, error answers |
| Chat recording | Chat files and TwitchDownloader commands |
| Failure handling | Behavior for each failure type |
| Development | Project layout, commands, dependency updates, plugin override |
MIT. The image contains the third-party twitch.py plugin
(streamlink-ttvlol). The recorder imports that file into its own process, so
the build downloads it from the upstream release, records its sha256 in
/app/plugins/twitch.py.sha256, and can be pinned to reviewed bytes with the
TTVLOL_PLUGIN_SHA256 build argument. The plugin keeps its upstream license.