# Release-signing public keys

This directory ships the **public** halves of every ed25519 keypair the
Seekmodo release pipeline has ever used to sign a Seekmodo Search for
WordPress release. Each file is a single-key JWK:

```json
{
  "kty": "OKP",
  "crv": "Ed25519",
  "alg": "EdDSA",
  "use": "sig",
  "kid": "<key id>",
  "x":   "<base64url, unpadded, 32-byte ed25519 public key>"
}
```

The filename is `<kid>.pub` — that's how a future in-plugin signature
verifier looks up the right trust root when it sees a manifest entry
with `sig_kid: "<kid>"`.

## Why ship multiple kids?

Rotation. Today the active kid is `seekmodo-2026-06`. When we rotate
to `seekmodo-2027-06` (annual cadence) or sooner (emergency), we'll
land a PR that drops the new `seekmodo-2027-06.pub` here while leaving
the old `seekmodo-2026-06.pub` in place. Releases signed under either
kid stay valid for verification until we deliberately retire the old
one (which only takes effect on *new* installs — already-installed
plugins keep whatever trust roots they shipped with).

## Threat model

Trust roots are vendored, not fetched. A hostile manifest can lie
about which kid signed a zip, but it can't introduce a new kid that
the plugin will accept — that requires a code change in this
directory + a release that ships it. A JWKS endpoint at
`https://seekmodo.com/.well-known/release-signing-keys.json` exists
purely for human transparency; in-plugin verification never trusts
it.

## How a new kid lands

Operator workflow (see `docs/SIGNING_KEYS.md` in `numinix/seekmodo`):

1. Generate keypair, store private half at
   `~/.numinix/release-signing-<kid>.key` (operator-local; never
   committed).
2. Update `ACTIVE_SIGNING_KID` in `tools/build_release.py`.
3. Run `python tools/build_release.py --bump patch`; the script will
   re-vendor this directory with the new `<kid>.pub` automatically.
4. Cut the release. Existing installs that already shipped with the
   old kid stay verifying releases signed under the old kid; from
   the rotation release onward, new installs get both kids and trust
   the new active one.
