-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathalertloop.example.yaml
More file actions
254 lines (239 loc) · 11.7 KB
/
Copy pathalertloop.example.yaml
File metadata and controls
254 lines (239 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# AlertLoop example configuration.
#
# THIS FILE IS THE ONLY SOURCE OF CONFIGURATION, on top of built-in defaults.
# Point the binary at it: alertloop --config alertloop.yaml all
# (or set ALERTLOOP_CONFIG=/path/to/alertloop.yaml).
#
# It is also the ONLY example: the same file serves the binary install and both
# Compose profiles. The database section reads its driver and DSN from the
# environment with SQLite as the default, which is what lets the postgres
# profile point it at PostgreSQL without a second, divergent copy of this
# file to keep in sync.
#
# The environment is not a second place to configure AlertLoop — it only keeps
# secrets out of this file. A value written as exactly ${VAR} or ${VAR:-default}
# is replaced from the environment at startup:
#
# admin_token: ${ALERTLOOP_ADMIN_TOKEN}
#
# Three rules: the WHOLE value is substituted or nothing (so a password with a
# literal "$" is never mangled); a missing variable without a :-default stops
# the process rather than leaving an empty setting; and the substituted text is
# data, not YAML, so a password containing ": " or "#" stays a password.
#
# Upgrading from 0.2.x: ALERTLOOP_ADDR, ALERTLOOP_DB_DSN, ALERTLOOP_LOG_*,
# ALERTLOOP_WORKER_* and friends no longer configure anything. A leftover one
# stops startup with the config line to write instead.
# HTTP listen address for the API and web UI.
addr: ":8080"
# Admin token protects the events web page and the admin console at /admin, and
# grants FULL API access (it is the admin console credential).
#
# There is deliberately NO fallback here. A reference with no default stops
# startup when the variable is unset, naming what is missing — which is the only
# safe behaviour for the credential that opens the whole API. An installation
# silently running on a placeholder published in this repository is worse than
# one that refuses to start, and that is not hypothetical: 0.3.0 shipped exactly
# that and it was fixed in 0.3.1.
#
# export ALERTLOOP_ADMIN_TOKEN="$(openssl rand -hex 32)"
#
# Under systemd put it in an EnvironmentFile; under Compose put it in .env.
admin_token: ${ALERTLOOP_ADMIN_TOKEN}
# API keys for services calling the JSON API. Each key has a scope:
# ingest - may only create events (POST /v1/events) → for event sources
# read - may only read events/deliveries/stats → for dashboards
# full - full access incl. state actions and replay → for trusted tools
# Omitted scope defaults to full. The admin token already has full access, so
# keys are optional (e.g. only needed for least-privilege service credentials).
#
# Commented out on purpose: an example that ships a WORKING key hands a live
# ingest credential to everyone who copies this file, and whoever holds one can
# create events and through them wake every configured channel. Uncomment and
# generate your own (openssl rand -hex 32), or keep them in .env and reference
# them here as ${VAR}.
#
# api_keys:
# - key: ${ALERTLOOP_INGEST_KEY}
# scope: ingest
# - key: ${ALERTLOOP_READ_KEY}
# scope: read
# Event retention window in days: events older than this are pruned automatically
# by the worker. No upper bound.
retention_days: 30
# When an incident closes, tell the channels that were told about it.
#
# Applies both to an ingested `status: resolved` and to the manual resolve
# action in the console. The notice goes ONLY to the channels that received the
# alert — resolving is not re-routed, and a channel whose alert dead-lettered is
# skipped, because it never learned there was a problem. Repeating a recovery
# does not notify twice.
#
# On by default: an alerting service that says the database is down and never
# says it came back leaves you to guess. Set it to false if your channel is a
# ticketing webhook that opens an issue per message — such a receiver should
# read the `kind` field ("alert" or "recovery") in the webhook body instead.
notify_on_resolve: true
# Logging. Output ALWAYS goes to stdout; `file` adds a copy on disk. Setting a
# file never silences stdout, so `docker compose logs`, journald and any log
# shipper keep working alongside it.
log:
level: "info" # debug | info | warn | error
format: "text" # text (human-readable) | json (for log processors)
# Path of the log file. Empty (the default) means stdout only.
#
# Written as a reference so the Compose file can give the api and the worker
# SEPARATE files without a second copy of this config: both containers mount
# this one file on purpose, and two processes appending to one log would
# rotate it out from under each other.
#
# AlertLoop creates the directory if it is missing, but it must be able to
# write there. Under Docker the containers run as uid 10001 with a read-only
# root filesystem, so a path such as /var/log/alertloop/alertloop.log works
# only if that directory is a mounted volume owned by that uid — see the
# "Reading AlertLoop's own logs" section of OPERATIONS.md. Under systemd the
# shipped unit declares LogsDirectory=alertloop, so /var/log/alertloop is
# created for the service user and this path just works.
file: ${ALERTLOOP_LOG_FILE:-}
# Rotation of that file, by size — nothing else rotates it. Disk use is
# bounded by max_size_mb * (max_files + 1): 300 MB with the values below.
# Set max_size_mb to 0 if logrotate or a shipping agent handles the file.
max_size_mb: 50 # rotate to <file>.1 at this size; older ones shift up
max_files: 5 # rotated files kept besides the active one
# In-process rate limiting.
rate_limit:
enabled: true
per_ip_per_second: 20 # requests per second per client IP
per_ip_burst: 40
ingest_per_second: 100 # overall event ingestion cap for the process
ingest_burst: 200
# IF YOU RUN BEHIND A REVERSE PROXY, SET THIS.
#
# Otherwise every request reaches AlertLoop from the proxy's address, the
# per-IP limiter sees exactly one client, and the whole internet shares a
# single bucket. Two things follow, and both are worse than having no limiter
# at all — because the documentation says there is one:
#
# * guessing at the admin token is effectively unlimited;
# * one noisy client empties the shared bucket and every legitimate event
# source starts getting 429.
#
# List the addresses of proxies you actually run. Their X-Forwarded-For is
# then believed, and only theirs: the header is trivially forged by anyone
# talking to AlertLoop directly, so trusting it unconditionally would be
# worse still — every request would look like a new client and the limiter
# would never fire.
#
# trusted_proxies:
# - "127.0.0.1" # nginx or Caddy on this host
# - "10.0.0.0/8" # a load balancer range
#
# Also add rate limiting at the proxy itself; deploy/proxy/nginx.conf ships
# with it configured. The two are complementary, not alternatives.
# The admin console is served by this API at /admin (same origin — no CORS
# needed). Only set cors_origins if you build your OWN browser app on a
# different origin that calls this API. Use "*" to allow any origin; empty (the
# default) disables CORS and allows same-origin only.
# cors_origins:
# - "https://my-dashboard.example.com"
database:
# "sqlite" (local/demo) or "postgres" (production). Inferred from dsn if empty.
# Written as a reference so the Compose postgres profile can select the driver
# without editing this file; hard-code "postgres" instead if you prefer the
# file to be self-contained.
driver: ${ALERTLOOP_DB_DRIVER:-sqlite}
# SQLite: a file path or ":memory:".
# PostgreSQL, either form:
# "host=HOST port=5432 user=USER dbname=alertloop sslmode=disable password=PASS"
# "postgres://USER:PASS@HOST:5432/alertloop?sslmode=disable"
# In the URL form, a password containing / ? # @ % or a space must be
# percent-encoded (/ as %2F) or the URL does not parse. The keyword/value form
# takes the password as written, unless it contains a space or a backslash
# or starts with a single quote — then write password='...' with \' and \\
# for ' and \.
# The Compose postgres profile passes a keyword/value DSN in here.
# A DSN carries a password, so it is a good candidate for ${VAR} too.
dsn: ${ALERTLOOP_DB_DSN:-alertloop.db}
worker:
concurrency: 2
poll_interval: 2s
max_attempts: 5 # tries before a delivery is dead-lettered
base_backoff: 30s # first retry delay; doubles each attempt
max_backoff: 30m # cap on the retry delay
# Delivery channels are OPTIONAL. With none configured (as below), AlertLoop
# simply stores events (visible in the API and at /admin) and delivers nothing —
# this is a perfectly valid way to run. To send notifications, uncomment one or
# more channels and fill in their fields.
#
# Each type is a LIST, so you can configure several channels of the same type
# (e.g. two Telegram chats). Without a `routing` section (see below) every event
# is delivered to EVERY configured channel. Each channel needs a unique `name`.
# If you enable a channel you must fill ALL its required fields, or startup will
# fail with a clear message.
channels: {}
# email:
# - name: dev-email
# host: "smtp.example.com"
# port: 587
# username: "alerts@example.com"
# password: "app-password"
# from: "alerts@example.com"
# to: ["ops@example.com"]
# starttls: true # require STARTTLS on a plaintext port (e.g. 587)
# tls: false # set true for implicit TLS / SMTPS (e.g. port 465)
#
# telegram:
# - name: dev-telegram
# bot_token: ${TELEGRAM_BOT_TOKEN} # from @BotFather; keep it in .env
# chat_id: "-1001234567890"
# # If this host cannot reach api.telegram.org directly, send this
# # channel's requests through a proxy. Schemes: http, https, socks5,
# # socks5h (socks5h is a synonym: the proxy resolves DNS either way).
# # MTProto proxies do NOT work for the Bot API. Any other scheme stops
# # startup. Leave it out to keep using HTTP_PROXY/HTTPS_PROXY, if set.
# # proxy: "socks5://user:pass@127.0.0.1:1080"
# #
# # Alternative to a proxy: a trusted Bot API mirror or reverse proxy.
# # api_base: "https://tg-mirror.example.com"
#
# - name: customer-telegram
# bot_token: "123456:ABC-DEF"
# chat_id: "-1009876543210"
#
# webhook:
# - name: siem
# url: "https://example.com/alertloop-hook"
# # Requests are HMAC-SHA256 signed; receivers verify X-AlertLoop-Signature.
# secret: "shared-webhook-secret"
# Routing is OPTIONAL. Without this section every event goes to every configured
# channel (the behaviour of 0.1.x). With it, each event goes to the channels of
# the FIRST rule it matches — rules are checked top to bottom and channel lists
# are never merged.
#
# The example below is the common split: the customer gets the orders, the
# developer gets the failures, and neither gets the other's events.
#
# routing:
# rules:
# # Suppression first: a rule that silences a source must sit above the
# # rules it overrides, because the first match wins.
# - name: silence-healthchecks
# match:
# source: [healthcheck]
# channels: [] # explicit "nowhere": stored, never delivered
#
# - name: incidents-to-dev
# match:
# type: [incident] # values in one field are OR-ed...
# min_severity: warning # ...and different fields are AND-ed
# channels: [dev-telegram, dev-email]
#
# - name: orders-to-customer
# match:
# type: [business_event]
# category: ["order.*"] # only a TRAILING "*" is supported
# channels: [customer-telegram]
#
# # Events matching no rule. Omit it and they are delivered nowhere — each one
# # is then logged at warn level so the loss is never silent.
# default: [dev-telegram]