# MinKMS RELEASE.2026-08-04T13-17-26Z

Released: 2026-08-05

This release fixes clustering bugs that could give a cluster two leaders at once, or leave it with no leader until an operator repaired it by hand. It also blocks a node from being taken over by a node outside its cluster. Two new capabilities: Thales CipherTrust Manager as an HSM backend, and a metric that reports when the server's TLS certificate expires.

The server now refuses to start if its config file holds a key it does not recognize. Read Breaking Changes and Migration Notes before you upgrade.

---

## Downloads

### Binary Downloads

| Platform | Architecture | Download                                                                                           |
| -------- | ------------ | -------------------------------------------------------------------------------------------------- |
| Linux    | amd64        | [minkms.RELEASE.2026-08-04T13-17-26Z](https://dl.min.io/aistor/minkms/release/linux-amd64/archive/minkms.RELEASE.2026-08-04T13-17-26Z)           |
| Linux    | arm64        | [minkms.RELEASE.2026-08-04T13-17-26Z](https://dl.min.io/aistor/minkms/release/linux-arm64/archive/minkms.RELEASE.2026-08-04T13-17-26Z)           |
| macOS    | amd64        | [minkms.RELEASE.2026-08-04T13-17-26Z](https://dl.min.io/aistor/minkms/release/darwin-amd64/archive/minkms.RELEASE.2026-08-04T13-17-26Z)          |
| macOS    | arm64        | [minkms.RELEASE.2026-08-04T13-17-26Z](https://dl.min.io/aistor/minkms/release/darwin-arm64/archive/minkms.RELEASE.2026-08-04T13-17-26Z)          |
| Windows  | amd64        | [minkms.exe.RELEASE.2026-08-04T13-17-26Z](https://dl.min.io/aistor/minkms/release/windows-amd64/archive/minkms.exe.RELEASE.2026-08-04T13-17-26Z) |

### FIPS Binaries

| Platform | Architecture | Download                                                                                           |
| -------- | ------------ | -------------------------------------------------------------------------------------------------- |
| Linux    | amd64        | [minkms.RELEASE.2026-08-04T13-17-26Z.fips](https://dl.min.io/aistor/minkms/release/linux-amd64/archive/minkms.RELEASE.2026-08-04T13-17-26Z.fips) |

Each binary also has `.sha256sum`, `.minisig`, and `.asc` signature files at the same path.

### Container Images

```bash
# Standard
docker pull quay.io/minio/aistor/minkms:RELEASE.2026-08-04T13-17-26Z
podman pull quay.io/minio/aistor/minkms:RELEASE.2026-08-04T13-17-26Z

# FIPS
docker pull quay.io/minio/aistor/minkms:RELEASE.2026-08-04T13-17-26Z.fips
podman pull quay.io/minio/aistor/minkms:RELEASE.2026-08-04T13-17-26Z.fips
```

---

## Breaking Changes

**The server rejects unknown keys in its config file.** MinKMS now refuses to start
if `config.yaml` holds a key it does not recognize, including a misspelled one.
Earlier releases ignored those keys without a warning, so a typo in a
security-relevant field looked like it applied while the server used the default
instead. Check your config file before you upgrade. See Migration Notes for the
steps (#242).

---

## Security Updates

- **A node now rejects cluster traffic from nodes outside its own cluster.** All
  nodes in a cluster share one identity derived from the HSM key, so mutual TLS
  alone cannot tell a current member apart from a node you removed or from a node
  in a different cluster built on the same HSM key. Each node now also checks that
  the sender is a current member of its own cluster, and rejects any sender that
  claims the node's own ID (#243).

- **A single-node cluster now accepts a join only from a leader that is part of
  it.** Joining a node to a cluster sends it a new cluster definition. The node now
  requires that definition to name both its own address and the leader that sent
  it. Before, it checked only for its own address. A node in a different cluster
  built on the same HSM key could therefore send a definition naming this node but
  not itself, demote it, overwrite its `cluster.json`, and shut it down (#243).

- **Server errors no longer expose internal error text to clients.** Unhandled
  errors now return a generic message. The full error still goes to the server log
  (#242).

No CVE or GHSA applies to these fixes. Each one closes a path that is only open to
a process already holding the cluster's HSM key, so they harden the trust boundary
rather than fix a remotely exploitable flaw. Upgrade at your normal pace.

---

## New Features

- **Thales CipherTrust Manager as an HSM backend.** MinKMS can now use a key held
  in Thales CipherTrust Manager, formerly Gemalto KeySecure, to seal and unseal its
  on-disk state and to derive its internode identity. Configure it under
  `hsm.thales.ciphertrust_manager`. MinKMS signs in with a username and password,
  optionally scoped to a domain, and then uses the symmetric key you name.

  Sealing uses envelope encryption: MinKMS encrypts the data locally with a fresh
  key and asks CipherTrust Manager to wrap only that key. It stores the key version
  CipherTrust Manager returns, so sealed state stays readable after you rotate the
  key. Rotating the key also rotates the identity MinKMS derives from it. The
  backend takes five fields, listed under New Configuration Options below (#237).

- **A metric for TLS certificate expiry.** The new `tls_cert_expiry_seconds` gauge
  reports when the certificate the server currently serves expires, as a Unix
  timestamp. Before, MinKMS warned about a near-expired certificate only in its
  log, a couple of days ahead. Logs are easy to miss and cannot drive an alert,
  and an expired certificate makes the server unreachable to clients and breaks
  communication between nodes. Alert a week ahead with:

  ```promql
  (tls_cert_expiry_seconds - time()) < 7 * 86400
  ```

  The gauge reports the expiry date itself rather than the time remaining, so the
  value does not depend on when Prometheus scrapes it. It always tracks the
  certificate in use, so it stays correct when the server reloads or rotates a
  certificate (#238).

---

## Bug Fixes

### Consensus and Clustering

Each node runs elections and sends heartbeats alongside the requests it serves for
its peers. Several handoffs between the two were not safe against concurrent
access, which caused the following (#243):

- **Two nodes could become leader at the same time.** Each could then commit
  different changes under the same commit number, leaving the cluster's copies of
  the data in conflict. A node now refuses to vote for a peer while it is
  collecting votes for itself.

- **A cluster could end up unable to elect any leader.** A write that committed but
  failed to reach the followers was recorded as if it had reached them. The next
  write then skipped the re-replication that should have repaired the gap. Once the
  gap grew to two commits, followers rejected every replication and no node could
  win an election again. Recovering needed manual repair. A node now records a
  write as replicated only if its commit number has not moved in the meantime.

- **A leader that stepped down forwarded writes to itself in a loop.** It kept
  naming itself as leader, so every incoming write was forwarded back to it over
  HTTP until the next election. A leader that steps down now clears the recorded
  leader, and a node that has no leader to forward to returns `cluster leader is
  unknown`. This state occurs briefly whenever a step-down meets an incoming
  write; the server no longer aborts when it sees it.

- **A new leader could report the wrong leader for its whole term.** A write
  arriving immediately after a node won an election stopped the node from recording
  itself as leader. It now records this when it replicates a write as well as when
  it wins an election.

- **Forwarding a write during shutdown could crash the node.** The forwarding path
  now reads the shutdown state and the destination together, so a shutdown that
  starts mid-forward can no longer crash it (#242).

Two related changes in behavior: MinKMS now rejects an invalid cluster definition
with HTTP 400 and leaves the node's state untouched, where before it returned 500
after the node had already demoted itself. And `minkms stat` now shows a real
heartbeat time for new leaders and single-node leaders instead of a zero time
(#243).

### Logging

- **A log record with more than six stack frames crashed the server.** Decoding
  such a record wrote past the end of a fixed six-frame array, and counted the
  frames wrongly even when it did not crash. Records arriving from another node
  could trigger this (#242).

### API

- Malformed request bodies sent to the internode endpoints now return HTTP 400
  instead of 500 (#242).
- The `unknown change type` error now names the change type the node received
  rather than the one it holds locally (#242).

### Metrics

- MinKMS now skips its request and response size metrics when the size is unknown,
  so chunked responses no longer record negative values (#242).
- Corrected `Paritioned` to `Partitioned` in a metric description, and corrected
  five misplaced comments on RPC metric fields (#242).

---

## Improvements

- **`minkms help metrics` documents every metric the server exposes.** The new page
  covers the `/v1/health/metrics` endpoint, the OpenMetrics naming and type
  conventions MinKMS follows, every runtime, network, HTTP, RPC, consensus, and TLS
  metric with its labels and histogram buckets, and example PromQL queries (#239).

- **`minkms decrypt` writes its output byte for byte.** Raw and base64 output no
  longer end in a newline, so redirecting the output to a file gives you the exact
  key material. If a script strips that newline today, drop that step. If a script
  depends on it, add it (#242).

- **Corrected six `--help` usage lines** that named commands which do not exist:
  `add-hsm`, `add-identity`, `add-policy`, `ls-policy`, `ls-identity`, and
  `ls-enclave` (#242).

---

## Security & Compliance

### Software Bill of Materials (SBOM)

This release ships an SBOM in three formats:

- [SPDX JSON](sbom-RELEASE.2026-08-04T13-17-26Z.spdx.json) — the industry standard format
- [CycloneDX JSON](sbom-RELEASE.2026-08-04T13-17-26Z.cyclonedx.json) — read by most security scanners
- [Go Modules](go-modules-RELEASE.2026-08-04T13-17-26Z.txt) — a plain-text dependency list

Each file lists every direct and transitive dependency, for security audits and
compliance reviews.

---

## Upgrade Instructions

Read Migration Notes first: a config file that started a node on the previous
release can stop one on this release.

This release changes neither the wire protocol nor the on-disk format, so you can
upgrade node by node while the cluster serves traffic. Upgrade the followers first
and the leader last:

1. Stop a follower.
2. Replace its `minkms` binary.
3. Start it, and wait for it to rejoin the cluster.
4. Repeat for each remaining follower.
5. Upgrade the leader last.

**Writes need every node.** The cluster answers reads while a node is down, but
rejects writes. Plan a short maintenance window for the leader.

### New Configuration Options

- `hsm.thales.ciphertrust_manager` — configures the Thales CipherTrust Manager HSM
  backend. It takes five fields:

  | Field | Meaning |
  | --- | --- |
  | `server` | The CipherTrust Manager endpoint, for example `https://ciphertrust.cluster.local` |
  | `key` | The name of the symmetric key to seal with |
  | `username` | The CipherTrust Manager username |
  | `password` | The password for that username |
  | `domain` | The domain the credentials belong to. Optional; the root domain is used when empty |

### Migration Notes

**Check every node's config before you replace any binary.** This release refuses
to start when a config file holds a key MinKMS does not recognize, and writes need
every node. If you find out node by node, the first bad config leaves the cluster
unable to accept writes in the middle of your upgrade.

1. On each node, look through `config.yaml` for keys MinKMS does not define and for
   misspelled ones. Check the spelling of security-relevant fields with particular
   care: until now, a typo there left the default in effect and said nothing.
2. Test each config before you start the upgrade. Run the new binary once with that
   config: it reports a rejected key as a startup error that names the key, so you
   can fix it without touching the running cluster.
3. Once every config is accepted, upgrade the cluster as described above.

### Support

For enterprise support:

- SUBNET Support: https://subnet.min.io
- Documentation: https://docs.min.io
