# Changelog
All notable changes to this project will be documented in this file.
Per-task history (acceptance criteria, scoring, decision notes) lives in `roadmap/tasks.toml` — query with `rmap show <id>` or read `roadmap/data.json`. This file is curated release notes only.
---
## [Unreleased]
## [0.6.2] - 2026-06-30
Protocol utilities. Added `MPP.Expires` for ISO 8601 challenge-expiration helpers and `MPP.DID.evm_did/2` for `did:pkh:eip155` credential-source identifiers.
Security. `MPP.Plug` now accepts an optional shared replay store for generic credential deduplication across non-Tempo methods, using the existing `MPP.Tempo.Store` interface and atomic `check_and_mark/2` when available.
**Security (Tempo + Stripe hardening — Task 46).** Tempo gains EIP-712 proof v3 credentials (`MPP.Methods.Tempo.Proof`, `type="proof"`) with wallet-bound signatures, optional store dedup, and zero-amount enforcement. Fee-payer co-signing now rejects fee tokens outside a per-chain allowlist (`FeePayerPolicy.default_allowed_fee_tokens/1`, overridable via `fee_payer_allowed_fee_tokens`). `MPP.DID.parse_evm_did/1` validates hash-credential `did:pkh` sources. `MPP.Methods.Tempo.SessionReceipt` adds optional `externalId` (PaymentWitness parity). Stripe rejects credential `externalId` values that disagree with the route request (mppx #537).
**Security (Tempo proof access keys — Task 69).** Zero-amount Tempo proof verification now accepts active delegated access-key signatures through the Tempo AccountKeychain path when the direct signer is not the root wallet, and rejects stale or revoked access keys.
**Security (Tempo hosted fee payer — Task 68).** Tempo charge verification can delegate fee-payer co-signing to a hosted `eth_fillTransaction` endpoint via `fee_payer_url`, matching mppx `fillHostedFeePayerTransaction`. Returned `feeToken` values are checked against the same sponsor allowlist as local co-signing before broadcast.
## [0.6.1] - 2026-06-29
**Security (Tempo hash-credential dedup).** The `type="hash"` credential path now commits its dedup mark through the store's atomic `check_and_mark/2` — the same primitive the `type="transaction"` path already uses — matching the reference SDKs (mpp-rs `Store::put_if_absent`, mppx atomic `markHashUsed`). The mark still happens only after successful on-chain verification, so a transient receipt-RPC failure does not burn a legitimate hash; stores that do not implement `check_and_mark/2` keep the documented best-effort fallback.
**Security hardening parity.** Verifier/Plug now issue expiring challenges by default, fail closed on missing/invalid expiration, require echoed `digest`/`opaque` values to match endpoint configuration, and MCP clients recognize payment-required result metadata via `org.paymentauth/payment-required`. Tempo validates declared payer source against the matched transfer sender and chain, verifies challenge-bound attribution metadata on unconfigured memo transfers, and provider/RPC/store failure details are sanitized before becoming public 402 responses. **Breaking:** credentials for newly-issued challenges must echo valid expiration data, and Tempo no-static-memo routes now require challenge-bound attribution metadata.
Development tooling. The dev baseline now pins Elixir `1.20.2-otp-29`, adds `ex_slop` and `reach` to `mix precommit.full`, refreshes the generated agent instructions, and includes MCP config for Cursor/Codex/Grok agents. Tempo's unsupported-`eth_simulateV1` degraded-mode log is now a warning so missing node support is visible during operations.
## [0.6.0] - 2026-06-24
**CI / security scaffolding.** The repo gains GitHub Actions CI — previously it had none. A base **CI** workflow gates every push/PR to `development`/`main` with the full check stack (format, `--warnings-as-errors` compile, Credo strict, Doctor, Sobelow, tests at a 95% coverage floor, Dialyzer), mirroring `mix precommit.full`; an **Integration** workflow runs the credential-gated `:integration` suite nightly (and on PR / manual dispatch), flunking loudly when secrets are absent rather than reporting a green 0-test run. A **Code Scanning** workflow uploads Sobelow findings to the Security tab as SARIF (CodeQL has no Elixir support), plus a Dependabot config (weekly Hex + Actions updates) and an expanded `SECURITY.md` scope. Elixir/OTP are pinned via `.tool-versions` so CI never drifts from local `mix format` output.
**Security (Tempo fee-payer gas draining).** When the server acts as fee payer it now validates the client-signed `0x76` envelope's gas economics **before** co-signing, closing two reported gas-draining vectors (GHSA-vv77-66rf-pm86, GHSA-qpxh-ff8m-c62v): unbounded `max_fee_per_gas`/`max_priority_fee_per_gas` and access-list padding. A new `MPP.Methods.Tempo.FeePayerPolicy` bounds `gas_limit`, `max_fee_per_gas`, `max_priority_fee_per_gas`, the `gas × max_fee_per_gas` total-fee budget, and rejects non-empty access lists. Cross-checking against the mppx and mpp-rs reference SDKs surfaced a further defense both enforce that we were missing — folded in here: the sponsored transaction must use the expiring nonce key and declare a `valid_before` that is in the future and within `max_validity_window_seconds` (default 15 min), so a client cannot hold a co-signed sponsorship broadcastable far into the future. All ceilings (gas economics + validity window) default to the reference-SDK values (per-chain; Moderato gets a higher priority-fee ceiling) and are overridable via `method_config["fee_payer_policy"]`. Safe-by-default — existing `fee_payer: true` deployments are protected without config changes. Complementing this static policy, the server now **pre-simulates the full co-signed sponsored transaction** via `eth_simulateV1` before broadcasting — on both the synchronous and optimistic paths: a transaction that would revert on-chain is rejected before the sponsor commits any gas, a node without `eth_simulateV1` (JSON-RPC -32601) degrades gracefully, and any other RPC error fails closed. This covers the residual class that static gas bounds cannot — a `gas_limit` set too low to complete execution. The `onchain_tempo` floor is raised to `~> 0.7` (lock 0.7.0) for the sender-recovery + `Onchain.Tempo.RPC.simulate/3` primitives `MPP.Methods.Tempo` calls directly; the reference SDKs name the method `tempo_simulateV1`, but Tempo deploys the AA-aware EVM-standard `eth_simulateV1` (both mainnet and Moderato return -32601 for the former).
Dependency updates. Runtime: `req ~> 0.5.17` → `~> 0.6.1` (cascades transitive `finch ~> 0.17` → `~> 0.21/0.22`; MPP uses only the stable `Req.request/2` + `Req.Response`/`Req.Request` surface, no code changes). Dev/test JS tooling moved as a set now that quickbeam relaxed its constraints: `quickbeam 0.10.5` → `0.10.15`, `oxc ~> 0.13.0` → `~> 0.15.1`, `npm ~> 0.6.1` → `~> 0.7.4`; `ex_unit_json ~> 0.4.3` → `~> 0.5.0`; dev-only `bandit ~> 1.11.1` → `~> 1.12.0`.
On-chain stack advanced to the onchain-0.10 line: `descripex ~> 0.7.0` → `~> 0.9`, `onchain ~> 0.7.0` → `~> 0.10`, `onchain_tempo ~> 0.2.2` → `~> 0.7` (the `onchain` bump is what moves `MPP.Methods.EVM` onto the `Onchain.RPC` surface). This required moving the whole ZenHive on-chain family together — descripex 0.8/0.9 add spec-derived JSON Schema and are additive, but descripex **0.9.1** was cut alongside to fix a `safe_convert` crash (it only rescued `ArgumentError`, so real-world specs like Cartouche's `%{required(non_neg_integer()) => <<_::256>>}` aborted the manifest/`describe` build with an uncaught `CaseClauseError`). The lock resolves the family at `descripex 0.11.0`, `cartouche 0.5.0`, `onchain 0.10.0`, `onchain_tempo 0.7.0` (cartouche 0.5 carries the `descripex ~> 0.11` floor). Compile clean under `--warnings-as-errors`; offline tests green against the full updated chain, integration suite green.
## [0.5.1] - 2026-06-09
Dependency updates. Bumped the onchain stack to the 0.7.0 line: `onchain ~> 0.5.4` → `~> 0.7.0`, `onchain_tempo ~> 0.2.1` → `~> 0.2.2`, `descripex ~> 0.6.0` → `~> 0.7.0`. onchain 0.7.0 cascades a major `decimal` `2.4.1` → `3.1.1` jump (transitive only — MPP has no direct `Decimal` use) and pulls `cartouche 0.2.2`. Dev-tool `doctor` advanced `~> 0.22` → `~> 0.23` (0.23 requires `decimal ~> 3.1`, unblocked by the jump). No library code changes — compile clean under `--warnings-as-errors`, 601 offline tests green.
## [0.5.0] - 2026-05-15
Tempo session-receipt support and a client-side HTTP transport. Tempo integration tests now use `Onchain.Tempo.Faucet` for per-test fresh wallets instead of hardcoded keys, removing nonce coupling and unblocking a future move to async tests. `MPP.Client.Transport` behaviour with an HTTP implementation lands as the client-side counterpart to server-side `MPP.Method`, exposing a `select_challenge/2` helper that Task 33c (Req plugin) and Task 33d (MCP) will reuse. Dependency floors tightened: `onchain ~> 0.5`, `onchain_tempo ~> 0.2`. A second cross-SDK gap pass added Tasks 36–42 (Stellar Charge, Accept-Payment, EVM `credentialTypes`/Permit2/EIP-3009, Tempo SessionReceipt, OpenAPI discovery) to the roadmap.
## [0.4.0] - 2026-04-18
The first cross-SDK gap pass with substance. `MPP.Verifier` extracts the verification pipeline (HMAC, realm, expiry, request match, method.verify) out of `MPP.Plug` into a transport-neutral module, so MCP and future transports share the same correctness gate. `MPP.JCS` implements the RFC 8785 subset MPP needs for cross-SDK HMAC interop. The MCP transport (`MPP.Mcp`) lands with -32042/-32043 error codes and the `org.paymentauth/credential` + `org.paymentauth/receipt` `_meta` keys. Client-side gets `MPP.Client.PaymentProvider` behaviour and `MultiProvider` first-match dispatch. Protocol utilities fill in: `MPP.Headers.parse_challenges/1` (multi-challenge `WWW-Authenticate` parsing), `MPP.BodyDigest`, `MPP.Amount` (parse_units / with_base_units / parse_dollar_amount). Eight new RFC 9457 session error types added under `paymentauth.org/problems/session/`. EVM integration tests against Sepolia validate real RPC round-trips for both ERC-20 and native-ETH paths.
**Breaking:** `MPP.Amount.parse_dollar_amount/2` now requires callers to supply `decimals` explicitly (previously a single-arg form inferred it from currency). Neither mppx nor mpp-rs maintain a currency-to-decimals table; implicit inference was a correctness risk.
**Scope decision:** proxy/gateway functionality scoped out to a separate `mpp_proxy` package. The `mpp` library focuses on protocol correctness; the BEAM-native payment gateway is a separate product surface.
Dependency bumps: quickbeam 0.8.1 → 0.10.0, oxc 0.5.4 → 0.7.2, ex_dna 1.2.2 → 1.3.0, credo back to hex `~> 1.7` (upstream shipped the Elixir 1.20-rc multi-line sigil fix).
## [0.3.0] - 2026-04-03
Generic EVM payment method and the `onchain_tempo` extraction. `MPP.Methods.EVM` verifies hash-only credentials on any EVM chain (Ethereum, Base, Polygon, Arbitrum, …) — ERC-20 transfers via Transfer event log parsing, native ETH via tx value/recipient matching. Tempo chain primitives moved out of MPP into the standalone `onchain_tempo` Hex package; MPP delegates chain ops and keeps only the payment-protocol surface (dedup store, eth_call simulation, RFC 9457 error wrapping). `mix mpp.demo` ships an interactive Bandit server on port 4402 with a toy `demo-token` method and a pre-computed valid credential in the startup banner — zero-friction first experience. Read-only live-protocol integration tests against `mpp.dev/api/ping/paid` validate parser compatibility with the reference server. `MPP.Tempo.Store` moduledoc gained deployment-topology guidance (`ConCacheStore` for single-node / sticky routing; shared backend otherwise, with `fly-replay` as a worked example).
## [0.2.0] - 2026-03-28
The Tempo payment method (`MPP.Methods.Tempo`) — on-chain TIP-20 transfer verification with both `type="hash"` (client-broadcast) and `type="transaction"` (server-broadcast) credential paths. `transferWithMemo(address,uint256,bytes32)` matching enforced when `methodDetails.memo` is configured. Server-side fee sponsorship via 0x78 domain signing with whitelisted call-scope validation matching mppx's four allowed selector patterns. Optimistic broadcast mode (`wait_for_confirmation: false`) simulates the payment call via `eth_call`, broadcasts asynchronously, and returns an optimistic receipt without waiting for inclusion. `MPP.Tempo.Store` behaviour adds pluggable replay protection with `get`/`put` + optional atomic `check_and_mark/2`; built-in `MPP.Tempo.ConCacheStore` provides ETS-backed TTL dedup. `MPP.Plug`'s `:methods` option enables multi-method 402 endpoints with per-method pricing. All public functions gain Descripex `api()` annotations with `MPP.describe/0-2` progressive discovery and `mix mpp.manifest` for JSON API contract export. Bug fixes covered the TransferWithMemo `memo` indexed-topic discovery (via real Moderato receipts), v-value normalization for ox/tempo SDK interop, hash-path dedup ordering to avoid burning hashes on transient RPC failures, and crash protection in post-broadcast store paths.
## [0.1.0] - 2026-03-25
First public release. Core protocol — HMAC-SHA256-bound Challenge, Credential, Receipt, Headers (WWW-Authenticate / Authorization / Payment-Receipt), and RFC 9457 problem types under `paymentauth.org/problems/`. `MPP.Methods.Stripe` verifies PaymentIntents via SPT with idempotency, analytics metadata, and `Req.Test` mockability. `MPP.Plug` mounts in any Phoenix or Plug router with per-route pricing and explicit credential configuration — no `Application.get_env`, no ENV fallback.