Added
-
feat:
NOTIFICATION_HISTORY_RETENTION_DAYSandTLS_INFO_RETENTION_DAYSset how many days the hourly cleanup keeps notification history (90 by default, at least 30, so the default certificate expiry notifications at 21 days aren't sent twice) and monitors' TLS information (30 by default, at least 1). They apply to the api and topeekaping, and a value below its minimum stops startup. Unset or0, nothing changes. See Cleanup Configuration (#75) -
feat:
bun db import-mongo --mongo-uri <uri> [--mongo-db <name>](orMONGO_URIandMONGO_DBin the environment, which keeps a password out of the shell history) moves a MongoDB install to SQLite or PostgreSQL. It applies the SQL migrations to the new database thatDB_TYPEand the otherDB_*variables name, then copies users (with their passwords and two-factor secrets), settings (including the keys that sign login tokens), API keys, proxies, monitors, notification channels, tags, maintenance windows, status pages with their domains, the links between them, heartbeats, stats, TLS information and notification history, in one transaction. It refuses a database that holds users, monitors or history of its own, and prints what it did with every collection, including what it skipped (such as links to deleted monitors) and what SQL has no place for (which monitors a status page hides). The monitors of a status page keep their order. It's in the single-mode image and in the migrate image. See Move to SQLite or PostgreSQL for the steps (#227)- Upgrade note: every row gets a UUID in place of its ObjectID. API key tokens and badge URLs that carry an old ID keep working, and so do logins and two-factor codes. Browsers that were signed in sign in once more, and scripts that put a monitor's ID in an API URL need the new ID.
- Upgrade note: the login lockout counters (
login_state) are not copied. - Upgrade note: open incidents in PagerDuty, PagerTree and Grafana OnCall won't resolve after the move, because their deduplication keys and fields carry the old monitor ID. Resolve them by hand. Webhook receivers that key on the monitor's
idsee the new ID, and links to/monitors/<id>in earlier Slack, Microsoft Teams and Google Chat messages no longer open the monitor. - Upgrade note: the source MongoDB must be 4.4 or later. On PostgreSQL, text wider than its column is cut and listed in the report, and two names that differ only after the cut get
~2,~3at the end, so no row is lost. SQLite keeps text whole. The escape\u0000is removed from monitor and channel configs, which PostgreSQL rejects. - The target can be a database that Peekaping has started on once. It holds only the default settings that the first start writes, and the import replaces them with the install's settings, the login keys included. So a PostgreSQL that
peekapinghas started on is a target too.
-
docs: pages for Deployment modes, Upgrading to single mode and Kubernetes (Helm). They say what single mode and cluster mode run, how to take a 0.0.44 to 0.0.47 bundle to single mode and back, and how to install the chart, from the published chart or from a repository checkout. The Docker + SQLite and Docker + PostgreSQL pages link to the upgrade page instead of each keeping its own copy of the upgrade notes
-
feat: each release publishes the Helm chart at
oci://ghcr.io/0xfurai/charts/peekaping, with the chart'sversionandappVersionset to the release, and every image tag defaults to theappVersion. So a chart version installs one consistent release, and you need no checkout and nohelm dependency build:helm install peekaping oci://ghcr.io/0xfurai/charts/peekaping --version <X.Y.Z>. A component'simage.tagstill overrides the default. See Kubernetes (Helm) (#221)- Upgrade note: an install from a checkout still runs
latest, and animage.tagyou set still wins. A values file that pins all six tags can drop them when you install the published chart, because the chart version pins them. A tag left in stays on its release when you upgrade.
- Upgrade note: an install from a checkout still runs
-
feat: Peekaping sends anonymous usage statistics once a day and error reports when its own code crashes. Both are on by default, and an admin can turn each off on its own: the setup screen starts with both boxes ticked, and Settings has a switch for each. The usage ping holds the version, mode, storage backend, operating system, a range for the number of monitors and the kinds of monitors and notification channels in use. A report holds the error type, the stack frames, the version, mode, storage backend and operating system. Neither holds names, URLs, hosts, settings, error messages or IP addresses. Every field is listed on the page above, and the Settings card shows the exact JSON of the next usage ping.
- Upgrade note: an install that upgrades starts sending each one for which no choice is stored. A choice an admin stored before, such as an opt-out, stays in effect: the new default applies only where nothing is stored. The admin sees a one-time notice in the app that says what is on, names what each sends and never sends, and links to the Settings cards. Dismissing it, or changing either card, stops it. It does not show when both are switched off by the environment.
- To send neither, set both
TELEMETRY=offandERROR_REPORTING=off.TELEMETRY=offswitches off only the usage statistics andERROR_REPORTING=offonly the error reports, each whatever an admin chose. In cluster mode, setERROR_REPORTINGon the API. - The dev compose files in this repository point both at an address nothing listens on, so a development stack sends nothing.
-
feat: remote probes run a monitor's checks from another machine. Create a probe on the new Probes page, start the
peekaping-probeimage (0xfurai/peekaping-probe, also on GHCR) with its token, and choose the probe in the monitor's form. The probe connects out to the API over HTTP(S), runs the checks assigned to it and sends the results back as heartbeats. A monitor can run on several probes: each probe's results are a location on the monitor page, with its own status, response time and heartbeat bar, and by default the monitor is Down while any online location is Down. A status page can show each location. Several instances can share one token, and the probe's monitors are dealt among them. A probe that goes silent is marked offline afterPROBE_OFFLINE_AFTER(3 minutes by default), the probe's own notification channels are told, and its monitors are not marked Down. The Docker Compose nginx gateway, the single-mode bundles on port 8383 and the Helm chart's Ingress and Istio VirtualService route/probe/to the API. See Remote probes (#31, #32, #33, #34, #252, #36, #257, #253, #452, #453, #456, #457, #458, #459, #465, #466, #469, #470, #471, #476, #477, #478, #479, #486, #487)- Upgrade note: nothing changes for an existing install. The migrations add the probe tables, put every monitor on the local probe, and record the probe on heartbeats and stats; the heartbeats and stats you have read as the local probe's.
- Upgrade note: with ingress-nginx, set
nginx.ingress.kubernetes.io/proxy-body-size: "2m"iningress.annotationsbefore you use a remote probe, because a probe's batch of results can pass the controller's default limit of 1 MiB. A reverse proxy of your own in front of port 8383 has to pass/probe/, and allow 2 MiB bodies.
-
feat: a monitor on several probes has a location rule that says when it goes Down: when any location is Down (the default, as before), when all of them are, or when at least N are. Locations whose probe is offline, or that haven't reported yet, don't count, so "all", or "at least" with N of 2 or more, keeps one probe's network trouble from raising an alarm while the other probes report. Choose it in the monitor's form once the monitor runs on more than one probe. Its Down and Up notifications name the locations, such as "Down at Frankfurt, Tokyo (2 of 3 locations)", and custom templates get
locations_downandlocations_total. See Remote probes (#35, #488, #489)- Upgrade note: every existing monitor gets the any rule, so it goes Down as it did before. A migration adds the rule's two columns to
monitors.
- Upgrade note: every existing monitor gets the any rule, so it goes Down as it did before. A migration adds the rule's two columns to
-
feat: the remote probe has a slim image,
0xfurai/peekaping-probe:slim(or<version>-slim, also on GHCR), about 40% smaller than the full one. It leaves out the Docker, gRPC keyword, Kafka producer, MongoDB and SQL Server monitor types, tells Peekaping so when it connects, and so can't be given a monitor of one of those types. See The slim image (#264) -
feat: the full and the slim image can run under one probe token. Peekaping keeps the monitor types of each probe instance: a monitor is dealt only to the online instances that run its type, so a Docker monitor goes to a full instance and never to a slim one, and an instance can't send results for a type it doesn't run. The probe's types, which the monitor form and the API check a monitor against, are those of its online instances together, so a slim instance saying hello no longer takes Docker away from the probe. The Probes API shows each instance's
supported_types. See Full and slim instances under one token (#264)- Upgrade note: a migration adds
supported_typestoprobe_instancesand gives each existing instance the types of its probe, so nothing changes for a probe on one build.
- Upgrade note: a migration adds
-
feat: probe availability. The Probes page shows each probe's online and offline time over the last 24 hours as a bar with its uptime, and the probe's name opens its own page, with its history over 24 hours, 7 days or 30 days and a bar for each instance's connections over the last 24 hours. The API serves it at
GET /api/v1/probes/availabilityandGET /api/v1/probes/{id}/availability. In single mode the local probe is thepeekapingprocess itself and has no history (PP-330)- Upgrade note: migrations add the tables
probe_status_changesandprobe_instance_connectionsand start each existing probe's history with its status at the upgrade. The hourly cleanup deletes status changes older than the data retention period.
- Upgrade note: migrations add the tables
-
feat: Create probe has an "Assign to all monitors" box, ticked by default: when the probe's first instance connects, the probe is added to every monitor of a type it runs. The Probes page has the same action for a probe that exists. A new monitor now starts on every enabled probe that runs its type, not only on the local probe; a cloned or edited monitor keeps its probes. See Remote probes (PP-324)
- Upgrade note: on an install with remote probes, a new monitor runs on several locations under the default "any" location rule, so one probe's network trouble takes it Down. Choose "at least 2" in the monitor's form for three or more probes. A migration adds
assign_all_pendingtoprobes, false for every existing probe.
- Upgrade note: on an install with remote probes, a new monitor runs on several locations under the default "any" location rule, so one probe's network trouble takes it Down. Choose "at least 2" in the monitor's form for three or more probes. A migration adds
-
feat: each heartbeat records the probe instance that ran the check, and the heartbeat tooltips and the important heartbeats list on the monitor page show "Instance:" and the instance's hostname. The heartbeat API, socket events and webhook payloads carry a nullable
instance_id(PP-331)- Upgrade note: a migration adds the nullable column
heartbeats.instance_id, empty for earlier heartbeats. It doesn't rewrite the table.
- Upgrade note: a migration adds the nullable column
-
feat: the CA is optional for mTLS on HTTP monitors. Without one, the check still sends the client certificate and verifies the server's certificate against the system's trusted roots. With one, nothing changes (#217)
-
feat: every HTTP request gets an
X-Request-IDresponse header (a valid one the client sends is kept) and one structured log line in every mode, not only withMODE=dev. The line names the route pattern, not the path, so push tokens no longer reach the log. Health and metrics requests log at debug level (#241) -
feat:
SQLITE_STARTUP_CHECKchooses the SQLite check at startup:quick(the default),fulloroff. Cluster mode on SQLite logs a warning that single mode or PostgreSQL fits better, and the Helm chart refusesDB_TYPE: sqlite, which gave each pod a database of its own (#240) -
feat: the monitor list and the monitor page mark a probe that doesn't run the monitor's type, and the stale-monitor notification says why, such as "probe Frankfurt doesn't run docker monitors" (#263)
-
feat: the version in the sidebar links to its GitHub release (#269)
Changed
-
feat: the docs at docs.peekaping.com match the landing page: dark by default with violet glows and the Geist font, a frosted navbar with the mascot, restyled sidebar, code blocks, tables and admonitions, and a welcome page with a hero, a copyable
docker runand link cards. Bash, nginx, HTTP, PHP and Dockerfile code blocks are now highlighted -
feat: the landing page at peekaping.com has a new dark design: a hero with a live heartbeat card and a copyable
docker run, a features grid, a quickstart terminal for SQLite and PostgreSQL, and monitor and alert channel lists that match what the server runs (MySQL/MariaDB, SendGrid, Twilio, LINE, PagerTree and Pushbullet were missing). The testimonials no longer repeat -
feat: the single-mode image runs Peekaping as one process,
peekaping, which serves the web app, the API and the websocket on port 8383, with no Redis, Caddy or supervisord. The image now runs on Alpine. Port 8383, the data volume and your environment variables stay the same. There is no PostgreSQL bundle any more: PostgreSQL runs in a container of its own (see below).peekapingruns as the non-rootpeekapinguser, andPUID,PGID, the Docker socket's group and theping_group_rangewarning work as before. The image can also start withdocker run --user, and it has a Docker health check. There is no MongoDB bundle any more (see Removed). The image, measured against 0.0.47:0.0.47 Now Image size 417.5 MB 78.6 MB RAM after 2 minutes idle 92.7 MiB 19.2 MiB RAM with 100 HTTP monitors checked every 20 seconds, after 5 minutes 131.2 MiB 36.6 MiB See Upgrading to single mode, Docker + SQLite, Docker + PostgreSQL and Single-mode user and permissions (#163, #209, #376) (thanks @tafaust)
- Upgrade note, SQLite bundle: pull the new tag and start it with the same volume. Migrations run at start, as before. Redis and its data are gone; Redis held only the schedule and queued checks, which
peekapingrebuilds from the database at start. - Upgrade note: a custom Caddyfile mounted at
/etc/caddy/Caddyfileno longer works, because the image has no Caddy. Put your own reverse proxy (Caddy, nginx, Traefik) in front of port 8383 instead. The login lockout counts failed logins per client address, and the Caddy inside the image used to setX-Forwarded-Forfor it. Behind your own proxy, make it put the client's address last inX-Forwarded-For(nginx:proxy_set_header X-Forwarded-For $remote_addr;, because plainproxy_passforwards the client's own header) and setTRUSTED_PROXIESto the address it connects from, so the API believes that header. Until you do, every login counts as coming from the proxy. - Upgrade note: port 8383 serves everything, and the image ignores
SERVER_PORT. A.envthat sets it, such asSERVER_PORT=8034from the examples, keeps working. To use another host port, change the published port, for example-p 8080:8383. - Upgrade note:
MODEfrom your environment now applies, withprodas the default. Before, the bundles always ran withprod. WithMODE=dev,peekapinglogs in the development format and logs every request. - Upgrade note:
API_URLfrom your environment now reaches the web app, in the/env.jsthatpeekapingserves. Before, the bundles always gave the web app an emptyAPI_URL, which means the API on the web app's own origin. If your environment or.envsetsAPI_URL, unset it, unless the web app must call an API on another origin. - Upgrade note:
peekapingtakes up to 30 seconds to shut down cleanly.docker stopwaits only 10 seconds by default, so usedocker stop -t 35, orstop_grace_period: 35sin Docker Compose. - Upgrade note: with
METRICS_ENABLED=true, port 8383 now serves/metrics, so anyone who can open the web app can read it. Before, Caddy didn't forward it. - Upgrade note: logs go to the container's output (
docker logs). Nothing writes to/var/log/supervisornow, so a volume mounted there can go.
- Upgrade note, SQLite bundle: pull the new tag and start it with the same volume. Migrations run at start, as before. Redis and its data are gone; Redis held only the schedule and queued checks, which
-
chore: for development,
docker-compose.deps.ymlwith--profile postgresreplacesdocker-compose.sqlite.yml,docker-compose.postgres.ymlanddocker-compose.mongo.yml.make docker-sqliteandmake docker-postgreskeep working -
chore: the Helm chart's bundled PostgreSQL and Redis (off by default) run pinned images, PostgreSQL 18.6 and Redis 8.10.2, instead of Bitnami's floating
latest, so a rescheduled pod can't start a new major version on old data. The Bitnami subcharts move topostgresql18.12.14 andredis28.3.1, andscripts/update-chart-deps.shchecks and updates both. The migrate Job now tries up to 6 times (migrate.backoffLimit), enough to outlast a restart of the bundled PostgreSQL during an upgrade- Upgrade note: with the bundled databases on, the upgrade restarts PostgreSQL and Redis on the pinned images. The data stays, since the major versions stay 18 and 8.
-
feat: the images of cluster mode run as a non-root user. The api, producer, ingester and migrate images run as
65532:65532, like the worker, and the web image runs nginx as the usernginx(UID 101) onnginxinc/nginx-unprivileged, still on port 80. The SQLite compose files (docker-compose.prod.sqlite.yml,docker-compose.dev.sqlite.ymland the one in Docker + SQLite) get a one-shotdata-permissionsservice that makes./.data/sqlitewritable by 65532 beforemigratestarts, on a fresh checkout and on a database that root created. The Helm chart's web pods set the sysctlnet.ipv4.ip_unprivileged_port_startto 0 (web.sysctls), so nginx binds port 80 on any container runtime, and mount anemptyDirat/tmpinstead of/var/cache/nginxand/var/run. Cluster user and permissions says what the SQLite folder, Docker monitors and ping monitors need (#175)- Upgrade note: if you run SQLite in cluster mode from your own compose file, the database in your data folder belongs to root, and the new images can't write to it:
migratestops withunable to open database file. Give the folder to 65532 once withsudo chown -R 65532:65532 .data/sqlite, or take the new compose file, or add itsdata-permissionsservice to yours and makemigratewait for it (mount a named volume in that service too). PostgreSQL installs need nothing. - Upgrade note: Docker lets the unprivileged web image bind port 80. On a runtime that doesn't, the
webcontainer stops withbind() to 0.0.0.0:80 failed (13: Permission denied): addsysctls: {net.ipv4.ip_unprivileged_port_start: "0"}to the service. The Helm chart sets it for you.
- Upgrade note: if you run SQLite in cluster mode from your own compose file, the database in your data folder belongs to root, and the new images can't write to it:
-
feat: cluster mode on SQLite and PostgreSQL runs the api and
peekaping-probeinstead of the api, producer, worker and ingester: two processes fewer, and Redis only between the api replicas. The local probe's monitors are checked bypeekaping-probeinstances that hold the local probe's token (PROBE_BOOTSTRAP_TOKENon the api,PROBE_TOKENwith the same value on the probe,PEEKAPING_URLpointing at the api). The api stores what it takes, a push to a push URL or a probe's results, on the replica that took the request, one monitor's results at a time and in order, and the stats of a heartbeat are counted once, by the replica that stored it. A batch a probe retries on another replica within the hour is answered as a duplicate, through a window the replicas share in Redis. The replica that holds the leader lock (the Redis keypeekaping:producer:leader) runs the stale-monitor watchdog, the probe offline sweep and the push monitors' checks, and another replica takes over within about 15 seconds after a hard kill, and at once after a graceful stop. In cluster mode the local probe is online while one of its instances is connected, like a remote probe, so a stopped probe deployment raises the probe offline notification; if a cluster runs with no instance at all, the leader reports the local probe offline once, afterPROBE_OFFLINE_AFTER(3 minutes by default, now read by the api), and logsthe local probe has N active monitors and no instance: deploy peekaping-probe with PROBE_TOKEN set to PROBE_BOOTSTRAP_TOKEN. To check more monitors, add probe replicas. The producer, worker and ingester are removed in this release (see Removed). Single mode doesn't change. MongoDB installs move to SQLite or PostgreSQL withbun db import-mongofirst (see Removed). The Helm chart'sproducer,workerandingestervalues becomeprobe.*, with a Secret<release>-probe-tokenthat the chart makes and keeps across upgrades, orprobe.token.existingSecretandprobe.token.existingSecretKeyfor one of your own. The prod and dev Docker Compose stacks for SQLite and PostgreSQL get aprobeservice in place of the producer, worker and ingester, and stop at start untilPROBE_BOOTSTRAP_TOKENis in.env.make devruns the api, a probe and the docs watcher. One api stores about 2,000 probe results a second on PostgreSQL 17 and on SQLite, where the ingester queue stored about 1,350 a second on PostgreSQL; a soak of 3 probe instances and 1,000 monitors at 20 seconds, with one instance stopped, missed no check and kept the api's heap at 14 to 17 MiB. See Upgrading to probes, Probe, Deployment modes and ADR 0002 (#254)- Upgrade note, Docker Compose on SQLite or PostgreSQL: add
PROBE_BOOTSTRAP_TOKEN=<the output of openssl rand -hex 32>to.env(at least 32 characters), take the new compose file from Docker + SQLite or Docker + PostgreSQL, and rundocker compose pullanddocker compose up -d --remove-orphans. Theprobeservice replaces theproducer,workerandingester. If you keep your own compose file, delete those three services, add theprobeservice from Upgrading to probes, and move the Docker socket mount,group_addand pingsysctlsof theworkerto it. Rundocker compose up -d --scale probe=3to check more monitors. - Upgrade note, Helm: remove the
producer,workerandingesterblocks from your values file and set what they held onprobe(probe.replicaCount,probe.resources, andprobe.sysctlsin place ofworker.sysctls); a leftover block is ignored.helm upgradecreates the<release>-probe-tokenSecret with a random token and keeps it from then on. If you render the chart without the cluster (helm template, Argo CD), make a Secret with a token of at least 32 characters and name it inprobe.token.existingSecret, or each render makes a new token. - Upgrade note: the order is to set the token, start the probes, upgrade the api, and remove the producer, worker and ingester. The compose files and the chart do it in one command, so no monitor goes unchecked for longer than the probes take to connect. A monitor goes stale only after three missed intervals.
- Upgrade note: a cluster that runs with no probe instance has no checker for the local probe's monitors, so the leader reports the local probe offline once after
PROBE_OFFLINE_AFTERand says what to deploy. Push monitors need no instance. - Upgrade note: the watchdog can't make a probe check again, so a stale monitor in this layout gets its notification once the wait has passed, without the "checking again" step. A monitor whose probe is offline is skipped, so a stopped probe deployment gives one notification, not one for each monitor.
- Upgrade note: set
PROBE_OFFLINE_AFTERandERROR_REPORTINGon the api replicas. The producer isn't there to read them any more, and the probe sends no error reports.QUEUE_CONCURRENCYandPRODUCER_CONCURRENCYno longer size the checks:PROBE_CONCURRENCY(128 by default) does, for each probe instance. - Upgrade note: MongoDB installs don't upgrade this way. They move to SQLite or PostgreSQL with
bun db import-mongofirst (see Removed). - Rollback: put back the previous compose file or run
helm rollback, with the previous image tags. The upgrade adds no migration, so nothing in the database changes and there is nothing to restore. The local probe's token stays in the database and is harmless. If you came from a release without remote probes, restore your backup, because that upgrade also ran the migrations that add them.
- Upgrade note, Docker Compose on SQLite or PostgreSQL: add
-
chore: the compose files, Dockerfiles and scripts in the repository root are named after the mode they run (PP-317).
docker-compose.prod.<db>.ymlis nowdocker-compose.cluster.<db>.yml,docker-compose.bundle.<db>.ymlisdocker-compose.single.<db>.yml,Dockerfile.bundle.sqliteisDockerfile.single(the PostgreSQL one is gone, see below),startup.bundle.sqlite.shandstartup.bundle.user.sharestartup.single.shandstartup.single.user.sh, andscripts/test-bundle-user.shisscripts/test-single-user.sh. The make targets follow:make docker-prod-postgres,make docker-prod-sqlite,make down-prod-postgresandmake down-prod-sqliteare nowmake docker-cluster-postgres,make docker-cluster-sqlite,make down-cluster-postgresandmake down-cluster-sqlite.- Upgrade note: this matters only if you run the compose files or make targets from a clone of the repository. After a
git pull, use the new paths in yourdocker compose -fcommands and scripts. A stack that you started from the old file keeps running, and the new file manages it, because Compose names the project after the directory, not the file.
- Upgrade note: this matters only if you run the compose files or make targets from a clone of the repository. After a
-
feat: single mode's PostgreSQL runs in its own container (PP-317). There is one single-mode image, built from
Dockerfile.single, and it runspeekapingalone. WithDB_TYPEunset orsqliteit keeps its data in/app/data/peekaping.db, as before. WithDB_TYPE=postgresit connects to the PostgreSQL thatDB_HOST,DB_PORT,DB_USER,DB_PASSandDB_NAMEname, andDB_HOSTis required. Any otherDB_TYPEstops the container with a message.docker-compose.single.postgres.ymlruns the image beside the officialpostgres:17image, with a healthcheck,depends_onwithservice_healthyand the data in./.data/postgres17, a folder of its own, so that the repository's single PostgreSQL Compose file never startspostgres:17on the old bundle's cluster in.data/postgres.docker-compose.single.sqlite.ymlsetsDB_TYPE=sqliteitself, so a.envshared with cluster mode can't switch it.Dockerfile.single.postgresandstartup.single.postgres.share gone, and so is thepeekaping-bundle-postgresimage from the release workflows, the e2e jobs and the release candidate smoke test, which run the PostgreSQL case as the single image beside apostgres:17container. See Docker + PostgreSQL and ADR 0002.- Upgrade note:
peekaping-bundle-postgresgets no more tags. 0.0.47 is its last, and it stays on the registries, so a running install keeps working and a pull of that tag still works. To upgrade, move to the Compose layout withpg_dumpandpg_restore, as Moving from the PostgreSQL bundle says. The new stack keeps its data in.data/postgres17and leaves the bundle's.data/postgresas it is, so you can go back by starting the old container. Don't point thepostgresimage at the bundle's folder: its files belong to another user and were written on an older Debian release. - Upgrade note: a single-mode container now reads
DB_TYPE. Unset orsqlitekeeps SQLite, andDB_NAMEis ignored, as before.postgresconnects out to the serverDB_HOSTnames. Any other value stops startup with a message, includingpostgresql,mysqlandDB_TYPE=mongo. - Upgrade note: PostgreSQL's major upgrades go through the official image now, and single mode works with any PostgreSQL, a managed one included.
- Upgrade note:
-
feat: the single-mode image is now published as
0xfurai/peekaping(andghcr.io/0xfurai/peekaping), with the tagslatestand<version>, for amd64 and arm64 (PP-321). It waspeekaping-bundle-sqlite.DB_TYPEpicks SQLite or PostgreSQL, as before.- Upgrade note: in
docker run, replace0xfurai/peekaping-bundle-sqlite:latestwith0xfurai/peekaping:latest. In a compose file, change theimage:line the same way. The port, the data volume and the environment variables stay the same, so nothing else changes. - Upgrade note:
peekaping-bundle-sqlitegets the same tags in 0.0.48 and 0.0.49, and none from 0.0.50. A setup that keeps the old name stops getting updates then.
- Upgrade note: in
-
feat: the SQLite startup check runs
PRAGMA quick_checkinstead of the fullintegrity_check, about 2 seconds instead of 30 on a 1.5 GB database, and the shutdown no longer runs a check at all. When the check finds damage, the process logs the findings and stops, instead of writing to a damaged database. See SQLite recovery (#231, #416)- Upgrade note: an install that runs on a corrupt SQLite database today, logging
Database integrity check FAILEDand carrying on, stops at startup after the upgrade, and a container with a restart policy keeps restarting. Follow the recovery page, or setSQLITE_STARTUP_CHECK=offwhile you do.
- Upgrade note: an install that runs on a corrupt SQLite database today, logging
-
feat: a probe with no notification channels of its own sends its offline and online notifications to the channels of its active monitors, each channel once. Before, it sent nothing, and its monitors went unchecked in silence. Create probe no longer asks for channels; Edit probe keeps them (PP-329, #549)
- Upgrade note: installs whose probes have no channels start getting probe offline and online notifications on their monitors' channels. In cluster mode the local probe's offline notification reaches every channel of its monitors.
-
feat: a probe holds at most 64 instances. A new instance at the cap takes the place of the oldest one that isn't online; when all 64 are online, the API answers 429 and the probe tries again after a minute (#258)
-
feat: the setup screen asks for usage statistics and error reports with two ticked boxes and one short note that links to what's sent, instead of thirty lines of small print (PP-328)
-
feat: the Type select of a notification channel lists the channel types alphabetically (#268)
Removed
-
chore!: remove MongoDB as a storage backend (the database Peekaping keeps its data in).
DB_TYPE=mongoandDB_TYPE=mongodbare gone, with the Mongo repositories, thepeekaping-bundle-mongoimage, the MongoDB Docker Compose files (docker-compose.bundle.mongo.yml,docker-compose.dev.mongo.ymlanddocker-compose.prod.mongo.yml), themongoprofile ofdocker-compose.deps.ymland themaketargets for them. Move off MongoDB storage is now only the guide for moving to SQLite or PostgreSQL. The MongoDB monitor type, which checks other MongoDB servers, is not affected, and neither isbun db import-mongo(PP-277)- Upgrade note: a MongoDB install has to move to SQLite or PostgreSQL with
bun db import-mongofrom this release before it starts on SQL, and it stays on 0.0.47 until then. The command is in the migrate image and in the single-mode image, and the guide has the steps for the MongoDB bundle, the Docker Compose stack and a server of your own. 0.0.47 is the last release that runs on MongoDB. The import reads a copy of your MongoDB data folder and changes nothing in it, so keep the folder until you are sure you don't need it. - Upgrade note: with
DB_TYPE=mongoorDB_TYPE=mongodb, every Peekaping binary (the api,peekapingandbun) stops at startup with an error that says MongoDB storage was removed and gives the address of the guide, and so does the migrate image. The Helm chart refuses to render with either value. None of them writes to the database. A Docker Compose stack for MongoDB that pullslateststops at the next pull, so pin its image tags to 0.0.47 until you have moved. - Upgrade note:
peekaping-bundle-mongogets no new tags. The images already published stay where they are.
- Upgrade note: a MongoDB install has to move to SQLite or PostgreSQL with
-
chore!: remove the producer, worker and ingester, which stayed only for MongoDB storage. On SQLite and PostgreSQL, cluster mode moves to probes in this release and they have nothing left to do (see the #254 entry above). The
cmd/producer,cmd/workerandcmd/ingesterprograms, thepeekaping-producer,peekaping-workerandpeekaping-ingesterimages, the queue-based check and ingest dispatch (themonitor:healthcheckandmonitor:ingestqueues), theirmaketargets anddev:*scripts and their three architecture pages are gone. Cluster mode is the api andpeekaping-probeinstances. Thenotificationqueue stays, and so does the leader lock keypeekaping:producer:leader, so replicas of an earlier release and of this one still share one leader during a rolling upgrade. The api no longer copies the choice to send error reports to Redis, because nothing else reads it: each api replica follows the stored choice and applies a change made on another replica within a minute. See Upgrading to probes and ADR 0002 (PP-277)- Upgrade note: the
peekaping-producer,peekaping-workerandpeekaping-ingesterimages get no new tags. 0.0.47 is the last release that has them, and the images already published stay where they are. On SQLite and PostgreSQL their work moves to the probes (see the #254 entry). - Upgrade note: a cluster that still runs them removes those services. For Docker Compose, delete the
producer,workerandingesterservices and add theprobeservice. For Helm, delete theproducer,workerandingesterblocks from your values file. Upgrading to probes has the steps. A stack that pullslatestkeeps getting the last images published, not new ones. - Upgrade note:
PRODUCER_CONCURRENCYis no longer read, so remove it from your.env.QUEUE_CONCURRENCYstays and now sizes only the api'snotificationqueue.PROBE_CONCURRENCYsets how many checks a probe instance runs at once.
- Upgrade note: the
Fixed
- fix: the monitor list, batch, create and update responses now include
notification_idsandtag_ids, likeGET /monitors/{id}, which now also returnspush_token(#191) - fix: in cluster mode, the hourly cleanup (heartbeats past the data retention period, expired stats, old notification history, old TLS information) ran on every api replica at the same time. It now runs once an hour however many replicas run: the replicas claim the hour with a Redis key (
peekaping:cleanup:<hour>, kept for two hours, where the hour is the time rounded to the nearest hour in UTC, so replicas whose clocks are less than an hour apart share one claim), and only the replica that wins logsRunning the hourly cleanup. The others log at debug level that another replica has it. A replica that can't reach Redis when the hour starts logs an error and skips that hour, and the next hour tries again. In single mode,peekapingruns the cleanup every hour in its process, with no Redis. The four cleanups are one job now, so they run one after the other, and the cron fires at the top of the UTC hour, which only moves the time in time zones offset by part of an hour. A failure to schedule the cleanup used to be ignored; it now stops startup with an error (#75) - fix: security: the API lets a page read its responses only from the origins in the new
CORS_ORIGINSsetting. Before, it answered every origin withAccess-Control-Allow-Origin: *, so a page on any site could read them.CORS_ORIGINSis a comma-separated list of origins, such ashttps://status.example.com,http://localhost:8383. Unset, it is the origin ofCLIENT_URL, and*alone allows any origin. A request from another origin is still served; its browser just won't hand the response to the page. Responses no longer carryAccess-Control-Allow-Credentials, which the API never needed because its token travels in theAuthorizationheader. An entry that isn't an origin stops startup, and/metricsstays unreadable from another origin. See API Server (#55)- Upgrade note: a setup where a page on an origin other than
CLIENT_URL's calls the API must setCORS_ORIGINSto that origin, or the browser refuses it. The web app the bundles, the cluster gateway and the Helm chart serve calls the API on its own origin and needs nothing. The dev Compose files setCORS_ORIGINStohttp://localhost:8383for the web container.
- Upgrade note: a setup where a page on an origin other than
- fix: security: the login lockout no longer trusts a client's
X-Forwarded-For. It counted failed logins under the leftmost address in that header, so a client could send a different one with every request and never be locked out, in every deployment. The API now believesX-Forwarded-ForandX-Real-IPonly from the proxies in the newTRUSTED_PROXIESsetting (comma-separated IP addresses and CIDR ranges; its own default is empty, which believes no header, as in the single-mode bundles, while the cluster compose files and the Helm chart set loopback and the private ranges when it is unset, and an explicitly empty value there trusts nobody). For a request from one of them, the client is the lastX-Forwarded-Foraddress that isn't a trusted proxy (the leftmost when all are), or theX-Real-IPwhen there is noX-Forwarded-For; anX-Forwarded-Forit can't read makes the client the proxy's own address. A request from any other address counts as coming from the address it connected from. A second counter now locks an account whatever addresses the guesses come from:BRUTEFORCE_ACCOUNT_MAX_ATTEMPTSfailed logins (100 by default) withinBRUTEFORCE_ACCOUNT_WINDOW(10 minutes) lock the account forBRUTEFORCE_ACCOUNT_LOCKOUT(1 minute). The count of failures is kept until the window, measured from the first failure, has passed, so until then each further failure after a lock ends locks the account again: guessing is bounded to the limit per window plus one attempt each time a lock ends, and after an attack stops the owner is back in within the window at most. An attacker who keeps going can keep the account locked. A successful login resets both counters. A login request body over 64 KiB is refused with 413. The cluster compose files and the Helm chart setTRUSTED_PROXIESfor their gateway, and the clusternginx.confnow replaces the client'sX-Forwarded-Forwith its address. See Reverse proxy and HTTPS (#50)- Upgrade note: if you run Peekaping behind your own proxy, make the proxy put the client's address last in
X-Forwarded-For(Caddy and Traefik do; plain nginxproxy_passforwards the client's own header, so addproxy_set_header X-Forwarded-For $remote_addr;) and setTRUSTED_PROXIESto the address the proxy connects from. For a proxy on the same Docker host, publish port 8383 on loopback (-p 127.0.0.1:8383:8383) and use the gateway of Docker's network, such asTRUSTED_PROXIES=172.17.0.1; don't trust that address while 8383 is published on all interfaces, because direct connections can arrive from it too. Until you do, the API sees every login as coming from the proxy, so one person's failed logins can lock an account for everyone. Installs with no proxy in front, and the cluster compose files and the Helm chart as shipped, need no change. A cluster install with its ownnginx.confneedsproxy_set_header X-Forwarded-For $remote_addr;in the/api/and/socket.io/locations, as the newnginx.confhas. An Ingress that passes the client'sX-Forwarded-Forthrough unchanged needsconfig.env.TRUSTED_PROXIES: ""in the Helm values. A malformedTRUSTED_PROXIESstops the API at start.
- Upgrade note: if you run Peekaping behind your own proxy, make the proxy put the client's address last in
- fix: security: login tokens and API keys are verified more strictly (#54).
- Login tokens are accepted only when signed with HS256. The signing secrets stay in memory and are read from the database again after a minute, instead of on every request, so a secret changed in the database is used within a minute.
- API keys are checked with SHA-256 and a constant-time compare, which cost far less than bcrypt on every request. A key created before this change keeps working, and its stored hash is replaced on its first use.
- A key with a usage limit is never accepted more often than its limit, also when requests arrive together: checking the limit and counting the use are one database update.
- A refresh token issued before a password change no longer works, so the sessions signed in with the old password can't renew. An access token already issued stays valid until it expires, within 15 minutes by default.
- Registrations that arrive together on a new install create one admin. The others fail with "admin already exists".
- The websocket reads the access token from the socket.io
authpayload. A token in the query string is no longer accepted, so it stops showing up in a proxy's access logs. The web app sends it that way. - Upgrade note: every user signs in again once, because refresh tokens issued by earlier releases are rejected.
- Upgrade note: after its first use, an API key is stored as a SHA-256 hash, which earlier releases can't check. After a rollback, recreate the keys used since the upgrade.
- Upgrade note: a script or tool that opens the websocket itself must pass the token as
auth: { token }instead ofquery: { token }.
- fix: creating a monitor, notification channel or maintenance window with
active: falsenow stores and returns it as inactive. Bun replaced the false value with the column'struedefault on insert, so creates always came back active; editing afterwards worked. Omittingactivewhen creating still produces an active resource (#394) - fix: the Helm chart's web pods copy the web app from the web image you set. Before, an init container always used
0xfurai/peekaping-web:latest, so pinningweb.image.tagstill served the latest web app. The chart's install notes now say it runs cluster mode and link to the Kubernetes (Helm) docs page - fix: on a fresh Helm install, the api, producer, worker and ingester pods no longer crash-loop until the migrate Job has created the database tables. They wait in an init container,
wait-for-migrations, until the Job has applied every migration. On an upgrade, the new pods wait for the new release's migrations while the old pods keep serving - fix: in the PostgreSQL Docker Compose files (
docker-compose.prod.postgres.yml,docker-compose.dev.postgres.ymland the one in Docker + PostgreSQL), the api, producer and ingester start after themigrateservice has finished. Before, they waited only for the database and Redis, so they could start before the migrations had run. If you use your own compose file, addmigratewithcondition: service_completed_successfullyto theirdepends_on - fix:
helm upgradeno longer fails while the last migrate Job still exists. Each install, upgrade and rollback runs its own Job,<release>-migrate-<revision>, and Helm removes the previous one, so the Kubernetes (Helm) page drops the step that deleted the Job first - fix: a migration that is interrupted at startup or in the Helm chart's migrate Job (
docker stop, an out-of-memory kill, a lost node) no longer leaves the database locked on PostgreSQL and SQLite. Before, it left a row inbun_migration_locks, and every later start failed withmigrations table is already locked. On PostgreSQL the lock now disappears with the migration's database connection, and a migration that starts while another one runs waits for it, up to 10 minutes (MIGRATE_LOCK_TIMEOUT), instead of failing. SQLite takes no lock. A database that is already stuck on that error starts after the upgrade, because a leftover row is ignored, sobun db unlockis no longer needed there. MySQL doesn't change. See Migrate (#226)- Upgrade note: on PostgreSQL and SQLite,
bun db lockno longer holds backmigrateorrollback, because they don't readbun_migration_locksany more. If you ranbun db lockto freeze migrations during maintenance, stop the migrate container or Job instead. On MySQL,bun db lockstill works as before.
- Upgrade note: on PostgreSQL and SQLite,
- fix: SQLite's connection settings (foreign keys and the busy timeout among them) no longer drop after a cancelled query, so deletes keep cascading and writes wait for the lock instead of failing with "database is locked". Transactions now take the write lock when they begin (#265, #485; also #234, #256, #261)
- fix: in single mode,
peekapingrefuses apeekaping-probethat connects with the local probe's token, with a 401 whose message says why (thepeekapingprocess checks the local probe's monitors, so use a remote probe's token). Before, such a probe was accepted and checked the local probe's monitors a second time. The probe logs the message, andpeekapinglogs a warning for each refused call.peekapingalso no longer storesPROBE_BOOTSTRAP_TOKEN: it logs one warning at startup when it is set, and a value shorter than 32 characters no longer stops startup. See Deployment modes (PP-315)- Upgrade note: a single-mode install that ran a
peekaping-probewith the local probe's token must give that probe a remote probe's token, or stop it. A token thatPROBE_BOOTSTRAP_TOKENstored earlier stays in the database, and cluster mode accepts it again, but single mode refuses it.
- Upgrade note: a single-mode install that ran a
- chore:
make helpandmake docker-configslist only targets that exist:make docker-dev-postgres,make docker-dev-sqlite,make docker-prod-postgresandmake docker-prod-sqlite, with thedown-*targets, instead ofdev-*andprod-*names and the missingdocker-statusanddocker-ps.make switch-to-postgresandmake switch-to-sqlitestart the development environment instead of failing with "No rule to make target". Every Docker target in the Makefile, and the scripts undertest_utils, rundocker compose, so they work on a machine that has only the Compose plugin (PP-322) - fix: maintenance no longer lowers the uptime % read from stats. Each check in a maintenance window counted twice in the total, so one Up and one Maintenance check showed 66.67% uptime instead of 100%. Maintenance checks count as Up, never as Down. This fixes the uptime % on the monitor page (24h, 7d, 30d, 365d and under the chart), the badges and each location on a status page; monitors that had maintenance show higher, correct numbers. A monitor's own 24h uptime on a public status page is read from heartbeats and still counts Maintenance as Down (PP-343)
- fix: security: a PostgreSQL monitor with
sslmode=verify-caorverify-fullverifies the server's certificate. Before, it connected without checking any certificate. An unknownsslmodenow fails the check, and a PostgreSQL or SQL Server query that isn't aSELECTis refused before connecting (PP-293, PP-294)- Upgrade note: a PostgreSQL monitor with
verify-caorverify-fullagainst a server whose certificate doesn't verify goes Down. Userequireto encrypt without verifying.
- Upgrade note: a PostgreSQL monitor with
- fix: security: the notification channel API returns each saved secret (bot tokens, passwords, API keys, secret webhook URLs) as
********. The edit form shows it as an empty field; saving or testing without retyping it keeps the saved secret, but only while the channel's type and endpoint stay the same, so a saved secret is never sent somewhere new (#124)- Upgrade note: a script that reads a channel's secret from the API gets
********now. A script that sends a changed endpoint has to send the secrets again.
- Upgrade note: a script that reads a channel's secret from the API gets
- fix: security: secrets no longer reach the logs or the Test notification response. A failed send no longer shows its request URL, which holds the bot token for Telegram, and monitor configs and queue payloads are no longer logged (#52)
- fix: security: a badge's color options take only hex colors and shields.io color names, so a crafted badge URL can't add markup to the SVG. Any other value draws the default color.
color=blueon status, uptime and certificate expiry badges now draws blue (#49) - fix: security: a malformed socket.io message from a signed-in client no longer crashes the API (#51)
- fix: security: the API closes slow clients (10 seconds for the request headers, 30 for the whole request, 120 for an idle connection) and answers 413 to a request body over 1 MiB (#53)
- fix: in cluster mode each notification is sent once, by one api replica. Before, every replica sent it, so three replicas sent three copies (#29)
- fix: a Down monitor stays Down until a check comes back Up. Before, it could slip back to Pending (a Docker container restarting, or max retries raised while Down), and then its Up went unannounced or its Down was sent twice (#133)
- fix: the resend interval counts only Down heartbeats of a Down monitor, so a Pending monitor no longer resends a Down notification it never sent (#132)
- fix: a monitor that is Down when its maintenance window ends sends a Down notification, and one that comes back Up after a window gets an event in its history (PP-131)
- fix: switched-off notification channels no longer send notifications (#418)
- fix: a probe's offline notification opens one incident on PagerDuty, Opsgenie, PagerTree and Grafana OnCall, keyed
Peekaping/<probe id>, and its online notification closes it as the channel's auto-resolve setting says. The help under the auto-resolve settings of PagerDuty and PagerTree says so. Before, Opsgenie sent it as a test alert, PagerTree and PagerDuty opened a new one on each send, and Grafana OnCall titled it "General notification" (PP-274, PP-280, PP-282, PP-285)- Upgrade note: webhooks and custom templates see
monitor.status: 1on a probe's online notification, where it was always0.
- Upgrade note: webhooks and custom templates see
- fix: a probe's notifications link to the Probes page, not to a monitor page that doesn't exist, and Slack's rich messages for them get a button. The webhook payload carries
monitor.type: "probe"for them (#259) - fix: an HTTP check runs with its own monitor's timeout, TLS, proxy, mTLS and NTLM settings. Before, checks running at once could share one client and report another monitor's result (#57)
- fix: an OAuth2 client credentials check with "Authorization Header" sends the client ID and secret only in the header, so strict servers such as Authelia accept it (#276) (thanks @slopgobblercobbler)
- fix: HTTP monitors send
Content-Typeonly with a request body, unless you set it in the headers. Some servers answered 400 to a bodyless GET withContent-Type: application/json(#415) - fix: TCP, DNS, MQTT, PostgreSQL and proxy addresses, and SMTP notification channels, work with IPv6 hosts. Before, every check failed with "too many colons in address" (#128)
- fix: MongoDB, SQL Server and Kafka checks go Down within the monitor's timeout when the server never answers. Before, MongoDB took 30 seconds, Kafka about two minutes, and SQL Server hung. Kafka SCRAM-SHA-256 and SCRAM-SHA-512 work, and SQL Server passwords with
;,=,{or}are passed right (PP-291, PP-294, PP-327) - fix: creating or updating a monitor saves the monitor and its notification channel and tag links together, or nothing. Before, a failed link left the monitor saved and scheduled, and a monitor's first notification could find no channels (#86)
- fix: the api shuts down cleanly on SIGTERM within 30 seconds: it finishes the requests in flight, then closes the websocket, the jobs, the event bus and the database. The cluster compose files and the Helm chart give it 35 seconds to stop (#84)
- fix:
bun dband the migrate image carry the SQL migrations in the binary, so they run without the source tree (#230) - fix: a period with no heartbeats shows a dash with a "No data" tooltip on the monitor page, the chart and status pages, and badges say "N/A" in grey, instead of 0% uptime. The uptime API answers
nullfor it (PP-344)- Upgrade note: API clients that read
stats/uptimeor a status page'suptime_24has a number have to handlenull.
- Upgrade note: API clients that read
- fix: the response time chart of a monitor checked less often than once a minute keeps its buckets in place between refreshes, and the first point of the 24-hour and 1-week charts is no longer empty (PP-345)
- fix: in single mode the Probes page shows the local probe online (PP-325)
- fix: the heartbeat bar fits its bars again when the sidebar opens or closes (#267)
- fix: an empty password field shows the "Password is required" message in every language. Its translation key was missing (#357) (thanks @larswmh)
Raw Commit List
- docs: complete the changelog for 0.0.48 (#570) (Thanks @0xfurai) 92a2732
- test(server): let the SQL Server Dial Timeout test outlast a short WAITFOR (#566) (Thanks @0xfurai) 20455d9
- PP-321 [1/2] ci: publish the single-mode image as 0xfurai/peekaping (#559) (Thanks @0xfurai) 3721cff
- ci: run jobs on GitHub-hosted runners instead of nexus-runner (#565) (Thanks @0xfurai) 82a3053
- PP-321 [2/2] docs: name the single-mode image 0xfurai/peekaping (#560) (Thanks @0xfurai) 4d1735b
- PP-344 [2/2] fix(web): show a dash with a "No data" tooltip for missing uptime (#563) (Thanks @0xfurai) 1c20302
- PP-344 [1/2] fix(server): answer null uptime for a period with no heartbeats (#562) (Thanks @0xfurai) eb331f2
- PP-131 fix(server): notify when a monitor is Down as its maintenance window ends (#561) (Thanks @0xfurai) a7b7edc
- PP-317 feat: run single mode's PostgreSQL in its own container, and name files after single and cluster mode (#558) (Thanks @0xfurai) eaf7d8b
- PP-331 [2/2] feat(web): name the probe instance in the heartbeat tooltip (#556) (Thanks @0xfurai) e981bcb
- PP-331 [1/2] feat(server): record the probe instance that ran each check (#555) (Thanks @0xfurai) 535321e
- PP-345 fix(server): align chart buckets to the check interval from the epoch (#554) (Thanks @0xfurai) 2ba0ab5
- PP-324 [3/3] feat(web): preselect every enabled probe on a new monitor (#552) (Thanks @0xfurai) 383daf9
- PP-324 [2/3] feat(web): add Assign to all monitors to Create probe and the Probes page (#551) (Thanks @0xfurai) 82918a8
- PP-324 [1/3] feat(server): assign a probe to all monitors on its first hello (#550) (Thanks @0xfurai) bab951d
- PP-325 fix(server): report the local probe online in single mode (#548) (Thanks @0xfurai) 69f2a18
- PP-343 fix(server): count each maintenance check once in uptime (#547) (Thanks @0xfurai) 35bfa59
- PP-292 test(server): run the RabbitMQ executor against a real broker (#527) (Thanks @0xfurai) 454fcc9
- PP-329 fix(web): label the notification channel picker, so main's probe channel e2e can find it (#549) (Thanks @0xfurai) 9499344
- PP-322 fix: list only real make targets and call docker compose (#546) (Thanks @0xfurai) 102f0cb
- PP-330 [3/3] feat(web): show probe availability on the Probes page and a probe page (#541) (Thanks @0xfurai) b48d027
- PP-330 [2/3] feat(server): add probe availability routes with uptime (#540) (Thanks @0xfurai) dba1379
- PP-330 [1/3] feat(server): record probe status changes and instance connections (#539) (Thanks @0xfurai) faaeefc
- ci(e2e): keep matrix out of job-level if conditions (#545) (Thanks @0xfurai) 453da90
- PP-329 fix: notify a probe's monitors' channels when the probe has none, and drop channels from Create probe (#543) (Thanks @0xfurai) 8f27334
- PP-338 docs: propose ADR 0006, outages drive notifications (#533) (Thanks @0xfurai) b58fe4b
- PP-294 fix(server): bound MongoDB and SQL Server checks by their timeout, carry escaped SQL Server passwords, and test both against real servers (#537) (Thanks @0xfurai) 7c6c567
- feat(docs): restyle the docs site to match the landing page (#544) (Thanks @0xfurai) a09176d
- PP-293 fix(server): verify Postgres certificates for verify-ca/verify-full, and test Postgres and MySQL against real servers (#529) (Thanks @0xfurai) 57f6cc7
- feat(landing): redesign the landing page (#538) (Thanks @0xfurai) 55e7ce7
- PP-333 docs: propose serving status pages from the server in ADR 0006 (#534) (Thanks @0xfurai) 735fb3c
- ci(e2e): run the bundle jobs on PRs only when bundle files change (#536) (Thanks @0xfurai) 1796e9e
- ci(e2e): cache the Playwright browsers (#535) (Thanks @0xfurai) b4192cf
- ci(e2e): rerun the suite on the same database only on pushes to main (#532) (Thanks @0xfurai) 5164840
- PP-327 fix(server): report a Kafka check that times out while connecting the same way every time (#530) (Thanks @0xfurai) b825235
- PP-328 fix(web): shrink the setup screen's usage statistics and error reports section (#531) (Thanks @0xfurai) da6b329
- docs(ticket): work in the session's worktree instead of adding one (#528) (Thanks @0xfurai) 33870cd
- PP-315 fix(server): refuse the local probe's token in single mode (#525) (Thanks @0xfurai) dff5fa5
- PP-285 fix(web): say that PagerTree auto-resolve also covers a probe coming back online (#524) (Thanks @0xfurai) d4d4c29
- PP-291 fix(server): bound the Kafka check by its timeout, make SCRAM work, and test against a real broker (#526) (Thanks @0xfurai) c0217a6
- PP-277 [3/3] refactor!: remove the producer, worker and ingester (#520) (Thanks @0xfurai) 2e315a4
- PP-277 [2/3] refactor!: remove MongoDB storage (#519) (Thanks @0xfurai) 1f93951
- PP-277 [1/3] test(server): seed the import tests with raw MongoDB documents (#518) (Thanks @0xfurai) 89f744e
- PP-290 test(server): run executor integration tests against real services in CI (#522) (Thanks @0xfurai) f2a15e8
- docs: drop the project session-name rule (#523) (Thanks @0xfurai) e383cca
- chore(ticket): run subagents at high effort (#517) (Thanks @0xfurai) e0df660
- PP-142 docs: decide workspaces and data isolation in ADR 0005 (#516) (Thanks @0xfurai) f8e9b8e
- PP-280 fix(server): open and close a probe's alert on Opsgenie, PagerTree and Grafana OnCall (#515) (Thanks @0xfurai) 2d00f0b
- fix(web): say that PagerDuty auto-resolve also covers a probe coming back online (PP-282) (#514) (Thanks @0xfurai) 71c81d2
- PP-274 fix(server): resolve a probe's PagerDuty incident when it comes back online (#513) (Thanks @0xfurai) 61b89cd
- #128 fix(server): build IPv6 host addresses with net.JoinHostPort (#510) (Thanks @0xfurai) 8e90690
- #264 [3/3] feat(server): store supported types per probe instance (#511) (Thanks @0xfurai) b4abbba
- docs: move the issue tracker back to Linear (#512) (Thanks @0xfurai) 60d6c0d
- #258 feat(server): cap the instances a probe can register (#500) (Thanks @0xfurai) 92c6645
- ci: run short jobs on nexus-runner (#473) (Thanks @0xfurai) bcee657
- fix(server): tell a lost and regained leadership from one that never ended (#509) (Thanks @0xfurai) edf9eb7
- #264 [2/3] feat: add a slim peekaping-probe build (#503) (Thanks @0xfurai) ad60473
- #264 [1/3] refactor(server): keep the server's storage and queue code out of the probe (#502) (Thanks @0xfurai) 004279c
- test(server): a Down monitor that keeps reporting Pending resends on its interval (#508) (Thanks @0xfurai) dd448fe
- #254 [3/3] docs: describe cluster mode with probes (#506) (Thanks @0xfurai) 648c886
- #254 [2/3] feat: run cluster mode as api replicas and probes on SQL storage (#505) (Thanks @0xfurai) 70fecac
- #254 [1/3] refactor(server): move the leader election to a shared package (#501) (Thanks @0xfurai) 976fde6
- #263 [2/2] feat(web): mark a probe that doesn't run its monitor's type (#498) (Thanks @0xfurai) 77b2780
- #263 [1/2] feat(server): say which probe doesn't run a stale monitor's type (#497) (Thanks @0xfurai) ace0bbe
- #260 test(server): guard that a retry on another api replica is a duplicate (#496) (Thanks @0xfurai) 1dc279b
- #259 fix(server): link probe notifications to the probes page (#495) (Thanks @0xfurai) 9b91541
- #133 fix(server): keep a Down monitor Down until it's Up (#494) (Thanks @0xfurai) 3b3b88b
- #132 fix(server): count only Down heartbeats toward the resend interval (#493) (Thanks @0xfurai) 73047e4
- docs: say which location rules keep one probe from raising an alarm (#492) (Thanks @0xfurai) 1b646a6
- #35 [2/2] feat(web): choose a monitor's location rule (#489) (Thanks @0xfurai) d1ceeff
- #35 [1/2] feat(server): judge a monitor by its location rule (#488) (Thanks @0xfurai) fff42fe
- #253 [2/2] docs: add the remote probes page and the release notes (#491) (Thanks @0xfurai) 2a83575
- #253 [1/2] test(e2e): run a real remote probe against the stack (#487) (Thanks @0xfurai) 5e561be
- #257 feat: route the probe protocol through the gateways and the chart (#486) (Thanks @0xfurai) 9ab83e6
- #265 fix(server): keep SQLite's settings on every connection and take the write lock at begin (#485) (Thanks @0xfurai) 2b3ade6
- #268 fix(web): sort the notification channel types by label (#482) (Thanks @0xfurai) 764fe9a
- #269 feat(web): link the sidebar version to its GitHub release (#481) (Thanks @0xfurai) 19fdaf8
- #267 fix(web): recount heartbeat bars when the sidebar opens or closes (#483) (Thanks @0xfurai) da5ff6d
- #36 [4/4] feat: show each location on status pages (#479) (Thanks @0xfurai) ab3e8aa
- #36 [3/4] feat(web): show each location on the monitor page and pick several probes (#478) (Thanks @0xfurai) 6dc0a76
- #36 [2/4] feat(server): let a monitor run on several probes, judged per location (#477) (Thanks @0xfurai) a86c9e3
- #36 [1/4] feat(server): record the probe on heartbeats and stats (#476) (Thanks @0xfurai) cd60d79
- feat(ticket-loop): act on the first failure or comment, and handle Codecov (#474) (Thanks @0xfurai) 8973716
- #33 [3/3] feat(server): add the peekaping-probe binary and image (#471) (Thanks @0xfurai) 37ec7d2
- #33 [2/3] feat(server): add the probe client: hello, assignments, buffered results (#470) (Thanks @0xfurai) a22814c
- #33 [1/3] refactor(server): read the scheduler's monitors through a source (#469) (Thanks @0xfurai) f5b0bec
- #123 chore(ci): set Codecov's patch target and project threshold (#475) (Thanks @0xfurai) 48b5c04
- #125 test(server): prove a notification channel PATCH keeps the fields it wasn't sent (#468) (Thanks @0xfurai) aa37747
- #34 [2/2] feat(web): choose a monitor's probe in the monitor form (#466) (Thanks @0xfurai) 09217fd
- #34 [1/2] feat(server): assign monitors to probes (#465) (Thanks @0xfurai) 24e0d40
- #191 fix(server): return notification and tag IDs from every monitor endpoint (#464) (Thanks @0xfurai) 94cce3b
- #252 [4/4] feat(server): report a silent probe offline and leave its monitors to it (#459) (Thanks @0xfurai) 48e5a41
- #252 [3/4] feat(server): take a probe's check results and ingest them as heartbeats (#458) (Thanks @0xfurai) 6e9fda4
- #252 [2/4] feat(server): deal a probe's monitors to its instances over a long-poll (#457) (Thanks @0xfurai) 9b98444
- #252 [1/4] feat(server): add the probe protocol's wire package, token auth, hello and bye (#456) (Thanks @0xfurai) 12d59a5
- #232 feat: turn usage statistics and error reports on by default (#450) (Thanks @0xfurai) 4009fc4
- fix(ci): get main green again (#463) (Thanks @0xfurai) 0dceb89
- #32 [2/2] feat(web): add the probes page with token, rotate and delete (#453) (Thanks @0xfurai) 61a153a
- #32 [1/2] feat(server): add the probe registry, its tokens and the bootstrap token (#452) (Thanks @0xfurai) e7250bb
- #84 [3/3] fix(server): shut the producer, worker and ingester down gracefully (#437) (Thanks @0xfurai) 8e40073
- #84 [2/3] fix(server): shut cmd/api down gracefully (#436) (Thanks @0xfurai) b1ee17a
- #84 [1/3] refactor(server): move the shutdown tools into internal/shutdown (#435) (Thanks @0xfurai) 1768288
- refactor(web): one folder and one registry entry per monitor and channel type (ADR 0004) (#455) (Thanks @0xfurai) 1af7c24
- #75 fix(server): run the hourly cleanup once per cluster (#446) (Thanks @0xfurai) cb859ac
- #53 fix(server): add HTTP server timeouts and a request body limit (#442) (Thanks @0xfurai) e6978d1
- #124 [2/2] fix(web): show saved notification channel secrets as empty fields (#448) (Thanks @0xfurai) 32d2a93
- #124 [1/2] fix(server): hide notification channel secrets in the API (#447) (Thanks @0xfurai) e3098f8
- feat(ticket-loop): detect merge conflicts and watch idle PRs for two days (#451) (Thanks @0xfurai) 1ea2708
- #240 feat: warn about SQLite in cluster mode and choose the SQLite startup check (#429) (Thanks @0xfurai) 41810a2
- #86 [2/2] fix(server): write a monitor and its links in one transaction (#433) (Thanks @0xfurai) d928f94
- #86 [1/2] refactor(server): carry the SQL transaction in ctx (#432) (Thanks @0xfurai) 33b9fd4
- #55 fix(server): allow only configured CORS origins (#443) (Thanks @0xfurai) 76f7af0
- #52 fix(server): keep secrets out of logs and API error responses (#441) (Thanks @0xfurai) 78fe08b
- #49 fix(server): stop badge colors from injecting markup into the SVG (#439) (Thanks @0xfurai) bad0467
- #51 fix(server): stop malformed socket.io messages from crashing the API (#440) (Thanks @0xfurai) 12b67aa
- #233 [5/5] feat(web): ask for error reports on setup and in settings (#426) (Thanks @0xfurai) 6deb9bb
- #233 [4/5] feat(server): report panics to Sentry when the admin opts in (#425) (Thanks @0xfurai) 3200fde
- #232 [3/5] feat(web): ask for usage statistics on setup and in settings (#424) (Thanks @0xfurai) 003501b
- #232 [2/5] feat(server): send an opt-in daily usage ping (#423) (Thanks @0xfurai) b155971
- #232 [1/5] feat: add the usage ping Worker (#422) (Thanks @0xfurai) 01323a8
- #242 refactor(server): narrow the interfaces the producer and check loader depend on (#428) (Thanks @0xfurai) 0905d8a
- #54 fix(server): tighten JWT and API key verification (#444) (Thanks @0xfurai) dca9f88
- #57 fix(server): give each HTTP check its own client (#445) (Thanks @0xfurai) c7329b0
- #243 refactor(server): inject the notification provider registry (#430) (Thanks @0xfurai) 70a25d3
- #241 feat(server): log every HTTP request with a request ID (#431) (Thanks @0xfurai) 787d108
- #276 fix(healthcheck): send OAuth2 credentials in body only for client_secret_post (#419) (Thanks @joergenklopfer16) 5239092
- #50 fix(server): stop trusting a client's X-Forwarded-For in the login lockout (#408) (Thanks @0xfurai) c9a276d
- fix(server): skip inactive notification channels when sending (#418) (Thanks @0xfurai) 1bf830a
- #231 fix(server): run the SQLite quick check at startup and stop on corruption (#420) (Thanks @0xfurai) 1e6663f
- fix(server): skip the full SQLite integrity check at shutdown (#27) (#416) (Thanks @0xfurai) 17b4369
- docs(chart): prefix migrate logs with the pod and name the Job TTL value (#403) (Thanks @0xfurai) 0fe2c95
- fix(server): store active=false on create (#414) (Thanks @0xfurai) 867cf06
- #227 [2/2] docs: explain how to move off MongoDB with bun db import-mongo (#411) (Thanks @0xfurai) 7a7734c
- #227 [1/2] feat(server): add bun db import-mongo (#410) (Thanks @0xfurai) ba5f587
- fix(healthcheck): only send Content-Type with a request body (#415) (Thanks @0xfurai) 1d709d4
- #226 fix(server): stop a killed migrate from leaving the database locked (#407) (Thanks @0xfurai) 38dd280
- #201 chore: check that the docs snippets match the files they copy (#405) (Thanks @0xfurai) 6293afc
- #225 docs: fix the cluster-mode docs examples (#404) (Thanks @0xfurai) b020d88
- #175 feat: run the cluster images as non-root (#409) (Thanks @0xfurai) 138993d
- #230 fix(server): embed SQL migrations (#413) (Thanks @0xfurai) ab38cc3
- #224 feat(chart): publish the Helm chart with each release (#401) (Thanks @0xfurai) 2ad7cba
- #222 chore(chart): pin the bundled PostgreSQL and Redis images by digest (#400) (Thanks @0xfurai) 6e916ba
- #223 fix(chart): create a new migrate Job for each release revision (#399) (Thanks @0xfurai) d268a31
- #221 fix(chart): wait for the migrations before starting the app pods (#398) (Thanks @0xfurai) 0310819
- docs(chart): document the bundled PostgreSQL and Redis in values.yaml (#387) (Thanks @0xfurai) 9569fc0
- #29 fix(server): send each notification from one api replica (#392) (Thanks @0xfurai) 03bac16
- #30 [3/3] docs: add deployment modes, the upgrade to single mode and Helm pages (#390) (Thanks @0xfurai) 9ba0f36
- #30 [2/3] chore: prune the compose files at the repo root (#389) (Thanks @0xfurai) a321ec6
- #30 [1/3] fix(chart): seed the web root from the web image and render the chart in CI (#388) (Thanks @0xfurai) f301876
- #28 [2/2] docs: document the single-mode bundles (#377) (#384) (Thanks @0xfurai) 8cf1237
- #28 [1/2] feat(docker): run the bundles as the single-mode peekaping binary (#376) (#383) (Thanks @0xfurai) d779e3d
- #27 [3/3] feat(server): serve the web app from peekaping (#375) (#382) (Thanks @0xfurai) f6a7d61
- #27 [2/3] feat(server): add the single-mode peekaping binary (#381) (Thanks @0xfurai) d00452b
- #27 [1/3] refactor(server): build every binary from one checked app registration (#373) (#380) (Thanks @0xfurai) f4862ac
- #26 [2/2] feat(server): add the single-mode scheduler and check pool (#372) (#379) (Thanks @0xfurai) 96d9510
- #26 [1/2] refactor(server): cut the check, ingest and stale seams out of the cluster code (#371) (Thanks @0xfurai) 89a1766
- #25 feat(server): add the in-memory event bus (#369) (Thanks @0xfurai) ceab89a
- #83 [1/2] refactor(server): type the event bus and order events per monitor (#367) (Thanks @0xfurai) f6efd21
- #217 feat: make the mTLS CA optional on HTTP monitors (#366) (Thanks @0xfurai) 52709ce
- docs(ticket): write ticket keys as # instead of PP- (#365) (Thanks @0xfurai) 4d26d23
- docs: record the single-process runtime decision (PP-24) (#364) (Thanks @0xfurai) 7de9971
- docs: move the issue tracker from Linear to GitHub (#363) (Thanks @0xfurai) a35c28c
- PP-23 docs: deprecate MongoDB as a storage backend (#361) (Thanks @0xfurai) 9e4a0da
- ci(release): bump the version after the e2e tests pass (#358) (Thanks @0xfurai) 8eda994
- ci: name the Playwright jobs after what they build and test (#359) (Thanks @0xfurai) b1f5bec
- chore(server): regenerate the OpenAPI docs for 0.0.47 (#360) (Thanks @0xfurai) d06da52
- fix(release): point the demo at the release's bundle image (#362) (Thanks @0xfurai) dc1d748
- fix(web): add missing password-required texts (#357) (Thanks @Lars) 65f824b
Release Statistics
- 168 commits since 0.0.47
- 3 contributors
Contributors
Thanks to: @0xfurai @Lars @joergenklopfer16
Docker Images
GitHub Container Registry (GHCR)
- UI:
ghcr.io/0xfurai/peekaping-web:0.0.48/ghcr.io/0xfurai/peekaping-web:latest - API:
ghcr.io/0xfurai/peekaping-api:0.0.48/ghcr.io/0xfurai/peekaping-api:latest - Probe:
ghcr.io/0xfurai/peekaping-probe:0.0.48/ghcr.io/0xfurai/peekaping-probe:latest - Probe (slim):
ghcr.io/0xfurai/peekaping-probe:0.0.48-slim/ghcr.io/0xfurai/peekaping-probe:slim - Migrate:
ghcr.io/0xfurai/peekaping-migrate:0.0.48/ghcr.io/0xfurai/peekaping-migrate:latest
Single mode (GHCR)
- SQLite, or PostgreSQL in its own container:
ghcr.io/0xfurai/peekaping:0.0.48/ghcr.io/0xfurai/peekaping:latest - The old name,
ghcr.io/0xfurai/peekaping-bundle-sqlite, gets the same tags through 0.0.49 and none from 0.0.50. Switch toghcr.io/0xfurai/peekaping.
Docker Hub
- UI:
0xfurai/peekaping-web:0.0.48/0xfurai/peekaping-web:latest - API:
0xfurai/peekaping-api:0.0.48/0xfurai/peekaping-api:latest - Probe:
0xfurai/peekaping-probe:0.0.48/0xfurai/peekaping-probe:latest - Probe (slim):
0xfurai/peekaping-probe:0.0.48-slim/0xfurai/peekaping-probe:slim - Migrate:
0xfurai/peekaping-migrate:0.0.48/0xfurai/peekaping-migrate:latest
Single mode (Docker Hub)
- SQLite, or PostgreSQL in its own container:
0xfurai/peekaping:0.0.48/0xfurai/peekaping:latest - The old name,
0xfurai/peekaping-bundle-sqlite, gets the same tags through 0.0.49 and none from 0.0.50. Switch to0xfurai/peekaping.