Add WEBHOOK_SECRET env var for authenticating incoming Owncast webhooks via a ?secret= query parameter. Requests with a missing or incorrect secret are rejected with 401. If unset, all requests are accepted (with a startup warning). Also includes previously uncommitted work: - IRC server password support (IRC_PASSWORD env var, PASS command) - IRC username/ident field in config - IRC_PASSWORD and SELinux volume flag in docker-compose.yml Made-with: Cursor
121 lines
4.1 KiB
Markdown
121 lines
4.1 KiB
Markdown
> [!IMPORTANT]
|
|
> This project was developed entirely with AI coding assistance (Claude Opus 4.6 via Cursor IDE) and has not undergone rigorous human review. It is provided as-is and may require adjustments for other environments.
|
|
|
|
# owncast-irc-bridge
|
|
|
|
Bidirectional chat bridge between [Owncast](https://owncast.online/) and IRC. Messages sent in your Owncast chat appear in an IRC channel and vice versa.
|
|
|
|
## Quick Start (Docker Compose)
|
|
|
|
**1. Create your config file**
|
|
|
|
```bash
|
|
cp config.example.toml config.toml
|
|
```
|
|
|
|
Edit `config.toml` with your IRC server/channel and Owncast URL.
|
|
|
|
**2. Get an Owncast access token**
|
|
|
|
In your Owncast admin panel, go to **Integrations > Access Tokens** and create a token with "send messages" permission.
|
|
|
|
**3. Set the token**
|
|
|
|
```bash
|
|
export OWNCAST_ACCESS_TOKEN="your-token-here"
|
|
```
|
|
|
|
Or create a `.env` file (git-ignored):
|
|
|
|
```
|
|
OWNCAST_ACCESS_TOKEN=your-token-here
|
|
WEBHOOK_SECRET=some-random-secret
|
|
```
|
|
|
|
**4. Configure the Owncast webhook**
|
|
|
|
In your Owncast admin, go to **Integrations > Webhooks** and add a webhook pointing to:
|
|
|
|
```
|
|
http://<bridge-host>:9078/webhook?secret=some-random-secret
|
|
```
|
|
|
|
If `WEBHOOK_SECRET` is set, the bridge rejects any request that doesn't include a matching `?secret=` query parameter. If unset, all requests are accepted (a warning is logged at startup).
|
|
|
|
Select the events: **Chat Message**, **Stream Started**, **Stream Stopped**.
|
|
|
|
**5. Run it**
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
Check logs:
|
|
|
|
```bash
|
|
docker compose logs -f
|
|
```
|
|
|
|
## Running Without Docker
|
|
|
|
Requires Rust 1.75+.
|
|
|
|
```bash
|
|
cargo build --release
|
|
export OWNCAST_ACCESS_TOKEN="your-token-here"
|
|
./target/release/owncast-irc-bridge --config config.toml
|
|
```
|
|
|
|
## Configuration
|
|
|
|
See [`config.example.toml`](config.example.toml) for all options. The only required sections are `[irc]` (with `server` and `channel`) and `[owncast]` (with `url`). Everything else has defaults.
|
|
|
|
| Section | Key | Default | Description |
|
|
|---------|-----|---------|-------------|
|
|
| `irc` | `server` | *(required)* | IRC server hostname |
|
|
| `irc` | `port` | `6667` | IRC server port |
|
|
| `irc` | `tls` | `false` | Use TLS for IRC |
|
|
| `irc` | `nick` | `owncast-bridge` | IRC nickname |
|
|
| `irc` | `channel` | *(required)* | IRC channel to join |
|
|
| `owncast` | `url` | *(required)* | Owncast instance URL |
|
|
| `owncast` | `webhook_port` | `9078` | Port the webhook server listens on |
|
|
| `owncast` | `websocket_enabled` | `false` | Also connect via WebSocket (redundant with webhook, useful as fallback) |
|
|
| `owncast` | `health_poll_interval_secs` | `30` | How often to poll Owncast status |
|
|
| `bridge` | `irc_prefix` | `[IRC]` | Prefix for IRC messages in Owncast |
|
|
| `bridge` | `owncast_prefix` | `[OC]` | Prefix for Owncast messages in IRC |
|
|
| `control` | `socket_path` | `/tmp/owncast-irc-bridge.sock` | Unix socket for `bridge-ctl` |
|
|
|
|
Secrets are always read from environment variables (not the config file):
|
|
|
|
| Variable | Required | Description |
|
|
|----------|----------|-------------|
|
|
| `OWNCAST_ACCESS_TOKEN` | Yes | Owncast integration API token |
|
|
| `IRC_PASSWORD` | No | IRC server password (PASS command) |
|
|
| `WEBHOOK_SECRET` | No | Shared secret for webhook authentication |
|
|
|
|
## Runtime Control
|
|
|
|
Use `bridge-ctl` to interact with a running bridge:
|
|
|
|
```bash
|
|
bridge-ctl status # Show bridge status as JSON
|
|
bridge-ctl irc reconnect # Reconnect to IRC
|
|
bridge-ctl owncast reconnect # Reconnect to Owncast
|
|
bridge-ctl quit # Shut down the bridge
|
|
```
|
|
|
|
Inside Docker:
|
|
|
|
```bash
|
|
docker compose exec bridge bridge-ctl status
|
|
```
|
|
|
|
## How It Works
|
|
|
|
- **Owncast → IRC:** Owncast sends webhook events to the bridge. The bridge formats the message and sends it to IRC via PRIVMSG.
|
|
- **IRC → Owncast:** The bridge listens for PRIVMSG in the configured channel and posts to Owncast via the integration API.
|
|
- **Deduplication:** If both webhook and WebSocket are enabled, duplicate messages are detected by ID and dropped.
|
|
- **Echo suppression:** Messages the bridge itself sent are recognized and not re-bridged.
|
|
- **Stream events:** Stream start/stop events are announced in IRC.
|
|
- **Health polling:** The bridge polls Owncast's `/api/status` endpoint and announces state changes in IRC.
|