Skip to content

Latest commit

 

History

History
336 lines (313 loc) · 24.3 KB

File metadata and controls

336 lines (313 loc) · 24.3 KB

Changelog

All notable changes to this project are documented here. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Added

  • Composer surface (openapi.yaml) — the gateway (build d62760094) serves three composer:*-scoped operations that had no spec entry, so no SDK, CLI or MCP method could be generated for them and the published contract's operation-parity check (CONTRACT-001) failed on exactly these three (api-spec#92, api-spec#91). Documented verbatim from the published contract at https://api.wave.online/openapi.json, fetched 2026-09-08T14:06:58Z:
    • POST /compose (createComposeProposal, scope composer:write) — composes a PLAN from a plain-language intent. Never calls a product and never bills; stamps x-wave-meter: wave_compose_proposals (counted, never billed).
    • POST /compose/run (runComposeProposal, scope composer:write) — runs a stored proposal once, gated on input validity, idempotency, manifest freshness, budget and the declared-IO typing gate. The 200 documented here is the replay shape (ComposeRunReplay) for a repeat call with the same idempotency key and input.
    • GET /compose/proposals/{proposalId} (getComposeProposal, scope composer:read) — org scoped re-read of a stored proposal by id.
    • Adds the composer tag and seven component schemas the three operations reach transitively: ComposeProposalCreate, ComposeProposal, ComposeCandidate, ComposeRetrievalInfo, ComposeRunCreate, ComposeRunInput, ComposeRunReplay. Two nullable: true fields the published contract carries (ComposeProposal.flowId, ComposeCandidate.product) and one on ComposeRunReplay.code are expressed here as type: [string, 'null'] to match this repo's declared openapi: 3.1.0 dialect — the OpenAPI 3.0-style nullable keyword the gateway publishes is not valid 3.1 struct and fails redocly lint's struct rule; this is the same class of publishing-service leakage the drift checker already treats as non-drift enrichment, not a content difference (compare() diffs unresolved $ref strings at the operation level, so this syntax normalization does not affect the published-contract-drift shared-drift finding for these operations).
    • Only the 200 response is documented for each operation because that is the only response the published contract declares for them; no 4xx/5xx were invented. This is a real gap in the live surface, not one this PR introduces: POST /compose/run's own description prose (copied verbatim from the published contract) names 501 RUN_NOT_YET_DECLARED, a 402 budget-exceeded refusal and a 409 COMPOSE_RUN_CONFLICT idempotency conflict, and the only documented 200 is the replay shape (ComposeRunReplay, replayed: const true) — the published contract does not model a first-run/fresh-execution response shape at all. Re-verified against a fresh fetch of https://api.wave.online/openapi.json (2026-09-08T15:41:07Z): unchanged, still only a bare 200. Modeling the missing error responses or a fresh-run schema here would mean inventing content the gateway does not publish — which would both violate this PR's own verbatim-extraction rule and reintroduce shared-drift/content-digest findings against the very published-contract-drift check this PR exists to satisfy (CONTRACT-001's compare() diffs each operation's raw responses map). Tracked as a live-contract completeness gap for the service side, not fixed by fabricating response shapes here.
    • Not modelled here: 64 pre-existing x-lifecycle-field shared-drift findings across other, unrelated operations, and CONTRACT-001's content-digest check (which was already failing before this change, over the same 64 operations) — both out of scope for api-spec#92.

Changed

  • POST /voice/generate contract clarified against the live gateway. The 200 response now documents that the primary path returns raw audio/mpeg bytes directly; the application/json shapes (VoiceSynthesisInline, VoiceGeneration) are returned only when the request opts out of that default. VoiceGenerateRequest now requires only text — voiceId is optional (the gateway picks a default when omitted) and is also accepted on the wire as the voice_id alias the SDK sends; supplying both voiceId and voice_id on the same request is now rejected at the schema level.

    Note: this PR originally also proposed renaming POST /voice/generate to POST /voice and replacing ClipCreate's contract. Both are dropped from this revival: the live gateway's x402 paywall gates on the top-level product path segment (/voice/...) for ANY sub-path, including ones that don't exist, so an unauthenticated 402 probe cannot distinguish /voice from /voice/generate — the rename claim is unverifiable and main already documents /voice/generate, so it stays. ClipCreate was independently re-verified against the live gateway on main since this PR branched (source + in, with out/duration as alternatives, sourceType, visibility, and more) and is more thorough than this PR's version; main's ClipCreate is kept as-is.

Added

  • Enhance AI video super-resolution surface (openapi.yaml) — the live-routed POST /v1/enhance route had no spec entry, so no SDK or CLI method could be generated for it. Adds the Enhance tag and the enhanceVideo operation:

    • POST /enhance (scope enhance:write), x402-payable via the reusable PaymentRequired 402 response. v1 ships exactly one model, espcn (ESPCN super-resolution, fixed 3x factor baked into the trained weights); any other model value 400s.
    • Input as raw request body (video/* / application/octet-stream) or a server-side ?url= fetch (https only, non-public hosts rejected), capped at 200 MiB either way.
    • Binary streaming output with per-job receipt headers: x-enhance-model, x-enhance-scale-factor, x-enhance-input-dimensions, x-enhance-output-dimensions, x-wave-meter, x-wave-usage-minutes. Billed against wave_enhance_minutes (output duration in minutes, rounded up).
    • Failure modes specified alongside the happy path: 400, 401, the 402 x402 challenge, 403, 413, 422 INPUT_TOO_LARGE, 429, 501 (spoke not provisioned), 502, and 503 with Retry-After. Cross-referenced with the async Studio AI enhancement surface (POST /studio-ai/enhancements).
  • 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).
  • 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.
  • Body content-policy gate (body-guard CI job, scripts/public-repo-guard/body-policy.sh) — PR titles/bodies, issue bodies, and comment bodies are now scanned server-side, the half of a public repo's surface the tree gate never covered. Blocks credential formats, infrastructure identifiers (internal IPs, operator home paths, hardcoded account IDs), self-identified internal-only markers, and a private repo named near operational detail. A line carrying guard:allow <reason> exempts the infra-identifier tier; credential formats block unconditionally (defang the string to discuss one).

  • Streams, productions, cameras, moderation, live pipeline, billing, and analytics operations (openapi.yaml) — 26 hand-documented operations backing the hosted MCP tool surface, replacing the draft additionalProperties: true placeholders for /billing, /cameras, /moderate, /productions, /streams, and /usage: stream lifecycle (listStreams, createStream, getStream, startStream, stopStream, getStreamStatus, getStreamAnalytics, listStreamHighlights, markStreamHighlight), content moderation (moderateContent), the live transcription pipeline (startLivePipeline, transcribeLiveAudio), multi-camera productions (listProductions, createProduction, getProduction, switchProductionCamera, setProductionOverlay), managed cameras (listCameras, registerCamera, controlCamera), and billing/analytics (getBilling, getBillingUsage, getUsage, getAnalyticsOverview, getAnalyticsTopContent, getAnalyticsEngagement). Every request/response shape is read from the live or in-review route handler, not guessed.

  • 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 none: 158 new POST /{name} operations (one per missing product), 157 new tags, and a bearerWithScopes OAuth2 security scheme carrying one scope per capability, all drawn verbatim from the live index (no invented fields). Each new operation carries:

    • x-schema-status: draft — the request/response shape is additionalProperties: true because the actual payload contract is not published anywhere the spec can read it.
    • x-skill-url — the capability's own skill document.
    • x-price — model/currency/network/meter from the live index, plus, where an unauthenticated GET on the route returned a real x402 402 challenge, the observed atomicAmount and asset (verified live 2026-09-02; most capabilities gate at a flat 1000-atomic-unit entry price, one at 600000 — these are the gateway's real numbers, not estimates).
    • Coverage: before 21/178 → after 178/178 (1 allowlisted: internal, which the live index itself marks pricing.model=free and auth.scope=null).
  • skills-index-coverage CI check (.github/scripts/skills-index-coverage.mjs, .github/scripts/skills-index-allowlist.json) — fetches the live skills index on every PR 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 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)

Documentation

  • docs: rewrite README — restructured the top-level README (badges, quick start, repo layout, related-packages table) and refreshed it against the current spec: clarified that the x402-payable render operations (renderVideo, renderPoll, renderEvents) and the device-authorization-flow responses are exceptions to the shared Bearer-token/Error-envelope shape, and fixed the error-envelope example to match the Error schema's actual field types (details is an object, suggestions/did_you_mean are arrays, not strings).

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 — marked deprecated: true / x-status: unrouted. Verified live 2026-09-02: the gateway returns 403 ROUTE_NOT_MAPPED ("this path and method are not part of the WAVE API") for both paths. The live-priced Chapters capability is the flat POST /chapters operation (added above); the nested shape stays documented — deprecated rather than deleted — until it is either wired up or formally removed.

  • /leaderboard and /platform — confirmed live 2026-09-02: both return 403 ROUTE_NOT_MAPPED at the gateway. Neither appears in this spec (never did) nor in the live gateway skills index (not a priced capability), so nothing here needed a deprecated: true marker — they are documented on the publicly served openapi.json at the API host but are not real operations. Recommend the publicly served copy drop them; out of scope for this spec since they were never present here.

Fixed

  • 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. (#76)
  • ClipCreate (openapi.yaml) documented {videoId, startTime, endTime}; the live POST /clips route's own request validator requires source (a recording id) plus an in time string and never accepted videoId. Corrected the schema to the real contract and added ClipCreateResponse/ ClipError for the operation's actual 201/400 shapes (the 400 body is the route's own {ok, error, detail} envelope, not the shared Error schema).
  • GET /leaderboard, GET /platform (openapi.yaml) — documented for real, following the 1.1.0 gateway deploy that moved both gateway-native root surfaces behind operator/tenant auth (measured live 2026-09-06). Both had been exempted in the drift allowlist as undocumented-live while unauthenticated; the exemption's own justification named real documentation as the intended remedy once each operation gained a security requirement, which it now has. New Operator tag for /platform's operator-only shape.
  • .github/scripts/published-drift-normalize.mjs: two new normalization rules measured against the 1.1.0 publish — the service overwrites a hand-written 4xx response with its generic injected envelope even when this repo already declares a real one for that code (previously only the "repo lacks the code" case was normalized), and it drops a parameter's description/example/schema.pattern while publishing its name/in/required/type faithfully. Both are matched by exact residual shape, never by key name, and both are reported (not silently absorbed) via new enrichmentObservations fields.
  • .github/scripts/published-drift-allowlist.json — new shared-drift exemptions for GET /usage (a key collision between this repo's real /v1 billing endpoint and the gateway's own unrelated, operator-only telemetry route that publishes at the same literal path with no distinguishing servers override), GET/POST /streams (the gateway still serves the auto-generated skills-index placeholder; this repo has promoted the real /v1 shape ahead of the gateway shipping it), and POST /agent/auth/token (the service's own generated 400 response is a coarser single-schema shape than this repo's accurate oneOf documentation of the RFC 8628 device-flow passthrough). New unpublished-repo exemptions for DELETE /videos/{videoId}/chapters/{chapterId} and GET /videos/{videoId}/chapters/detect/{jobId}: both are live-probed and confirmed answering (not ROUTE_NOT_MAPPED), but the published /openapi.json document has not yet registered them — a gap in the service's own spec generation, not in this repo's declaration.

1.0.0 - 2026-04-05

Added

  • Initial public release: the WAVE OpenAPI 3.1 specification (12 API modules).