Repository navigation
Releases: J000K3R/probeglass
Release list
v0.2.2
Container image: ghcr.io/j000k3r/probeglass:0.2.2
Every change since the first release, in full. This is
CHANGELOG.md
as it stands at this tag.
Changelog
All notable changes to this project are documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
What each release holds, one line each. Every version below is written out in
full; the release page for a tag carries its own section of this file, so what is
here and what the release says are the same document.
| Version | Released | In one line |
|---|---|---|
| [0.2.2] | 2026-10-18 | A symbol on every sensor, place and group — ours, yours, or searched for online — and one set of measurements for the whole dashboard |
| [0.2.1] | 2026-10-16 | A sensor under maintenance says so |
| [0.2.0] | 2026-10-16 | Maintenance windows, a second interface language, and the container as the only thing that ships |
| [0.1.0] | 2026-10-12 | The first release: sensors, history, incidents, alerts, accounts, and the dashboard |
[0.2.2] - 2026-10-18
Added
-
Dependencies between sensors — a power cut is one outage, not thirty. A sensor
can name another as the one it sits behind, and while that one is down the sensor is
excused: it opens no incident of its own, sends no alert, and the checks it fails
in the meantime do not count against its uptime. Its status still says what the probe
found, and its card carries a badge saying the downtime is a consequence rather than
an outage of its own.- Chains work: router → switch → the things on the switch. Circles and a parent
that does not exist are refused when the sensor is saved. - Only a confirmed outage counts. A parent that is merely flaky — one lost ping —
excuses nothing, or a single dropped packet on the router would quietly stop thirty
sensors from alerting. - The chain is walked, not just the first link, so a sensor two levels down is
excused by the same router. - A sensor behind a parent that recovers while it is still failing earns its own
incident back: it uses up its retries again and the new outage is dated from
after the parent came back. - Set in the sensor editor, where the picker leaves out the sensor being edited, and
in the configuration file asdepends_on. A file may declare them in any order.
This is the same mechanism as a maintenance window and not a second one. A window is
something a person said in advance; a parent that is down is something that happened
to the sensor rather than by it, and both mean the same thing to the arithmetic. So
the marker became the general one:results.maintisresults.excusednow, with
excused_reasonbeside it, and the rollup counts are named for what they are. The
boundary handling is shared, which is most of why this is not a parallel copy of the
window feature. - Chains work: router → switch → the things on the switch. Circles and a parent
-
A symbol on every sensor, place and group — one of ours, or a picture of your own.
A sensor, a site and a group's heading can each carry an icon, drawn beside the name
in every view: the cards, the list, the tree and the wall, and in the heading of the
sensor's own window. In the sensor editor the symbol stands in the name row itself,
as a button the size of the field: choosing one is a click on the picture that will
be drawn, not a trip to a field called "Icon" further down the form.- Two kinds of symbol. Twenty-six drawings ship inside the binary — router,
switch, server, NAS, container, cloud, printer, camera — and they follow the theme,
so one file is right in both themes and under all six accents. Beyond those, an
image can be uploaded: PNG, JPEG or GIF, at most 64 KiB and 512 pixels a side. - Uploaded images live in the database, not on disk. "One SQLite file, and
copying it is the whole backup" is a promise this project makes in three places,
and an icon in a directory beside the database would quietly make it false — the
copy would come back without them. They are small: a hundred at the cap are six
megabytes. - No SVG, and that is not a preference. An SVG is not an image but a document:
it may carry script and external references, and it would be served from this
dashboard's own origin, where that script would run with the session cookie of
whoever looked at it. What an upload may be is decided by reading the file, never
by its name or the type the browser claimed for it. - An image that something is still wearing cannot just disappear. Deleting one
is refused while sensors, sites or groups point at it, and the refusal says how
many. Saying "clear" as well takes it off them and deletes it in the same
transaction. - Groups have settings, and are still not rows. A group exists because somebody
typed its name on a sensor: no foreign key, no create step, and nothing to keep in
step when a sensor is renamed. What is stored about a group is its symbol, the
colour of its heading and its place in the order. Forgetting a group removes those
and leaves every sensor exactly where it was. - A group's colour is one of six names, never a hex value, for the reason the
accent setting is one: every colour in this interface exists twice, once per
theme, and a stored#f59e0bwould be the dark theme's amber drawn on somebody's
white page. - Built-in drawings are drawings and nothing else. They are the files that ship
in the binary and are served from the dashboard's own origin, so a test holds every
one of them to that: no<script>, noonload, no external reference. - The configuration file takes both, as
icon: builtin:routeron a sensor or a site.
It has to be able to: a sensor the file declares is written from the file whole on
every start, so a column the file could not name would be cleared each time the
daemon came up.probeglass doctorchecks the same references, because a file
doctor accepts has to be a file serve applies. - The backup carries all of it — the columns, the group settings, and the uploaded
images with their bytes. The image id is the one row in that document that keeps
its identity through a restore: a sensor's icon column holdsupload:7as plain
text with no foreign key behind it, so an image that came back under a different
number would leave every reference pointing at whatever else landed there. - The four example sensors a fresh installation starts with carry a symbol, and so
does the demo data set, so the feature is on the first dashboard anybody looks at
rather than in a settings page nobody opened.
Sensors and sites got a column each and groups a small table of their own; both are
migration 9 and apply themselves on the next start. A group was deliberately not
turned into a row with sensors pointing at it: that would make creating a group a
step of its own and reach into the configuration file, the backup, the example data
and every delete path, and a heading somebody typed is not worth that. - Two kinds of symbol. Twenty-six drawings ship inside the binary — router,
-
The picker searches Dashboard Icons when the symbol
you want is not one of the twenty-six. A field at the top of the picker takes a
name, and the results come back with their pictures beside them.Choosing one imports it: the picture is downloaded once and stored as an ordinary
uploaded icon — same table, same limits, same backup. That is the whole design and it
is deliberate. Drawing it from the CDN would put somebody else's server in the path
of every page load and take the image out of the one SQLite file a backup copies, so
what is taken from the catalogue is a picture, once, and nothing after that depends
on it being reachable.- The server asks, the browser never does. The catalogue's index is fetched and
cached in memory by the daemon and revalidated with a conditional request on every
search, so an icon added upstream is found by the search looking for it and the
usual search costs an empty response rather than 1.2 MB. The previews are served
through this installation's own route, which is also why the content policy still
allows images from this origin and nowhere else. - A container with no route out says so. The answer to a search carries an
offline flag rather than failing, and the field says the catalogue cannot be
reached and points at the site. The built-in drawings and everything already
uploaded keep working. - Only PNGs are taken, and they go through the same decode an upload does — no
SVG, ever, and a picture that would be refused as an upload is refused here too. - A picture larger than an icon may be is made to fit, not refused. The
catalogue's logos are not all 512 square — a wide one is 623×512, one is
1123×512 — and the limit that keeps uploads small would otherwise turn a search
result into an error. An imported picture is reduced to what the dashboard draws
(an area average, never enlarged); a file somebody picked themselves is still
refused when it is too big, because shrinking their own file without asking
would be a surprise. The same goes for a picture over the byte limit: it is read
from the CDN whole and reduced until it fits, rather than cut off at the icon
limit — a file cut in half cannot be decoded at all, and the refusal then quotes
the length of the cut instead of the picture's.
The catalogue is the work of Dashboard Icons
(Apache-2.0); the pictures are the logos of the projects they belong to, and the
picker credits both. - The server asks, the browser never does. The catalogue's index is fetched and
Changed
- The settings say which channel the installation is on. Settings → Help and
m...
v0.2.1
Container image: ghcr.io/j000k3r/probeglass:0.2.1
Every change since the first release, in full. This is
CHANGELOG.md
as it stands at this tag.
Changelog
All notable changes to this project are documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
What each release holds, one line each. Every version below is written out in
full; the release page for a tag carries its own section of this file, so what is
here and what the release says are the same document.
| Version | Released | In one line |
|---|---|---|
| [Unreleased] | — | Nothing yet |
| 0.2.1 | 2026-10-16 | A sensor under maintenance says so |
| [0.2.0] | 2026-10-16 | Maintenance windows, a second interface language, and the container as the only thing that ships |
| [0.1.0] | 2026-10-12 | The first release: sensors, history, incidents, alerts, accounts, and the dashboard |
[Unreleased]
Nothing yet.
0.2.1 - 2026-10-16
Added
-
A sensor inside a maintenance window says so. Its card and its row carry a
violet Maintenance badge beside the status, the card is ringed in the same
colour, and the tree, the wall and the map pick it up too. The status itself does
not change: a box that is not answering is still red, because that is true — what
the badge adds is the sentence somebody needs in order to decide whether to get up.This was a gap rather than a change of mind. 0.2.0's API published the flag from the
day the feature landed and the dashboard used it in one place, the "running now" mark
in the window list, and nowhere on a sensor — so a window did everything it promised
and nothing on screen admitted it. Reported by the first person to use the feature.
Fixed
- Switching the language no longer races the settings page. The page is rebuilt
when the language changes, and the redraw was not waited for, so a control could be
replaced under whoever was using it. The rebuild is awaited now. - Long sensor names are no longer broken mid-word. The card's header wraps the
badges onto a second line rather than squeezing the name until "Internet gateway"
comes out as "Inter net gate way".
[0.2.0] - 2026-10-16
Added
-
Maintenance windows. Tell the installation in advance that a box is allowed
to be away — the nightly snapshot, the Saturday the disk gets swapped — and the
checks that fail in that period open no incident, send no alert, and are left out
of the uptime figure.- A window covers one sensor or a whole group, repeats weekly on chosen
days or covers one stretch of time, and is created in Settings →
Monitoring → Maintenance windows. A weekly window is read in the zone the
person making it is in; the form fills that in from the browser, because a
container has no idea where its operator lives. - The probe keeps running. A window is not a pause: the result is still
recorded, so a box that comes back early shows up in its chart as having come
back. What is suppressed is the claim that somebody needs to do something. - The arithmetic follows the marker into the rollups. A sample left out of
today's figure has to stay left out of next month's too, and the raw rows are
gone by then — so the count is carried into the hourly and daily tables by the
rollup itself, in two columns rather than one, because a window is a period and
not a promise and the box may come back early. - An outage that was already open when a window begins is closed with a note
saying why, and nothing is sent — the message that opened it already went out.
If the box stays away, a new outage opens once it has failed its way past the
retries again, dated from after the window. A recovery inside a window is
silent, because no "down" was ever sent. - A window applies from the moment it is saved. The checks already recorded
are not rewritten, which is stated in the documentation rather than left to be
discovered.
- A window covers one sensor or a whole group, repeats weekly on chosen
-
A second interface language, and the switch for it. German joins English.
Every word the dashboard draws now comes out of a message catalogue rather than
out of the markup, so the next language is one file and one entry in a list,
and no code at all:internal/web/assets/i18n.<code>.jsbeside the English reference in
i18n.js, holding the same keys in the same order with the same placeholders.
A test compares the two key by key, in order, and compares both against the
keys the interface actually asks for — inindex.htmland in everyt('…')
call in the scripts — so a missing word and a mistyped one both fail the build
rather than reaching the screen.- The code in the two lists that already say what the interface offers: the Go
whitelist that validates a stored preference, and theavailablelist the
picker is drawn from.
-
The switch, in Settings and in the account menu, taking effect on the view
you are already looking at rather than on the next page load. Each language is
named in its own language — "Deutsch", not "German" — because the reader who
needs that entry is the one who cannot read the rest of the list. -
Dates, numbers and relative times follow it too.
Intlreads the browser's
language rather than the document's, so every formatter is now handed the chosen
one explicitly, and "5 min ago" under a card is a catalogue message like any
other word.
Changed
- A stored language follows the account, like the theme, the accent, the
density and the layout, rather than the installation. An installation that has
never chosen one behaves exactly as before and reads English.
Removed
-
The binary releases for darwin, windows, freebsd and linux. 0.1.0 shipped
seven zip files; a release now ships the container image and nothing else. The
archives promised a second way to run ProbeGlass that no document described,
and two things would not have survived it: thedockersensor talks to the
Docker Engine over a unix socket, which Windows does not have, anddoctor
tells you to run it withdocker exec. What is gone is the publishing of other
platforms, not the ability to compile —make buildstill builds for whatever
machine you are on.The
checksums.txtfile and its Sigstore signature went with the archives,
because there was nothing left to check a download of. The release workflow no
longer requests an OIDC token at all. The image itself is not signed; if that
is wanted it would be a small addition of its own.
Notes
Three surfaces are deliberately still English, and are meant to stay that way:
- the watchdog's own page, which has to be readable in exactly the situation
where the dashboard's scripts did not load; - the
doctorfindings, which are the CLI's output; - the channel types and their setup recipes, which the server sends as its own
text.
The probe messages the server writes into the history — HTTP 200 OK, no such host — are data rather than interface and read the same in every language.
[0.1.0] - 2026-10-12
The first release: a working foundation that monitors, records, and alerts, with
the structure in place to grow. What follows is the whole of it — the feature set
it lands with, and every change made on the way to the tag.
Added
- Sensors — HTTP(S), TCP (with optional TLS handshake and send/expect
exchange), ICMP ping, and DNS (A, AAAA, CNAME, MX, TXT, NS, SRV, PTR, with an
optional custom resolver). Each type validates its own target and options, in
the UI as well as on save. - Dashboard — live status grid fed by Server-Sent Events, per-sensor
response-time charts, uptime bars, sparklines, incident timeline, search,
grouping, and tags. Theme (auto/dark/light), accent colour, density, and
layout are selectable in Settings. - History — three resolutions with retention: raw samples for 7 days, hourly
rollups for 90 days, daily rollups forever. Exactly one resolution answers any
given range, so no probe is counted twice; gaps are reported as gaps. - Incidents — a state machine per sensor: a configurable number of
consecutive failures before an incident opens, repeat alerts while it stays
open, and a recovery alert naming how long the sensor was down. State is
rebuilt from the database on restart, so a restart neither loses an outage nor
re-announces one. - Alerts — Telegram, Discord, Matrix, ntfy, Slack, and generic webhooks with
HMAC-SHA256 request signing. Per-sensor channel assignment, per-sensor reminder
intervals, custom title and body templates with a safe fallback when a template
is broken, and a test button per channel. - Authentication — first-run setup screen, bcrypt password hashing,
HMAC-signed session cookies, an API key for scripts, cross-site protection for
cookie-authenticated writes, and an in-memory login rate limit. - Configuration — YAML file,
PROBEGLASS_*environment variables, and
command-line flags with a documented precedence order, plus day and week
duration suffixes for retention settings. - Declarative sensors and channels — declare them in the configuration file
and they are re-applied on every start. - API — a documented JSON API for sensors, history, incidents, channels,
settings, and an SSE event stream. Sensor updates are partial: a body changes
only the fields it sends, and an explicit empty value clears a field. - CLI —
serve,version,healthcheck(used by the image's
HEALTHCHECK, so no shell is needed), andseed --demofor a synthetic week
of history. - Packaging — multi-arch container image (amd64...
v0.2.0
Container image: ghcr.io/j000k3r/probeglass:0.2.0
Every change since the first release, in full. This is
CHANGELOG.md
as it stands at this tag.
Changelog
All notable changes to this project are documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
What each release holds, one line each. Every version below is written out in
full; the release page for a tag carries its own section of this file, so what is
here and what the release says are the same document.
| Version | Released | In one line |
|---|---|---|
| [Unreleased] | — | Nothing yet |
| [0.2.0] | 2026-10-16 | Maintenance windows, a second interface language, and the container as the only thing that ships |
| [0.1.0] | 2026-10-12 | The first release: sensors, history, incidents, alerts, accounts, and the dashboard |
[Unreleased]
Nothing yet.
[0.2.0] - 2026-10-16
Added
-
Maintenance windows. Tell the installation in advance that a box is allowed
to be away — the nightly snapshot, the Saturday the disk gets swapped — and the
checks that fail in that period open no incident, send no alert, and are left out
of the uptime figure.- A window covers one sensor or a whole group, repeats weekly on chosen
days or covers one stretch of time, and is created in Settings →
Monitoring → Maintenance windows. A weekly window is read in the zone the
person making it is in; the form fills that in from the browser, because a
container has no idea where its operator lives. - The probe keeps running. A window is not a pause: the result is still
recorded, so a box that comes back early shows up in its chart as having come
back. What is suppressed is the claim that somebody needs to do something. - The arithmetic follows the marker into the rollups. A sample left out of
today's figure has to stay left out of next month's too, and the raw rows are
gone by then — so the count is carried into the hourly and daily tables by the
rollup itself, in two columns rather than one, because a window is a period and
not a promise and the box may come back early. - An outage that was already open when a window begins is closed with a note
saying why, and nothing is sent — the message that opened it already went out.
If the box stays away, a new outage opens once it has failed its way past the
retries again, dated from after the window. A recovery inside a window is
silent, because no "down" was ever sent. - A window applies from the moment it is saved. The checks already recorded
are not rewritten, which is stated in the documentation rather than left to be
discovered.
- A window covers one sensor or a whole group, repeats weekly on chosen
-
A second interface language, and the switch for it. German joins English.
Every word the dashboard draws now comes out of a message catalogue rather than
out of the markup, so the next language is one file and one entry in a list,
and no code at all:internal/web/assets/i18n.<code>.jsbeside the English reference in
i18n.js, holding the same keys in the same order with the same placeholders.
A test compares the two key by key, in order, and compares both against the
keys the interface actually asks for — inindex.htmland in everyt('…')
call in the scripts — so a missing word and a mistyped one both fail the build
rather than reaching the screen.- The code in the two lists that already say what the interface offers: the Go
whitelist that validates a stored preference, and theavailablelist the
picker is drawn from.
-
The switch, in Settings and in the account menu, taking effect on the view
you are already looking at rather than on the next page load. Each language is
named in its own language — "Deutsch", not "German" — because the reader who
needs that entry is the one who cannot read the rest of the list. -
Dates, numbers and relative times follow it too.
Intlreads the browser's
language rather than the document's, so every formatter is now handed the chosen
one explicitly, and "5 min ago" under a card is a catalogue message like any
other word.
Changed
- A stored language follows the account, like the theme, the accent, the
density and the layout, rather than the installation. An installation that has
never chosen one behaves exactly as before and reads English.
Removed
-
The binary releases for darwin, windows, freebsd and linux. 0.1.0 shipped
seven zip files; a release now ships the container image and nothing else. The
archives promised a second way to run ProbeGlass that no document described,
and two things would not have survived it: thedockersensor talks to the
Docker Engine over a unix socket, which Windows does not have, anddoctor
tells you to run it withdocker exec. What is gone is the publishing of other
platforms, not the ability to compile —make buildstill builds for whatever
machine you are on.The
checksums.txtfile and its Sigstore signature went with the archives,
because there was nothing left to check a download of. The release workflow no
longer requests an OIDC token at all. The image itself is not signed; if that
is wanted it would be a small addition of its own.
Notes
Three surfaces are deliberately still English, and are meant to stay that way:
- the watchdog's own page, which has to be readable in exactly the situation
where the dashboard's scripts did not load; - the
doctorfindings, which are the CLI's output; - the channel types and their setup recipes, which the server sends as its own
text.
The probe messages the server writes into the history — HTTP 200 OK, no such host — are data rather than interface and read the same in every language.
[0.1.0] - 2026-10-12
The first release: a working foundation that monitors, records, and alerts, with
the structure in place to grow. What follows is the whole of it — the feature set
it lands with, and every change made on the way to the tag.
Added
-
Sensors — HTTP(S), TCP (with optional TLS handshake and send/expect
exchange), ICMP ping, and DNS (A, AAAA, CNAME, MX, TXT, NS, SRV, PTR, with an
optional custom resolver). Each type validates its own target and options, in
the UI as well as on save. -
Dashboard — live status grid fed by Server-Sent Events, per-sensor
response-time charts, uptime bars, sparklines, incident timeline, search,
grouping, and tags. Theme (auto/dark/light), accent colour, density, and
layout are selectable in Settings. -
History — three resolutions with retention: raw samples for 7 days, hourly
rollups for 90 days, daily rollups forever. Exactly one resolution answers any
given range, so no probe is counted twice; gaps are reported as gaps. -
Incidents — a state machine per sensor: a configurable number of
consecutive failures before an incident opens, repeat alerts while it stays
open, and a recovery alert naming how long the sensor was down. State is
rebuilt from the database on restart, so a restart neither loses an outage nor
re-announces one. -
Alerts — Telegram, Discord, Matrix, ntfy, Slack, and generic webhooks with
HMAC-SHA256 request signing. Per-sensor channel assignment, per-sensor reminder
intervals, custom title and body templates with a safe fallback when a template
is broken, and a test button per channel. -
Authentication — first-run setup screen, bcrypt password hashing,
HMAC-signed session cookies, an API key for scripts, cross-site protection for
cookie-authenticated writes, and an in-memory login rate limit. -
Configuration — YAML file,
PROBEGLASS_*environment variables, and
command-line flags with a documented precedence order, plus day and week
duration suffixes for retention settings. -
Declarative sensors and channels — declare them in the configuration file
and they are re-applied on every start. -
API — a documented JSON API for sensors, history, incidents, channels,
settings, and an SSE event stream. Sensor updates are partial: a body changes
only the fields it sends, and an explicit empty value clears a field. -
CLI —
serve,version,healthcheck(used by the image's
HEALTHCHECK, so no shell is needed), andseed --demofor a synthetic week
of history. -
Packaging — multi-arch container image (amd64, arm64, armv7) on a
distroless, non-root base with the dashboard embedded; static binaries for
Linux, macOS, Windows, and FreeBSD; CI running formatting, vetting,
go test -race, a seven-platform build, andgolangci-lint. -
The examples a fresh installation starts with stand at two places. The four
invented sensors carried a week of past but nowhere to be, so the map — the one
screen a new installation had nothing at all to say about — was empty behind the
button that draws it. They stand on the two places the demo data set uses, a
"Homelab" and a rented server somewhere else, each with real coordinates, because
a pin in the Gulf of Guinea is what a missing coordinate pair would look like and
is the one thing on that map that reads as a bug. Both carry the example mark with
the sensors, so the removal button takes them along rather than leaving two empty
pins behind, and the panel that counts the example data counts them now: a place
the removal would leave holding a sensor of your own stays, and the number on the
button is that same number. The names and the coordinates are asked of the demo
data set rather than copied from it, which matters the first time somebody loads
that data after a fresh install: the loader leaves a place that is already there
alone, so the two sets fold into one pin each instead of drawing a second on...
v0.1.0
Full Changelog: https://github.com/J000K3R/probeglass/commits/v0.1.0