github kOlapsis/maintenant v1.10.0
Improved Safety

6 hours ago

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_HOSTS or MAINTENANT_BASE_URL, the API and the dashboard only answer localhost, 127.0.0.1, ::1 and the listen address; any other Host header gets 421 misdirected_request. Behind a reverse proxy with a domain name, set MAINTENANT_BASE_URL to the public URL, or list the names in MAINTENANT_ALLOWED_HOSTS. Pings, the status page, /mcp and /oauth/ still answer any host.
  • Kubernetes probes. The Helm chart and the raw manifests now send Host: localhost on their probes. A manifest of your own that probes /api/v1/health needs the same header, or the kubelet gets 421 and 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 to true to allow clear text; a warning is logged at startup. Credentials are still never sent in clear, except to localhost.
  • 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 with read write.
  • --mcp-stdio. The running instance must have MAINTENANT_MCP=true or MAINTENANT_MCP_SOCKET=true. With neither, it serves no socket and the command fails, naming both variables.
  • Checks declared by labels. maintenant.endpoint.http.method accepts GET and HEAD only; any other value falls back to GET with a warning. Authorization, Cookie and Proxy-Authorization headers set by a label are ignored. HTTP, TCP and certificate checks declared by a label refuse link-local addresses (including 169.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-verify keep working, with a warning on every connection: move them to --grpc-server-fingerprint (see Agents below).
  • Status page subdomain. When MAINTENANT_STATUS_URL is set, its host serves the status page at / and answers 404 to /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 the url of a Slack, Discord or Teams channel is cut after the host, with has_url_secret: true. A PUT without headers or url keeps 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 with POST /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=true registers only the 34 read tools; the 17 write tools do not exist.
  • OAuth scopes read and write bound the write tools.
  • --mcp-stdio no longer starts a second instance on the same database: it relays stdin and stdout to mcp.sock, a Unix socket (mode 0600) 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_logs starts 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.py answers 400 to an alert whose id is not a UUID, before using it as a file name, and 413 to 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-fingerprint for Docker and native agents, MAINTENANT_GRPC_SERVER_FINGERPRINT on 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/maintenant and signed with keyless Cosign. Its version now follows the application version.
  • image.tag defaults to empty, so the chart runs the image of its own application version instead of latest.

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 http or https.

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.

Don't miss a new maintenant release

NewReleases is sending notifications on new releases.