# Changelog — joakim.jardenberg.net

Software releases for the public profile site and PPCP/MCP server.
Software version and profile content version are distinct: this file tracks the
software. The profile content edition is 0.4 (2026-09-13), still under owner review.

The machine-readable copy of this file is served at `/CHANGELOG.md`.

## 0.1.0-alpha.4 — 2026-09-13

Error-binding correction found in independent production testing against
joakim.jardenberg.net (42/44 grouped checks passed; the two failures were the
exact v0.2.1 error payloads for a missing `MCP-Protocol-Version` header and an
unsupported protocol version). No content, key, protocol or design change.

- `signedErrorFrame` now signs exactly the v0.2.1 SPEC.md §8 payload
  `{ id, error: { code, message } }`. Previously any `extraData` (for example
  `supported` / `requested` for version negotiation) was spread into the signed
  `payload.error`, so the signed payload no longer matched the published error
  test vector.
- `extraData` is unchanged on the wire: it still sits in `error.data` alongside
  the signed envelope, so MCP version negotiation still sees `supported` and
  `requested`.
- Setup-page regression: the `/mcp` "Try it" `curl` example omitted
  `MCP-Protocol-Version`, so the corrected server rejected its own documented
  example. The example now sends the required `MCP-Protocol-Version`,
  `Mcp-Method` and `Mcp-Name` headers plus modern `_meta`, and documents the
  legacy `initialize` + `2025-11-25` alternative. A conformance test extracts
  the exact rendered example from the served HTML and runs it.
- The example includes a descriptive `User-Agent`, because the hosting edge in
  front of this site has been observed to block some default library
  user-agents (e.g. `Python-urllib/3.11`) with an edge error before the request
  reaches the server; named clients are served. Documented as a factual
  observation; no server-side workaround.
- `ppcp_get_profile` description now states "Public draft; owner review pending;
  AI-assisted editorial wording". Profile 0.4 source unchanged.
- Regression tests cryptographically verify both failing cases (missing header
  and unsupported version): the envelope signature is checked against the
  published JWKS, `payload_digest` is recomputed from the canonical payload, and
  the payload's exact key sets are asserted (`id`, `error`; `code`, `message`),
  including that negotiation fields are absent from the signed payload.

## 0.1.0-alpha.3 — 2026-09-13

Transport edge cases found in the alpha.2 review. No content, key or design change.

- The GET SSE refusal and the `DELETE` refusal now use the same signed
  error-frame constructor as every other error; both also carry CORS headers.
- Origin validation now applies to `GET` and `DELETE` as well as `POST` and
  `OPTIONS`, matching the transport's rule for all incoming connections.
  Origin-less non-browser clients are still accepted.
- JSON-RPC envelope validation: `"jsonrpc": "2.0"` is required, and a request
  `id` must be a string or a safe integer. Missing/wrong `jsonrpc`, `id: null`,
  and boolean, fractional, array or object ids get a signed `-32600` + HTTP 400.
- Notifications (any method sent without an `id`) now always return HTTP 202
  with no body; a `tools/call` notification can no longer receive a result.
- `MCP-Protocol-Version` is now required as an HTTP header on **every**
  non-`initialize` request that carries an `id`, including requests with neither
  the header nor `_meta`: the oldest advertised revision is `2025-06-18`, so an
  absent header must not silently take an unsupported pre-2025 path. Missing
  header → signed `-32020 HeaderMismatch` + HTTP 400. `initialize` (legacy
  negotiation) and notifications remain exempt. `clientCapabilities` must be a JSON
  object, so arrays are rejected; `clientInfo` stays optional, as in the spec.
- Request bodies are decoded as UTF-8 fatally (malformed bytes → signed
  `-32700` + 400) and structural nesting is bounded at depth 32 before any
  recursive traversal, so deep nesting yields a signed `-32600` + 400 rather
  than an unhandled error.

## 0.1.0-alpha.2 — 2026-09-13

MCP transport and signing-binding corrections.

- Implements the 2026-07-28 Streamable HTTP binding: `MCP-Protocol-Version` must
  agree with `_meta["io.modelcontextprotocol/protocolVersion"]`, `Mcp-Method`
  and (for `tools/call` / `resources/read`) `Mcp-Name` are required and
  validated against the body, with `-32020 HeaderMismatch` + HTTP 400 on any
  mismatch or missing header, including Base64-sentinel decoding.
- Adds `server/discover`, `resultType: "complete"` on every result and
  `io.modelcontextprotocol/serverInfo` in result `_meta`. Unknown methods now
  return HTTP 404 with `-32601`; notifications return 202. The legacy
  `initialize` path for 2025-11-25 and 2025-06-18 is unchanged.
- `-32022 UnsupportedProtocolVersion` lists supported versions for unknown ones.
- `resources/read` now signs exactly the result's `contents` binding, with the
  exact resource text as the signed text arm.
- Origin checking uses an exact documented allowlist (no host pattern matching);
  origin-less clients are still accepted. `DELETE` returns 405 (stateless).
- Every JSON-RPC error frame — parse errors, oversized bodies, batch rejection,
  header mismatches, RPC errors — is now produced by one signed constructor.
- Request bodies are read with a hard byte bound instead of being buffered
  first; JSON-RPC batches are rejected; lone UTF-16 surrogates are rejected
  before anything is canonicalized or signed.

## 0.1.0-alpha.1 — 2026-09-13

First release of the public profile site.

- Public, anonymous, account-free reading of the full profile at `/`, with a
  sticky contents sidebar, mobile contents drawer, section anchors and in-page
  search.
- Exact public Markdown at `/profile.md` (`text/markdown`) as the single source
  for every other representation.
- PPCP 0.1.0 JSON at `/profile.json`, validated against the published profile
  schema, and a signed packaging at `/profile.signed.json`.
- Read-only MCP server on `POST /mcp` (stateless streamable HTTP), with
  `ppcp_get_profile`, `ppcp_list_sections`, `ppcp_get_section`, `ppcp_search`,
  `ppcp_get_sources` and `ppcp_get_version`, plus `resources/list` and
  `resources/read` over an exact URI allowlist.
- Browser setup page on `GET /mcp` with an endpoint copy button; SSE `GET`
  streams are not supported and return 405.
- org.jardenberg/verifiable-mcp 0.2.1 signing: Ed25519 (EdDSA), RFC 8785
  canonicalization, RFC 7638 `kid`, signed wrapper with `iat`, `payload`,
  `payload_digest`, `content_digest` and `provenance`, signed error frames and
  signed `isError` results. Missing key degrades visibly to unsigned.
- Discovery: `/.well-known/mcp.json` (with the signing JWKS),
  `/.well-known/ppcp.json`, `/llms.txt`, `/robots.txt`, `/sitemap.xml`,
  `/contact.vcf`.
- `/about-this-profile`, `/changelog` and `/api/health`.
- Schema.org `ProfilePage` + `Person` with `sameAs` to Wikidata and Swedish
  Wikipedia.
