ghostkey_lib 0.2.0

A library for working with Freenet's Ghost Keys
Documentation
# Changelog

## 0.2.0 — delegate → notary rename

The PKI intermediate signing key has been renamed from "delegate" to
"notary" throughout the `ghostkey_lib` source API, the `ghostkey` CLI,
the `ghostkey-api` server, and the Freenet.org Hugo site. The rename is
tracked in [freenet/web#24](https://github.com/freenet/web/issues/24) and
deconflicts with Freenet's own `Delegate` concept (sandboxed WASM agents).

### Why 0.2.0 and not 0.1.5

Public struct fields on `NotaryCertificateV1`, `NotaryPayload`, and
`GhostkeyCertificateV1` have been renamed (`.delegate` → `.notary`,
`.delegate_verifying_key` → `.notary_verifying_key`). Downstream code that
accesses those fields directly will no longer compile against the new
version. Under pre-1.0 Rust semver, `0.1.x → 0.1.y` is the compatible
range, so Cargo would auto-upgrade downstream crates and break their builds;
`0.1.x → 0.2.0` is the breaking range and requires explicit opt-in. This
is a breaking bump to make that opt-in explicit.

### Wire-format compatibility

This is a **source-API rename only**. Ghost-key certificates minted by
earlier releases continue to parse, verify, and round-trip byte-identically
under 0.2.0. A regression test suite in `tests/legacy_v1_compat.rs` loads
real fixtures generated by pre-rename code and asserts the freeze.

### Terminology — notary vs delegate vs vault

Three distinct concepts, all worth calling out because they collide:

- **notary certificate** (this crate) — the PKI intermediate. Signs a
  donation attestation.
- **Freenet `Delegate`** — the generic platform primitive: a sandboxed
  WASM agent running inside a Freenet node.
- **Ghostkey Vault** — the specific `freenet/ghostkeys` delegate that
  stores user ghost keys and handles identity operations. "Vault" is its
  product name (see `ghostkeys/ui/`). Issue #24 called it a "wallet
  delegate", which is superseded — use "vault" in all forward-looking
  docs.

### Library changes (`ghostkey_lib`)

- New module `ghostkey_lib::notary_certificate` exporting
  `NotaryCertificateV1` and `NotaryPayload`.
- Deprecated stub module `ghostkey_lib::delegate_certificate` re-exports
  the renamed types as `DelegateCertificateV1` / `DelegatePayload` so
  downstream code that imported the old path continues to compile with
  deprecation warnings. Slated for removal in a future release.
- `GhostkeyCertificateV1.delegate` field renamed to `notary` but its CBOR
  wire-format key is frozen as `"delegate"` via `#[serde(rename)]`.
- `NotaryPayload.notary_verifying_key` has its CBOR wire-format key frozen
  as `"delegate_verifying_key"` via `#[serde(rename)]`. Because cert
  signatures cover the serialized payload bytes, the wire format cannot
  change without invalidating every existing ghost key.
- PEM armor: writes always use the canonical `BEGIN NOTARY_CERTIFICATE_V1`
  header. Reads accept both the canonical header and the legacy
  `BEGIN DELEGATE_CERTIFICATE_V1` header via a `legacy_armor_aliases` table
  in `armorable.rs`. Slated for removal in a future release.
- New regression test suite `tests/legacy_v1_compat.rs` pins the wire-format
  compatibility decisions against real fixtures under
  `tests/fixtures/legacy_v1/`.

### CLI changes (`ghostkey`)

- New canonical subcommands `generate-notary` and `verify-notary`. The
  legacy `generate-delegate` and `verify-delegate` are retained as clap
  aliases and emit a one-shot stderr deprecation warning before clap
  parses (via a manual argv pre-scan, since clap normalizes matches to
  the canonical name).
- New canonical flags `--notary-certificate`, `--notary-dir`. The legacy
  `--delegate-certificate` and `--delegate-dir` are retained as clap
  aliases with the same deprecation warning.
- Default on-disk filenames: `notary_certificate.pem`,
  `notary_signing_key.pem` (was `delegate_*.pem`).
  `generate-ghost-key` reads the new canonical filenames first and
  transparently falls back to the legacy names with a deprecation
  warning. Existing key directories keep working unchanged.
- Shell helper renamed from `generate_delegate_keys.sh` to
  `generate_notary_keys.sh`. The old filename is preserved as a stub
  that warns and execs the new script. Per-amount on-disk filenames
  move from `delegate_{kind}_{amount}.pem` to
  `notary_{kind}_{amount}.pem`.

### API server changes (`ghostkey-api`)

- Canonical CLI flag `--notary-dir` (legacy `--delegate-dir` accepted
  as a clap alias with deprecation warning).
- The `--notary-dir` flag is bound to `NOTARY_DIR` via clap's `.env()`
  binding. If the operator has only the legacy `DELEGATE_DIR` set, a
  pre-parse hook in `main()` copies it into `NOTARY_DIR` before clap
  runs, with a deprecation warning. Operators on systemd units or
  `.env` files that only declare `DELEGATE_DIR` keep working unchanged.
- Per-amount key file lookup in `delegates::pick_scheme` resolves the
  naming scheme at the directory level, not per-file. A partial
  on-disk migration (e.g. only the cert renamed, signing key still
  legacy) is explicitly rejected to avoid pairing a new-named cert
  with a legacy-named signing key — that mismatch would produce ghost
  certs whose signatures don't chain. Unit tests in `delegates::tests`
  pin every resolution branch.
- `DonationResponse` and `SignCertificateResponse` dual-emit BOTH
  `delegate_certificate_base64` (legacy) and `notary_certificate_base64`
  (canonical) fields with identical values. This lets already-cached
  browser JS keep working while freshly served JS picks up the new
  field. Removal of the legacy field tracked for a future release.

### Hugo site changes

- `donation-success.js` reads `notary_certificate_base64` first and
  falls back to `delegate_certificate_base64`, migrating the value to
  the new key. The legacy key is intentionally NOT deleted for one
  release to avoid breaking a stale multi-tab session.
- `stripe-donation-form.html` writes to both localStorage keys for the
  same reason.
- `/ghostkey/` landing page copy refers to the notary key where it
  previously said "delegate key" (in the PKI sense only — mentions of
  Freenet's WASM Delegate / Ghostkey Vault are correctly left alone).

### Deliberately NOT renamed

- `hugo-site/content/about/news/introducing-ghost-keys.md` — historical
  blog post, the `delegate-key-created` JSON key inside the embedded
  sample is baked into the actual `info` field of real pre-rename
  certificates.
- Inside the cert `info` field JSON: the `delegate-key-created` key
  name is emitted by `generate_notary_keys.sh` verbatim and is parsed
  by River's UI. Renaming it would break every historical donation.
- `rust/api/src/delegates.rs` module filename: the internals are
  renamed, but moving the file would churn the module path without
  buying anything. Documented in the file's module doc comment.

### Upgrading

Breaking bump. Downstream code that imports from the deprecated module
path (`ghostkey_lib::delegate_certificate::DelegateCertificateV1`) will
still compile with a deprecation warning, but code that accesses
`GhostkeyCertificateV1.delegate` or `DelegatePayload.delegate_verifying_key`
as struct fields must update to `.notary` / `.notary_verifying_key`.

In particular, the `freenet/ghostkeys` Ghostkey Vault will need a
follow-up PR that bumps its `ghostkey_lib` dependency to `0.2` and
updates field accesses in `delegates/ghostkey-delegate/src/handlers.rs`.
This is planned and out of scope for this release.

### Release checklist note

The `ghostkey-api` and `integration_test` crates previously depended on
`ghostkey_lib` from crates.io and now depend on the in-tree `../gklib`
path. This removes an implicit check that the published crate didn't
drift from the in-tree source. After publishing 0.2.0, manually verify
the publish succeeded by temporarily flipping `rust/api/Cargo.toml` to
`ghostkey_lib = "0.2"` from crates.io and running
`cargo check --manifest-path rust/api/Cargo.toml`.