Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ explanation rather than silence.

## Scope

This is a pre-release feature plugin (`0.4.0`). The API is in WordPress core trunk for 7.2, and
This is a pre-release feature plugin (`0.5.0`). The API is in WordPress core trunk for 7.2, and
`src/` follows it. There is no supported-version matrix yet: `main` is the only supported branch,
and fixes land there.

Expand Down
95 changes: 95 additions & 0 deletions docs/changelog/0-5-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: "0.5.0"
description: "WP_SECRETS_KEY is required, the provider, store and keyring are abstract classes, and src/ follows core trunk through 7.2 beta 5. Two breaking changes: check both before updating."
date: 2026-11-10
---

# 0.5.0

Released 2026-11-10. In this release `src/` and the shared test helpers match WordPress core trunk
as of [r65390](https://core.trac.wordpress.org/changeset/65390), byte for byte: what ships
in 7.2 beta 5.

## Before you update

This release has two breaking changes. Both match core from 7.2 beta 5.

**1. `WP_SECRETS_KEY` is required.** Without it, the Secrets API is unavailable: every call that
needs the key returns `secret_key_unavailable`. Until now the key was derived from `LOGGED_IN_KEY`
and `LOGGED_IN_SALT` when `WP_SECRETS_KEY` was not defined. Rotating the salts then locked every
stored secret ([ADR 0016](../decisions/0016-require-wp-secrets-key.md)).

- A site that already defines `WP_SECRETS_KEY` needs to do nothing.
- A site with no stored secrets only needs to add a key. Site Health shows a generated `define()`
line to paste into `wp-config.php`, and so does `wp secret generate-key`.
- A site that stored secrets under the fallback must move them **before** updating, or they read
as `secret_key_unavailable` until it does:
1. Set `WP_SECRETS_KEY` to a new value, from `wp secret generate-key`.
2. Set `WP_SECRETS_KEY_PREVIOUS` to the old `LOGGED_IN_KEY . LOGGED_IN_SALT`, the two values
joined together.
3. Run `wp secret rotate`.
4. Remove `WP_SECRETS_KEY_PREVIOUS`.

Nothing is lost if you update first: the root key is left as it was, and the same steps make
the secrets readable again afterward.

**2. A `secrets.php` drop-in uses `extends`, not `implements`.** `WP_Secrets_Provider`,
`WP_Secrets_Store` and `WP_Secrets_Keyring` are abstract classes now, not interfaces, so a later
release can add a method without breaking every drop-in
([ADR 0017](../decisions/0017-abstract-classes-for-the-extension-points.md)). Change `implements`
to `extends` in your drop-in. Nothing else in the contract changes.

A drop-in that still uses `implements` is **a fatal error on every request**, WP-CLI included. It
does not fail closed: PHP reports it while compiling the drop-in, before WordPress can catch it.
If you copied one of the [examples](../reference/examples.md), take the new version. They extend
the classes.

## Ported from core

| Changeset | Change | Props |
|---|---|---|
| [r65272](https://core.trac.wordpress.org/changeset/65272) | The tests follow core's PHPUnit guidelines: a summary, `@covers` and a message for each assertion. The docblocks no longer refer to the feature plugin. | peterwilsoncc |
| [r65373](https://core.trac.wordpress.org/changeset/65373) | A failed read of the root key no longer replaces it, which made every stored secret unreadable. Only a write creates a root key. [#66413](https://core.trac.wordpress.org/ticket/66413) | aryanjasala, dhrupo, ericmann |
| [r65375](https://core.trac.wordpress.org/changeset/65375) | A secret name can't end in a newline. Secret values and key material are marked `#[\SensitiveParameter]`, so they stay out of stack traces. [#66414](https://core.trac.wordpress.org/ticket/66414) | dhrupo, aryanjasala, ericmann |
| [r65377](https://core.trac.wordpress.org/changeset/65377) | Summaries, `@covers` and messages for r65375's tests. | – |
| [r65378](https://core.trac.wordpress.org/changeset/65378) | The `secrets.php` drop-in can set its overrides as plain variables, such as `$wp_secrets_keyring = new My_Keyring();`. The plugin's own drop-in loader does the same. | ericmann, jorbin |
| [r65385](https://core.trac.wordpress.org/changeset/65385) | `WP_SECRETS_KEY` is required. A key that is empty or only whitespace, the sample placeholder, or equal to a key or salt is not usable. New: `wp_is_secrets_api_available()` and `WP_Secrets_Config_Key_Provider::get_key_status()`. | ericmann, peterwilsoncc, Clorith, dhrupo, wildworks, johnbillion, jorbin, digitalpritam |
| [r65387](https://core.trac.wordpress.org/changeset/65387) | Site Health reports a key that can't unlock the stored secrets, and secrets that can't be listed, as problems instead of good. It gives advice that fits a provider drop-in and a legacy-form key. [#66380](https://core.trac.wordpress.org/ticket/66380) | nofearinc, ericmann, ravipatel, dhrupo, Clorith, jorbin, peterwilsoncc |
| [r65390](https://core.trac.wordpress.org/changeset/65390) | The provider, store and keyring are abstract classes. | ericmann, jorbin |

peterwilsoncc committed r65272, and whyisjake committed the rest.

Three changes from the same period are in core only, because they edit core files the plugin can't
change. WordPress 7.2 has them:

- [r65374](https://core.trac.wordpress.org/changeset/65374) lists `secrets.php` under Plugins >
Drop-ins and in Site Health Info. Props nofearinc, ericmann.
- r65375 masks `_wp_secrets_root_key` on Settings > All Settings, where saving it changed made
every stored secret unreadable. On WordPress 7.1 or earlier, don't edit that field.
- [r65389](https://core.trac.wordpress.org/changeset/65389) has the installer write
`WP_SECRETS_KEY` into `wp-config.php`, with a placeholder in `wp-config-sample.php`.
[#66399](https://core.trac.wordpress.org/ticket/66399). Props ericmann, peterwilsoncc,
digitalpritam, ravipatel, Clorith, dhrupo.

## Changed

- **Drop-ins should mark their own sensitive parameters.** Attributes aren't inherited, so a
keyring's `wrap()` and a provider's `set()` add `#[\SensitiveParameter]` themselves. The
examples and [`drop-in-example.php`](../reference/drop-in-example.php) do. On PHP 7.4 the
attribute line is a comment.
- **CI tests the plugin where it runs.** The full suite runs on WordPress 7.1 and the 6.8 floor.
On trunk, which ships the API, a separate job checks only that the plugin stands down.

## In review in core

Each of these will be ported once committed.

| Ticket | Change | PR |
|---|---|---|
| [#66187](https://core.trac.wordpress.org/ticket/66187) | The Site Health checks move into `WP_Site_Health` and `WP_Debug_Data`. | [15881](https://github.com/WordPress/wordpress-develop/pull/15881) |
| [#66424](https://core.trac.wordpress.org/ticket/66424) | A failed read of a secret's record no longer reads as a missing secret. | [15329](https://github.com/WordPress/wordpress-develop/pull/15329) |

WP-CLI's `wp config create` will generate the key as well
([wp-cli/config-command#235](https://github.com/wp-cli/config-command/pull/235)). Moving the
`wp secret` commands into WP-CLI is proposed in
[wp-cli/entity-command#677](https://github.com/wp-cli/entity-command/issues/677).
2 changes: 1 addition & 1 deletion docs/spec/scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ scope", and "Timeline" in the [proposal][proposal].

## As built

**The plugin.** `secrets-api.php` declares version `0.4.0`, `Requires at least: 6.8`, and
**The plugin.** `secrets-api.php` declares version `0.5.0`, `Requires at least: 6.8`, and
`Requires PHP: 7.4`.

**Core-bound versus plugin-only.** Everything under `src/` is written to be copied into
Expand Down
4 changes: 2 additions & 2 deletions secrets-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Plugin Name: Secrets API
* Plugin URI: https://github.com/ericmann/secrets-api
* Description: Feature plugin for the WordPress Secrets API proposed for 7.2. Encrypted, versioned credential storage with pluggable storage and keyring back ends.
* Version: 0.4.0
* Version: 0.5.0
* Requires at least: 6.8
* Requires PHP: 7.4
* License: GPL-2.0-or-later
Expand All @@ -25,7 +25,7 @@
/**
* Plugin version.
*/
define( 'WP_SECRETS_API_PLUGIN_VERSION', '0.4.0' );
define( 'WP_SECRETS_API_PLUGIN_VERSION', '0.5.0' );

/**
* The WordPress version expected to ship the Secrets API in core.
Expand Down
Loading