v4.1.8 Changelog
See what's new in Kener v4.1.8, including hidden monitors for your team, SSL certificate details, image uploads through the API, and v4 API writes that match the dashboard
Release date: October 22, 2026
Important
Two changes can surprise you after the upgrade:
- Signed-in users whose role has
monitors.readnow see hidden monitors on the status site. All three built-in roles have it. See Hidden monitors for your team. - Some
/api/v4writes now return400for input that they stored before, and some writes now do more, for example send subscriber emails. See API changes.
New features
Hidden monitors for your team
A signed-in user whose role has monitors.read now sees hidden monitors on the status site. Before, a monitor set to Hidden in Status Page was hidden from everyone, your team too. Now you can keep private monitors in the same Kener install. All three built-in roles (admin, editor, member) have monitors.read. A user whose custom role does not have it sees what an anonymous visitor sees.
For these users, hidden monitors show on:
/and/{page_path}, when the monitor is on the page./monitors/{tag}, with its status bars, latency chart, and day details.- The affected monitors on
/incidents/{id}and/maintenances/{id}.
Badges, embeds, RSS feeds, sitemap.xml, events pages, inline events, the notifications list, and subscriber emails still hide hidden monitors for every viewer.
If you added hidden monitors to a public page, your signed-in members now see them there. Anonymous visitors still do not.
Kener now sends Cache-Control: private on each response to a signed-in request, unless the route sets its own header.
Warning
A CDN rule that ignores the origin's Cache-Control header can cache a signed-in page and serve its hidden monitors to anonymous visitors. Do not cache HTML or /dashboard-apis/* responses for requests that carry the kener-user cookie.
See Signed-in viewers.
SSL certificate details on the status page
SSL monitors now show the certificate state on the home page bars and on /monitors/{tag}:
| State | Meaning |
|---|---|
| SECURED | The certificate is valid and has not expired |
| INSECURE | The certificate expired or failed verification, for example self-signed, unknown CA, or wrong host name |
| UNKNOWN | The latest check read no certificate, for example after a connection error or a timeout |
The page also shows the time remaining, the expiry date, and the issuer. If the page stays open past the expiry, the label changes to INSECURE by itself.
The SSL monitor also gets three optional fields:
timeout(Timeout (ms)) sets the TLS handshake timeout. The default is10000. Before, a host that did not answer kept the worker waiting with no limit.tlsServername(TLS Server Name (SNI)) sets the SNI name for a monitor whose host is an IP address. Kener sends no SNI for a bare IP.allowSelfSignedCert(Allow Self-Signed / Untrusted Certificates) judges an untrusted certificate by its expiry only. The monitor can beUP, but the page still shows INSECURE, because a browser rejects that certificate.
A DOWN threshold of 0 hours is now allowed. latency is still the TLS handshake time, so latency alerts, badges, embeds, and API data do not change. See SSL Monitor. Thanks to @phatlet.
Upload images through the API
POST /api/v4/images takes an API key and a base64 image, and returns an /assets/images/<id> URL. Put that URL in page_logo, the site logo, the favicon, a monitor image, or any other image field. Before, only the dashboard could upload an image, so a tool that copies config between two Kener instances could not copy logos.
curl -X POST https://your-kener.com/api/v4/images \
-H "Authorization: Bearer $KENER_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"base64\":\"$(base64 < logo.png | tr -d '\n')\",\"mime_type\":\"image/png\"}"
- The image ID comes from the image content: the first 16 hex characters of its SHA-256, plus
.pngor.jpg. When you upload the same image again, Kener stores nothing new and returns the same ID. Old image IDs keep working. - When you leave out
max_widthandmax_height, Kener keeps the original size. The dashboard still fits images inside 256×256. - On MySQL, a migration changes
images.datafromtexttolongtext. Before, MySQL rejected an image over about 49 KB with "Data too long", in the dashboard too. The migration does nothing on PostgreSQL and SQLite.
See the API Reference.
Inline events and the event list together
Before, Display Events Inline controlled two things. On showed inline event cards and a side column with the event list, and it removed the top bar button. Off showed the button only. Now a separate Event List setting picks where the list of current and past events shows:
eventListPlacement |
Status pages (/ and /{page_path}) |
Monitor pages |
|---|---|---|
button (Top bar button, default) |
Top bar button | Top bar button |
aside (Side column) |
Column on the right | Top bar button |
hidden (Hidden) |
No list | No list |
So you can now show the inline cards and the top bar button together. An install that has no eventListPlacement yet keeps its home page look: Kener uses aside when inline events are on, and button when they are off. No migration runs.
If you have inline events on, two pages change:
- Custom status pages (
/{page_path}) now show the side column. Before, they showed no event list. - Monitor pages now show the top bar button. Before, they showed no event list.
The side column and the button badge now load the events of the current page when you switch between custom pages. Before, they kept the events of the first page. See Event display settings.
Set your own heartbeat secret
The heartbeat secret is the last part of the heartbeat URL, /ext/heartbeat/{tag}/{secret}. Before, the admin UI could only make a random one. Now you can type a known value in Heartbeat secret, or set type_data.secretString through the API. Then you can give a sender, such as a Prometheus Alertmanager rule, its URL before the monitor exists.
- The rule is at least 12 characters, using only
A-Z a-z 0-9 . _ ~ -. The admin form keeps the save button off while the value breaks the rule. POST /api/v4/monitorsandPATCH /api/v4/monitors/{tag}return400with codeBAD_REQUESTfor a new or changed secret that breaks the rule. Before, the API stored any value, for examplea/b?c.- A heartbeat monitor saved with no secret gets a generated one, and the response includes it. Before, a heartbeat monitor created through the API with no secret could never receive a heartbeat.
- A cloned heartbeat monitor gets a new secret. Before, the clone kept the secret of its source.
- A stored secret that a save sends back unchanged keeps working, even when it breaks the rule.
See Heartbeat secret.
Default Docker connection from `DOCKER_HOST`
A new Docker monitor now starts on the connection in DOCKER_HOST. Before, it always started on /var/run/docker.sock, so behind a socket proxy you typed the proxy address into each new monitor.
| Environment | A new Docker monitor starts on |
|---|---|
DOCKER_HOST not set |
socket, /var/run/docker.sock |
DOCKER_HOST=unix:///run/proxy/docker.sock |
socket, /run/proxy/docker.sock |
DOCKER_HOST=tcp://docker-socket-proxy:2375 |
tcp, docker-socket-proxy:2375 |
The same, with DOCKER_TLS_VERIFY or DOCKER_TLS set |
tls, docker-socket-proxy:2375 |
Any other value, for example ssh:// |
socket, /var/run/docker.sock, and the server logs one warning |
A saved Docker monitor keeps its stored connection. A monitor that you create through /api/v4/monitors does not use this default, so send connectionType and daemon. See Default connection and DOCKER_HOST.
State and impact flags in subscriber emails
A subscription_update email template can now change with the state and impact of an incident, for example a red banner when an incident starts and a green one when it is resolved. Mustache cannot compare strings, so most of the new variables are flags:
| Variable | Value |
|---|---|
update_state |
The state of the update, for example INVESTIGATING |
is_investigating, is_identified, is_monitoring, is_resolved |
true for the state of the update |
incident_impact |
The worst impact among the visible monitors of the incident: DOWN, DEGRADED, or empty |
is_down, is_degraded |
true for that worst impact |
is_maintenance |
true in maintenance emails |
Existing templates render as before. See subscription_update. Thanks to @codekuu.
API changes
Every v4 API write now runs the same code as the same dashboard write. Before, 11 route files wrote to the database themselves, so an API write could skip a step that the dashboard does.
Incidents:
POST /api/v4/incidents/{incident_id}/commentsnow emails subscribers. An update that reopens a resolved incident now clears its end time. Thanks to @andrewmitchell-automations.PATCH /api/v4/incidents/{incident_id}/comments/{comment_id}now changes the incident state and end time, the same as the dashboard.- A RESOLVED update dated before the incident start returns
400with "A RESOLVED comment cannot be dated before the incident start". This applies toPOSTandPATCH. DELETE /api/v4/incidents/{incident_id}/comments/{comment_id}stores the update asINACTIVE, notDELETED. Every v4 comment route returns404for an update that is notACTIVE.DELETE /api/v4/incidents/{incident_id}deletes the updates of the incident. Before, they stayed with statusDELETED.POST /api/v4/incidentskeeps anend_date_timethat you send. A monitor impact other thanDOWN,DEGRADED, orMAINTENANCEreturns400. The same monitor twice returns201. Before, it returned500and left a half-saved incident.- An incident end before its start returns
400, for a new end and a new start alike. Astart_date_timeof0returns400. APATCHwithtitle: ""keeps the old title.
Maintenances:
PATCH /api/v4/maintenances/{maintenance_id}with a new start keeps theCOMPLETEDandCANCELLEDevents. A new start in the same minute no longer sends a second "Maintenance Created" email.PATCH /api/v4/maintenances/{maintenance_id}/events/{event_id}returns400for a window shorter than one minute.
Monitors, pages, and site data:
POST /api/v4/monitorsandPATCH /api/v4/monitors/{tag}return400with codeBAD_REQUESTfor a proxy that is not anhttp://orhttps://URL, for examplesocks5://proxy.internal:1080. APATCHthat sends back the stored proxy unchanged still succeeds.- The same two routes now check the heartbeat secret. See Set your own heartbeat secret.
POST /api/v4/pagesandPATCH /api/v4/pages/{page_path}return400for a monitor tag listed twice, before they save anything. Before,POSTsaved the page andPATCHremoved all its monitors, and then both returned500.PATCH /api/v4/site/oidcSettingsnow clears the OIDC config cache, the same as the dashboard.
The same work changes three things in the dashboard:
- A manual edit of monitoring data now updates the latest status and queues an alert evaluation, the same as the API. So a manual DOWN edit can fire an alert.
- A RESOLVED update dated before the incident start fails before Kener saves anything. Before, the dashboard saved the update, returned a server error, and left the incident open. Alerts get the same check.
- An incident edit whose new start is after the stored end is now rejected.
Security
Three production dependencies move past critical advisories. Thanks to @codekuu.
| Package | Before | Now | Advisory |
|---|---|---|---|
protobufjs |
7.5.4 | 7.6.6 | GHSA-xq3m-2v4x-88gg (critical), GHSA-75px-5xx7-5xc7 (high) |
proxy-addr |
2.0.7 | 2.0.8 | GHSA-jqcg-44mw-7w3h (critical) |
shell-quote |
1.8.3 | 1.12.0 | GHSA-w7jw-789q-3m8p (critical) |
The fixes for hidden monitors that leaked to the public are in Bug fixes.
Improvements
Warning when a secret goes over plain HTTP
The API and Prometheus monitor editors now show a warning when the monitor uses a $SECRET variable and its URL or proxy uses plain http://. Kener sends that secret in clear text. HTTP targets still work. Thanks to @lokiee0.
Release images publish when one image fails
The release workflow builds four Docker images. Before, when one build failed, GitHub cancelled the other three. So the v4.1.6 and v4.1.7 images did not publish at release, and latest stayed on v4.1.5. Now each image builds on its own. The v4.1.7 images were published again on October 8, 2026.
Bug fixes
- Inline incidents no longer show hidden monitors. With inline events on, an ongoing incident on
/,/{page_path}, or/monitors/{tag}showed the name and image of a hidden monitor to anonymous visitors. Inline incidents now leave hidden monitors out for every viewer. - Event lists no longer match events through hidden monitors. Inline events and
/dashboard-apis/notifications?tags=…matched an event through any linked monitor, hidden or not. So anyone who knew the tag of a hidden monitor could read the titles of the events that affect only that monitor. Now an event shows only when it is global or links a monitor that is not hidden. On the page of a hidden monitor, signed-in users see only global events inline. They still see the events of that monitor in its day details. - Maintenance emails and
/maintenances/{id}no longer name hidden monitors. Maintenance emails listed hidden monitors, for exampleBilling DB(MAINTENANCE). The page data of/maintenances/{id}(__data.json) held the tag of a hidden monitor for anonymous visitors, but the page did not show it. - Subscribers get emails only for events that a status page shows. A non-global event whose linked monitors are all hidden no longer emails its title to subscribers. A non-global event with no linked monitors sends no email either.
{{incident_impact}}now counts only visible monitors, so a hidden DOWN monitor no longer makes the email say DOWN. - A masked secret no longer becomes the stored secret. Since v4.1.7, Kener masks the OIDC
client_secretand the captcha Secret Key in responses. On an instance with no stored secret, a save that sent the masked value stored the mask as the real secret. This happened, for example, when you copiedoidcSettingsfrom one instance to another. Kener now stores an empty string, and thePATCHresponse shows"". A copied captcha config withisEnabled: trueshows as misconfigured until you enter the Secret Key. - Latency and uptime alerts close their incidents. With incident creation on, a LATENCY or UPTIME alert opened an incident with no monitor and no link to the alert, so the incident never closed. These incidents now get the monitor with the
DEGRADEDimpact, and they close with the alert. The opening email of an alert incident now also carries the impact. - Status bars no longer return
500for bad numbers. On PostgreSQL,/dashboard-apis/monitor-barsand/dashboard-apis/monitor-barreturned500fordays=abcorendOfDayTodayAtTz=NaN. A bad value now gets the same default as a missing one. A timezone that Kener cannot read now falls back to UTC, so the browser no longer sendsNaN. Thanks to @anandghegde. - Embeds use the defaults for a bad
daysorheight./embed/monitor-{tag}and/embed/latency-{tag}sentdays=NaNto the API, and drew the latency chart atheight: NaNpx. A bad value now gets the default: 90 days and 128 px. Only whole numbers count, so?height=300pxand?days=30abcnow also get the defaults. - The duration label stays under the navbar. On incident and maintenance cards, the duration between the start and end dates drew on top of the navbar when you scrolled.
Documentation
- Every v4 monitor API example now sends
monitor_type. Before, the examples senttype, which the API ignores, so a copied example created anAPImonitor. - Monitors Overview now lists the Prometheus monitor.
- User Subscriptions now says that each incident update emails subscribers, from any source, and which events send no email.