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
6 changes: 3 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ explanation rather than silence.

## Scope

This is a pre-release feature plugin (`0.3.0`) tracking a proposal, not yet shipped in WordPress
core. There is no supported-version matrix yet: `main` is the only supported branch, and fixes
land there.
This is a pre-release feature plugin (`0.4.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.

Things worth reporting:

Expand Down
82 changes: 82 additions & 0 deletions docs/changelog/0-4-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: "0.4.0"
description: "The first release since the Secrets API landed in core as r64337: src/ now follows core trunk, WordPress 6.8 is the minimum, the WP-CLI commands keep working on 7.2, and WP_SECRETS_KEY is about to become required."
date: 2026-10-30
---

# 0.4.0

Released 2026-10-30. The Secrets API landed in WordPress core trunk for 7.2 as
[r64337](https://core.trac.wordpress.org/changeset/64337) ([#66187](https://core.trac.wordpress.org/ticket/66187)).
Since then the sync runs the other way: core trunk is the source, and the plugin ports each core
changeset into `src/`. In this release `src/` and the shared test helpers match trunk as of
[r64995](https://core.trac.wordpress.org/changeset/64995), byte for byte.

## Ported from core

| Changeset | Change | Props |
|---|---|---|
| [r64344](https://core.trac.wordpress.org/changeset/64344), [r64345](https://core.trac.wordpress.org/changeset/64345) | `str_contains()` and `str_starts_with()` in Site Health, `list_secrets()` and the test mocks. | Soean |
| [r64674](https://core.trac.wordpress.org/changeset/64674) | Saving a secret's current value again keeps the previous version, and fires no `wp_secret_changed`. The provider conformance suite tests it. [#66353](https://core.trac.wordpress.org/ticket/66353) | ravipatel, ericmann, whyisjake |
| [r64675](https://core.trac.wordpress.org/changeset/64675) | `??` in `Mock_Store::get()`. | Soean, huzaifaalmesbah |
| [r64690](https://core.trac.wordpress.org/changeset/64690) | `array_all()` in the default keyring. | Soean |
| [r64995](https://core.trac.wordpress.org/changeset/64995) | `??` instead of the remaining fallback ternaries. | Soean, westonruter, mukesh27 |

whyisjake committed r64337 through r64690, and SergeyBiryukov committed r64995.

## Changed

- **WordPress 6.8 is the minimum.** r64690 uses `array_all()`, which core polyfills only from 6.8.
CI has a PHP 7.4 / WordPress 6.8 leg.
- **The WP-CLI commands keep working on a WordPress that ships the API.** Core left `wp secret` and
`wp network-secret` out. On 7.2 the plugin now registers them on core's implementation and loads
nothing else. `migrate-legacy` exits with an explanation there instead. See
[ADR 0014](../decisions/0014-the-plugin-keeps-wp-cli-on-a-core-that-ships-the-api.md).
- **The AWS Secrets Manager and Vault examples skip an unchanged write,** so they pass the
conformance test from r64674.

## Fixed

- On a 7.2 beta, RC or trunk build, the plugin showed the "another plugin has already declared
`wp_get_secret()`" error instead of standing down, because `version_compare()` ranks
`7.2-beta1` below `7.2`. Only the release part of the version is compared now.
- The test suite runs against a WordPress that ships the API. Every trunk job had failed on a
duplicate test helper since r64337.

## Coming in core: `WP_SECRETS_KEY` becomes required

This release, like core trunk today, still derives the key from `LOGGED_IN_KEY` and
`LOGGED_IN_SALT` when `WP_SECRETS_KEY` is not defined. That is about to change. Rotating the salts
locks every secret on such a site ([#66187 comment:24](https://core.trac.wordpress.org/ticket/66187#comment:24)).
The salts were also read without the checks `wp_salt()` applies
([comment:27](https://core.trac.wordpress.org/ticket/66187#comment:27)). The decision is
[ADR 0016](../decisions/0016-require-wp-secrets-key.md), which supersedes
[ADR 0015](../decisions/0015-keep-the-salt-fallback-and-generate-a-key-at-install.md). The plugin
ports the change once core commits it.

Define `WP_SECRETS_KEY` now. On a site that already stores secrets under the fallback, move them
first, or they become unreadable:

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`.

## In review in core

Each of these will be ported once committed.

| Ticket | Change | PR |
|---|---|---|
| [#66187](https://core.trac.wordpress.org/ticket/66187) | Require `WP_SECRETS_KEY`. Reject the placeholder and any key equal to a key or salt. | [15258](https://github.com/WordPress/wordpress-develop/pull/15258) |
| [#66399](https://core.trac.wordpress.org/ticket/66399) | `setup-config.php` writes `WP_SECRETS_KEY`, and `wp-config-sample.php` has a placeholder for it. | [15124](https://github.com/WordPress/wordpress-develop/pull/15124) |
| [#66380](https://core.trac.wordpress.org/ticket/66380) | Site Health reports a broken backend, and stops giving advice that locks stored secrets. `secrets.php` is listed as a drop-in. | [15263](https://github.com/WordPress/wordpress-develop/pull/15263), [14848](https://github.com/WordPress/wordpress-develop/pull/14848) |
| [#66413](https://core.trac.wordpress.org/ticket/66413) | A failed read of the root key no longer replaces it. | [15186](https://github.com/WordPress/wordpress-develop/pull/15186) |
| [#66414](https://core.trac.wordpress.org/ticket/66414) | Secret names cannot end in a newline, values stay out of stack traces, and All Settings no longer lets the root key be edited. | [15200](https://github.com/WordPress/wordpress-develop/pull/15200) |
| [#66424](https://core.trac.wordpress.org/ticket/66424) | A failed read of a secret's record no longer reads as a missing secret. | — |

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.3.0`, `Requires at least: 6.8`, and
**The plugin.** `secrets-api.php` declares version `0.4.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.3.0
* Version: 0.4.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.3.0' );
define( 'WP_SECRETS_API_PLUGIN_VERSION', '0.4.0' );

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