edwh-restic-discord
Discord webhook notifications for edwh-restic-plugin: a failed backup reaches your Discord server instead of dying in a cron log.
Core emits an event per restic operation and ships no notifiers itself. This package is one: it
registers a channel called discord, and does nothing at all until [restic.notify] channels names
it.
Installation
(uv) pip install edwh-restic-discord
Installed beside edwh-restic-plugin, it is discovered through its entry point; no import or module
list is needed.
Quick start
.env (gitignored, secrets only):
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/1234567890/token_value
.toml (or default.toml, the committed template):
[restic.notify]
channels = ["discord"]
Then check it works without waiting for a real failure:
edwh restic.notify-test
edwh restic.notify-test --all-events
A webhook url is what activates the channel: named in channels but without one, it is skipped
with a note and your backups run on.
Routing: one webhook, or one per anything
The .env url above is the default for every event. The general form is a table whose keys say
which events go where:
[restic.notify.discord]
[restic.notify.discord.urls]
"*" = "https://discord.com/api/webhooks/1/all" # everything
"backup" = "https://discord.com/api/webhooks/2/backups" # one operation
"warning" = "https://discord.com/api/webhooks/3/noisy" # one level
"failed" = "https://discord.com/api/webhooks/4/oncall" # one phase
"check.failed" = "https://discord.com/api/webhooks/5/damage"
Five kinds of key, freely mixed:
| Key | Means | Matches |
|---|---|---|
"*" |
everything | any event |
"backup", "backup.*" |
one operation | backup.started, backup.failed, … |
"failed", "*.failed" |
one phase, across operations | backup.failed, check.failed, … |
"error", "warning", "info" |
one level | whatever core gives that level |
"check.failed" |
one exact event | only that one |
Operations are backup, restore, check, forget, wipe; phases are started, succeeded,
failed, slow.
The most specific key wins, and exactly one webhook set is chosen per event. A list value is how you deliberately fan out:
[restic.notify.discord.urls]
"failed" = [
"https://discord.com/api/webhooks/4/oncall",
"https://discord.com/api/webhooks/6/audit",
]
webhook and webhooks are accepted aliases for url and urls. Values must be complete
https:// webhook urls; there is no Discord equivalent of a bare-topic plus server form.
Mentions by friendly name
Discord pings by numeric id, so [restic.notify.discord.users] and
[restic.notify.discord.roles] map names you choose to the ids from Discord:
[restic.notify.discord]
webhook = "https://discord.com/api/webhooks/1/all"
[restic.notify.discord.roles]
oncall = "123456789012345678"
[restic.notify.discord.users]
robin = "234567890123456789"
[restic.notify.discord.mentions]
"failed" = ["oncall"]
"backup.failed" = ["oncall", "robin"]
The mention table uses the same keys and precedence as webhooks. Values can be mapped names, or raw
Discord mentions such as <@id>, <@&id>, @everyone and @here. A matched route adds one short
content line to the message and allows those mention types to ping.
What a notification looks like
Each event becomes a Discord embed:
- Title:
⏳ backup.failed - acme-prod@db-01(phase picks the emoji) - Description: a one-line headline, plus restic logs on failures
- Fields: repository, host, project, target, failed scripts, and other event details
- Color: info is blue, warning is orange, error is red
All options
Under [restic.notify.discord]:
| Key | Default | Meaning |
|---|---|---|
url, webhook |
— | shorthand for the "*" route |
urls, webhooks |
— | the route table (or a bare value, same as url) |
logs |
true |
include restic's stdout/stderr on failures |
timeout |
4.0 |
seconds per request |
max_chars |
3900 |
character budget for the embed description |
username |
— | webhook display-name override |
avatar_url |
— | webhook avatar override |
users |
— | map of friendly name to Discord user id |
roles |
— | map of friendly name to Discord role id |
mention, mentions |
— | mention route table |
Environment:
| Key | Meaning |
|---|---|
DISCORD_WEBHOOK_URL |
default webhook when TOML has no url route |
events and min_level are read by core, not by this plugin, and still apply:
[restic.notify.discord]
webhook = "https://discord.com/api/webhooks/1/all"
min_level = "warning" # core drops info events before routing ever sees them
You rarely need events: this plugin narrows core's subscription to what its own table can deliver,
so restic.notify-test reports an event with no matching route as filtered rather than as a silent
success. An event that reaches us with no route sends nothing and says so once per event name.
A worked setup
[restic.notify]
channels = ["discord"]
project = "acme-prod-db01"
[restic.notify.discord]
logs = true
username = "restic"
[restic.notify.discord.urls]
"*" = "https://discord.com/api/webhooks/1/timeline"
"failed" = "https://discord.com/api/webhooks/2/oncall"
"check.failed" = "https://discord.com/api/webhooks/3/urgent"
"backup.slow" = "https://discord.com/api/webhooks/1/timeline"
[restic.notify.discord.roles]
oncall = "123456789012345678"
[restic.notify.discord.mentions]
"check.failed" = ["oncall"]
Security note
A notifier runs in-process and is trusted like any other dependency. Events carry an allowlist of
fields and never the repository URI, but logs on a failure event is restic's full stdout/stderr,
which can include repository paths and hostnames. A Discord webhook url is also a secret: anyone who
has it can post to that channel. Keep it in .env, point failure routes only at channels you trust,
and set logs = false when the full terminal output should stay off Discord.
Development
uv pip install -e .[dev]
edwh test.run
edwh plugin.release # to publish a new version with vommit
License
edwh-restic-discord is distributed under the terms of the
MIT license.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file edwh_restic_discord-0.1.0.tar.gz.
File metadata
- Download URL: edwh_restic_discord-0.1.0.tar.gz
- Upload date:
- Size: 11.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc98ff201fc1e4ee90433a76eab2d3bfcc33d8e62525c111df7b495688fc01b2
|
|
| MD5 |
c996124070bf73e2f027d177aae57cd7
|
|
| BLAKE2b-256 |
1613f0613a8e6221cd364f59ea8352ae83fe730647f48ff7a023dee6508c8abb
|
File details
Details for the file edwh_restic_discord-0.1.0-py3-none-any.whl.
File metadata
- Download URL: edwh_restic_discord-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Linux Mint","version":"22.3","id":"zena","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1813a5083c384bc3bc3eccfa4687426430f6b20e7a53a7b8415b61f196b64c3d
|
|
| MD5 |
7f84f9fc1a1b33c02d2cc3066c01c49d
|
|
| BLAKE2b-256 |
59e1b7c6a3b359442b2c4fbf7bbcd4c86c114011e5983877f5ea3a768cac42e0
|