Skip to content
181 changes: 96 additions & 85 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ All notable changes to this project are documented here. The format is based on

### Added

- **`X402PaymentRequired.error_detail.payment_rejected`** (`openapi.yaml`) — documents the
additive `{ reason, rail }` deny verdict the gateway publishes on a 402 when a submitted payment
was rejected; absent on an ordinary unpaid challenge. Live
receipt: a malformed `X-PAYMENT` header returns `payment_rejected: { reason:
"invalid_payment_header", rail: "base-usdc" }`. Closes #46. (#76)
- **Spec coverage from the live gateway skills index** (v1.1.0). Diffed the live capability
index (`https://gateway.wave.online/.well-known/wave-skills.json`, 178 priced capabilities)
against this spec's 72 operations and added a draft operation for every capability that had
Expand All @@ -30,6 +35,90 @@ All notable changes to this project are documented here. The format is based on
and push to `main` and fails if a live, non-allowlisted priced capability has no matching
path segment or tag in `openapi.yaml`. Wired into `foundation-gate.yml`.

- **Console-management operations** (`openapi.yaml`) — six new operations across four paths
Comment thread
yakimoto marked this conversation as resolved.
give the derived MCP tool plane coverage of the console-management surfaces: `GET`/`POST`
`/pricing/manifests` (org-scoped list and validated upsert), `POST /custody/{op}` with a
four-value enum (`grant`/`revoke`/`inspect`/`exercise`), `GET /engine/capabilities`, and
`GET`/`POST` `/gpu/infer`. Adds the `PricingManifest` component schema and the `Pricing`,
`Custody`, `Engine`, and `Gpu` tags. (#72)
- **Agent-auth device authorization ceremony** (`openapi.yaml`) — two new paths under the
`Agent Auth` tag: `POST /agent/auth/device` (the RFC 8628 bootstrap, deliberately
unauthenticated) and its poll counterpart, modelling the device-code grant shape and the
honest `403` while the user has not yet approved. (#66)
- **Registered RFC 8628 grant type URN** (`openapi.yaml`) — the device-authorization poll's
`grant_type` enum now also documents the registered URN
`urn:ietf:params:oauth:grant-type:device_code` alongside the existing bare `device_code`
shorthand; both remain valid and the bare form is canonicalized to the URN before
forwarding upstream. Non-breaking: no enum value was removed. (#68)
- **`POST /batch` endpoint** (`openapi.yaml`) — documents the batch operation contract: an
`operations` array of method, enforced `/v1` path, and optional body, capped at 25 items
per call. (#64)
- **`/search` reconciled to the shipped contract** (`openapi.yaml`) — replaces four stale
search paths that described an archived contract with the four the gateway actually
serves: `POST /search` (hybrid dense+sparse query), `POST /search/index` (upsert one
document or a batch), `DELETE /search/index/{id}`, and the shipped suggest/semantic
variants. (#56)
- **Async render job lifecycle** (`openapi.yaml`) — documents `GET /render/{jobId}` and
`GET /render/{jobId}/events` (server-sent events) so a render started through `POST
/render` can be polled and streamed to completion. Also fixes a YAML structural bug that
had corrupted an adjacent schema. (#34)
- **`AV Mux/Demux` surface** (`openapi.yaml`) — documents `POST /v1/av/remux` (combine
separate RTP H.264 video and Dante/AES67 audio into one MPEG-TS/fMP4 stream) and `POST
/v1/av/demux` (the inverse), with the `AvRemuxRequest`, `AvDemuxRequest`, and
`AvTransformResult` schemas. (#23)
- **Braided Audio publish/stop** (`openapi.yaml`) — documents `POST /v1/braid/publish` and
`DELETE /v1/braid/publish/{ns}` under a new `Braided Audio` tag, with the
`BraidPublishRequest`, `BraidPublishResult`, and `BraidStopResult` schemas. (#19)
- **WAVE Render in the OpenAPI source of truth** (`openapi.yaml`) — adds the `Render` tag and
`POST /render`, payable via the x402 challenge flow, so render gets a generated client
instead of a hand-written one. (#12)
- **Error responses carry suggestions** (`openapi.yaml`) — the shared `Error` envelope gains
optional `suggestions[]`, `did_you_mean[]`, and `doc_url` fields, matching the gateway's
error layer. Additive only. (#10)
- **Word-level timestamps on the Voice API** (`openapi.yaml`) — `VoiceGenerateRequest` gains
a `timestamps` boolean (default `false`); when set, `VoiceGeneration.alignment` returns the
new `VoiceAlignment` schema with parallel `characters[]` and start/end time arrays. (#9)
- **Realtime control/event plane** (`openapi.yaml`) — adds the `Realtime` tag and
`/realtime/connect`, `/realtime/channels/{channel}/publish`, `/presence`, and `/history`
paths, each with a per-operation `servers` override pointing at
`https://realtime.wave.online`. (#4)
- **Fleet agent directory resolve** (`GET /identity/resolve`) — adds the `Identity` tag, the
`identityResolve` operation (`agent` query parameter, optional `org` self-assertion), and
the `IdentityResolveResponse` oneOf (`AgentIdentity` | `TelephonyIdentity`). Directory data
is public only: `key`/`keys` fields are key names, never values. Gated by the
`directory:read` scope. (#57)
- **SDK types generated from the spec** (`generated/api-types.d.ts`) — `openapi-typescript`
now emits typed paths, operations, and schemas from `openapi.yaml`, and a CI gate
regenerates them on every pull request and fails if the committed artifact has drifted.
(#49)
- **Breaking-change gate** — pull requests are diffed against the base branch with `oasdiff`;
an unacknowledged breaking change fails the build unless the PR body carries an explicit
`Breaking: yes` marker. (#49)
- **MoQ join-token mint surface** (`openapi.yaml`) — adds the `MoQ` tag and both mint
operations: `POST /moq/publish/{ns}/{track}` and `GET /moq/subscribe/{ns}/{track}`, the
`MoqJoinToken` response schema, path parameters constrained to `^[a-z0-9-]{1,64}$`, the
`X402PaymentRequired`/`X402Accepts` schemas and the reusable `PaymentRequired` response
(the 402 body is not the standard `Error` envelope). The media session itself is not
modelled, since it is not an HTTP surface; the spec pins the direct-to-relay flow to
`draft-ietf-moq-transport-18`. (#30)
- **WAVE Attestation Standard v1** (`attestation/` directory) — a frozen v1 specification
covering the wire envelope shape, canonicalization algorithm, Ed25519 signature scheme,
and verification procedure, plus a standalone verifier reference and the
`/.well-known/wave-attestation-keys.json` key-rotation contract (30-day overlap window, up
to 5 keys at once). (#14)
- **Attestation component schemas** (`openapi.yaml`) — `RenderAttestation` (the render
attestation v1 subject payload) and `WaveAttestation` (the full v1 wire envelope, with the
`alg`/`sig` invariant enforced). (#11)
- **`capabilities.json`** — registers this repository with the organization's platform
registry for discovery and lifecycle tracking. (#3)

### Changed

- **License: Apache-2.0** — the specification and repository now carry the Apache-2.0
license, with a NOTICE file reserving the WAVE marks; `openapi.yaml`'s `info.license` and
the README were updated to match, and the staging server entry was removed from the spec.
(#6)

### Deprecated

- **`GET/POST /videos/{videoId}/chapters` and `POST /videos/{videoId}/chapters/detect`** —
Expand All @@ -48,97 +137,19 @@ All notable changes to this project are documented here. The format is based on

### Fixed

- `pr-agent` lane: fork-triggered `/` commands are now refused, and the AI
call's budget fits inside its step. Three defects, one of them only visible
once the first was fixed.

The job-level `if:` refused forks on the `pull_request` arm and could not on
`issue_comment` — fork status is absent from that payload, so there was never
an expression to write. A `fork gate` step now asks the pulls endpoint and
fails closed: only a literal `false` proceeds, so a 404, a rate limit or a
deleted fork all skip. The lane runs no `actions/checkout`, so fork code was
never executed and no exfiltration path existed; what this closes is the
comment claiming forks were already skipped, which was true of one arm only.

`CONFIG__AI_TIMEOUT` was 600s inside a 360s step, so the runner killed the
step before pr-agent could reach its own timeout or fall back to a secondary
model. Now 300s.

Fixing the first exposed a third: `stamp attempt 2 end` runs under
`if: always()`, so when attempt 2 never ran the verdict subtracted from zero
and reported a 1787580408-second attempt as a confident TIMED OUT.

Contributors on forks are affected: a maintainer's `/review` on a fork PR is
now declined with a warning rather than silently running.
(wave-av/wave-foundation-public#73)

- **`X402PaymentRequired.error_detail` nesting** (`openapi.yaml`): `error_detail` referenced the
`Error` envelope (`{ error: { code, ... } }`), but the gateway nests the bare error object
directly under `error_detail` (`{ code, message, ... }`), with no inner `error` wrapper —
confirmed against a live 402 receipt (`curl https://api.wave.online/v1/clips`). The inner
object is now extracted as the `ErrorBody` component schema (referenced by `Error`, so every
other response is unchanged) and `error_detail` composes `ErrorBody` instead, matching the wire
shape so generated types no longer expect a nonexistent `error_detail.error` member.
shape so generated types no longer expect a nonexistent `error_detail.error` member. (#76)

## [1.0.0] - 2026-04-05

### Added

- **Fleet agent directory resolve** (`GET /identity/resolve`) — identity-fabric E1. Adds the
`Identity` tag, the `identityResolve` operation (`agent` query param, optional `org`
self-assertion), and the `IdentityResolveResponse` oneOf (`AgentIdentity` |
`TelephonyIdentity`) so generated clients see the documented telephony variation. Directory
data is public only: `key`/`keys` are Doppler key NAMES, never values. Gateway gate:
`directory:read` scope (distinct from compliance `identity:read`).
- **SDK types generated from the spec** (`generated/api-types.d.ts`) — `openapi-typescript@7.13.0`
now emits typed paths/operations/schemas from `openapi.yaml`, and the `sdk-types` CI gate
regenerates them on every PR and fails if the committed artifact has drifted from the spec.
Consumers get typed clients without keeping their own copy in sync.
- **Breaking-change gate** (`breaking-change` CI job) — PRs are diffed against the base branch
with `oasdiff`; an unacknowledged breaking change fails the build unless the PR body carries
the explicit `Breaking: yes` marker.

- **MoQ join-token mint surface** (`openapi.yaml`) — the Media over QUIC product had no spec at
all, so no SDK or CLI could be generated for it. Adds the `MoQ` tag and both mint operations:
- `POST /moq/publish/{ns}/{track}` (`mintMoqPublishToken`, scope `moq:write`) and
`GET /moq/subscribe/{ns}/{track}` (`mintMoqSubscribeToken`, scope `moq:read`), with the
optional `x-wave-declare-protocol` publish header.
- `MoqJoinToken` response schema (`relayWsUrl`, `joinToken`, `expiresIn`, `ns`, `track`, `role`,
`scope`, optional `protocol`) and the `MoqNamespaceParam` / `MoqTrackParam` path parameters
constrained to `^[a-z0-9-]{1,64}$`.
- Failure modes are specified alongside the happy path: `400 MOQ_JOIN_BAD_RESOURCE`, 401, the
402 x402 challenge, 403, 429, and the fail-closed `503 MOQ_JOIN_UNCONFIGURED`.
- `X402PaymentRequired` / `X402Accepts` schemas and a reusable `PaymentRequired` response — the
402 body is **not** the `Error` envelope (its `error` member is a string and the normalized
error object is nested under `error_detail`), which the spec previously did not capture.
- The MoQ media session itself is intentionally **not** modelled: it is not an HTTP surface. The
tag description explains the direct-to-relay flow, the `join` query parameter /
`x-wave-moq-join` header token carriers, and pins the surface to
`draft-ietf-moq-transport-18` (draft-19, published 2026-07-06, is not yet deployed).
- **`X402PaymentRequired.error_detail.payment_rejected`** (`openapi.yaml`) — documents the
additive `{ reason, rail }` deny verdict the gateway now publishes on a 402 when a submitted
payment was REJECTED, so generated types and docs can discover the diagnostic field. Optional
and conditional: absent on an ordinary unpaid challenge, present with a rejection reason token
and rail name when a submitted credential was denied — confirmed against a live rejected-payment
receipt (`curl -H "X-PAYMENT: <malformed>" https://api.wave.online/v1/clips`), which returned
`error_detail.payment_rejected: { reason: "invalid_payment_header", rail: "base-usdc" }`.
Closes #46.
- **WAVE Attestation Standard v1** (`attestation/` directory):
- `attestation/ATTESTATION-STANDARD-v1.md` — Frozen v1 specification: wire envelope
shape, field types, canonicalization algorithm (`canonicalJson` + `attestationId`),
Ed25519 signature scheme, verification procedure, and v0→v1 version history.
- `attestation/attestation-v1.schema.json` — JSON Schema Draft 2020-12 for the v1
envelope. Includes `$defs` for `RenderAttestationSubject`, `ContextAttestationSubject`,
and `SettlementAttestationSubject`. Enforces the `alg:"none"` ⟺ `sig:null` invariant
via `allOf/if/then/else`.
- `attestation/verifier-reference.md` — Standalone verifier procedure: step-by-step
algorithm, `VerifyResult`/`VerifyError` type definitions, helper function signatures
(`attestationSubject`, `canonicalJson`, `attestationId`, `fetchKeys`), and outcome
reference table.
- `attestation/well-known-keys.md` — `/.well-known/wave-attestation-keys.json` endpoint
specification: JWKS-style OKP/Ed25519 response shape, `kid`/`x`/`iat`/`exp` field
definitions, key rotation policy (30-day overlap window, ≤5 keys at once), signature
wire encoding, and security requirements.
- **OpenAPI component schemas** (`openapi.yaml` `components/schemas`):
- `RenderAttestation` — render attestation v1 subject payload (the `subject` field for
`kind: render` envelopes).
- `WaveAttestation` — full v1 wire envelope schema with `id`/`kind`/`v`/`subject`/
`alg`/`sig`/`created` fields and the `alg`/`sig` invariant.
- Initial public release: the WAVE OpenAPI 3.1 specification (12 API modules).

[Unreleased]: https://github.com/wave-av/api-spec/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/wave-av/api-spec/releases/tag/v1.0.0
Loading