/me messages as IRC CTCP ACTION with bold attribution
When an OwnCast user sends "/me claps", the bridge now relays it as a proper CTCP ACTION so IRC clients render it natively. The username is bolded with mIRC \x02 for visual attribution. Made-with: Cursor
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 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
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
export OWNCAST_ACCESS_TOKEN="your-token-here"
Or create a .env file (git-ignored):
OWNCAST_ACCESS_TOKEN=your-token-here
4. Configure the Owncast webhook
In your Owncast admin, go to Integrations > Webhooks and add a webhook pointing to:
http://<bridge-host>:9078/webhook
Select the events: Chat Message, Stream Started, Stream Stopped.
5. Run it
docker compose up -d
Check logs:
docker compose logs -f
Running Without Docker
Requires Rust 1.75+.
cargo build --release
export OWNCAST_ACCESS_TOKEN="your-token-here"
./target/release/owncast-irc-bridge --config config.toml
Configuration
See 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 |
The access token is always read from the OWNCAST_ACCESS_TOKEN environment variable (not the config file).
Runtime Control
Use bridge-ctl to interact with a running bridge:
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:
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/statusendpoint and announces state changes in IRC.