maintenant 1.10.0 reduces what the public pages expose, bounds the checks declared by Docker labels, requires an explicit scope for the MCP write tools, and lets agents pin the certificate of their server. Several defaults change and can break an existing install: read Before you upgrade first.
Before you upgrade
- Accepted hosts. Without
MAINTENANT_ALLOWED_HOSTSorMAINTENANT_BASE_URL, the API and the dashboard only answerlocalhost,127.0.0.1,::1and the listen address; any otherHostheader gets421 misdirected_request. Behind a reverse proxy with a domain name, setMAINTENANT_BASE_URLto the public URL, or list the names inMAINTENANT_ALLOWED_HOSTS. Pings, the status page,/mcpand/oauth/still answer any host. - Kubernetes probes. The Helm chart and the raw manifests now send
Host: localhoston their probes. A manifest of your own that probes/api/v1/healthneeds the same header, or the kubelet gets421and restarts the pod. - SMTP. On any port other than 465, STARTTLS is now required. A relay that does not offer it makes the send fail, with a message naming
MAINTENANT_SMTP_ALLOW_PLAINTEXT. Set it totrueto allow clear text; a warning is logged at startup. Credentials are still never sent in clear, except tolocalhost. - MCP tokens. OAuth tokens issued before the upgrade have no scope and now count as
read. A client that uses write tools must authorize again withread write. --mcp-stdio. The running instance must haveMAINTENANT_MCP=trueorMAINTENANT_MCP_SOCKET=true. With neither, it serves no socket and the command fails, naming both variables.- Checks declared by labels.
maintenant.endpoint.http.methodacceptsGETandHEADonly; any other value falls back toGETwith a warning.Authorization,CookieandProxy-Authorizationheaders set by a label are ignored. HTTP, TCP and certificate checks declared by a label refuse link-local addresses (including169.254.169.254), multicast and unspecified addresses. Monitors created from the interface or the API are not affected. - Agents. Without
MAINTENANT_GRPC_TLS_CERT, the server now generates its self-signed gRPC certificate once and keeps it next to the database (grpc-server.crt,grpc-server.key), instead of a new one at every start. Agents already enrolled with--grpc-insecure-skip-tls-verifykeep working, with a warning on every connection: move them to--grpc-server-fingerprint(see Agents below). - Status page subdomain. When
MAINTENANT_STATUS_URLis set, its host serves the status page at/and answers404to/api/,/mcp,/oauth/and the dashboard pages, even if the proxy forwards them. - Channels in the API. The API and the MCP tools no longer return channel secrets: headers come back masked with
has_headers, and theurlof a Slack, Discord or Teams channel is cut after the host, withhas_url_secret: true. APUTwithoutheadersorurlkeeps the stored values, so the masked values never need to be sent back.
Public surfaces
- The public status page no longer exposes monitor ids or targets. A component monitor can carry an optional
label, shown on the page instead of the type name. - Heartbeats ping with a secret
ping_token, distinct from their id. Rotate it from the Rotate token button of the detail panel or withPOST /api/v1/heartbeats/{id}/rotate-token. Existing heartbeats received their id as their token, so the ping URLs already deployed keep working. - Pings are limited per token, 60 per minute, instead of per client address. A flood of unknown tokens no longer blocks valid pings coming through the same proxy.
Labels and generated commands
- Values read from labels are quoted in the generated update commands.
- Webhook headers are validated when a channel is saved.
MCP
MAINTENANT_MCP_READ_ONLY=trueregisters only the 34 read tools; the 17 write tools do not exist.- OAuth scopes
readandwritebound the write tools. --mcp-stdiono longer starts a second instance on the same database: it relays stdin and stdout tomcp.sock, a Unix socket (mode0600) served by the running instance next to its database. A client launching a newer image can no longer migrate the live database under it.get_container_logsstarts with a fixed line telling the assistant that the following lines come from the container, cannot be trusted and must never be run as instructions. Each line is cut at 4 KB and the whole answer at 1 MB.examples/agent-webhook/receiver.pyanswers400to an alert whoseidis not a UUID, before using it as a file name, and413to a body over 1 MB, without reading it.
Agents
- The SHA-256 fingerprint of the server certificate is logged at startup and returned when an enrollment token is created. The generated install commands pin it:
--grpc-server-fingerprintfor Docker and native agents,MAINTENANT_GRPC_SERVER_FINGERPRINTon Kubernetes. - The gRPC server bounds the authentication delay, the message size, the connections per address and the size of each field.
Helm chart
- The chart is published with each release to
oci://ghcr.io/kolapsis/charts/maintenantand signed with keyless Cosign. Its version now follows the application version. image.tagdefaults to empty, so the chart runs the image of its own application version instead oflatest.
Fixes
- SQLite files are created with mode
0600. - Sensitive API routes answer
Cache-Control: no-store, and the interface clears its API caches when the session expires. - A status page subscriber who asks twice within 15 minutes receives one confirmation mail, and confirmation mails are capped at 100 per hour.
- The log excerpt attached to a container exit only holds the lines of its last run.
- Changelog source links are kept only when they use
httporhttps.
Upgrading
Migrations 42 and 43 run at startup on SQLite and PostgreSQL. They add heartbeats.ping_token, filled with each heartbeat's id, and status_component_monitors.label.
The Helm chart moves to 1.10.0, the same as the application version.
The full documentation is at docs.maintenant.dev.