Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/scripts/published-drift-allowlist.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@
"direction": "unpublished-repo",
"justification": "Same reasoning and same measurement date as DELETE /videos/{videoId}/chapters/{chapterId}: live-probed and confirmed answering, not ROUTE_NOT_MAPPED, but absent from the published /openapi.json document. A gap in the service's own spec generation. Lapses (and should be deleted) the moment the published document lists this operation."
},
{
"path": "/enhance",
"method": "POST",
"direction": "shared-drift",
"justification": "Measured live 2026-09-05: an unauthenticated POST to https://api.wave.online/v1/enhance returns 402 (x402 challenge, atomicAmount 600000 matching this repo's documented wave_enhance_minutes meter), confirming the route is genuinely live and priced. The published /openapi.json document still carries the auto-generated skills-index draft placeholder (x-schema-status: draft, operationId enhance, application/json additionalProperties:true body) because the service's own spec generation has not caught up to the real handler. openapi.yaml has been hand-documented ahead of that (operationId enhanceVideo, binary video body, model/url query params, per-job response headers) from the actual endpoint behavior, not guessed. Exempt only while the published operation still claims x-schema-status: draft; the moment the service promotes it out of draft, the two shapes get compared for real.",
"expect": {
"x-schema-status": "draft"
}
Comment thread
yakimoto marked this conversation as resolved.
},
{
"path": "/identity/resolve",
"method": "GET",
Expand Down
54 changes: 54 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,60 @@ All notable changes to this project are documented here. The format is based on

### 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
Expand Down
20 changes: 14 additions & 6 deletions contract-drift.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"about": "Point-in-time operation-level diff between this repo's openapi.yaml and the contract the gateway publishes. It is a dated receipt, not a live view: regenerate with `node .github/scripts/published-drift.mjs openapi.yaml --out contract-drift.json`. The published-contract-drift workflow uploads a fresh copy on every scheduled run. The PUBLISHED half of this receipt ages on the gateway's schedule and only the scheduled drift job can refresh it; the REPO half is pinned by sources.repoOperationsDigest, which the freshness job checks offline on every run so this file cannot quietly disagree with the openapi.yaml sitting next to it.",
"generatedAt": "2026-09-06T03:41:45.745Z",
"generatedAt": "2026-09-06T05:15:13.584Z",
"criterion": [
"CONTRACT-001",
"COMPAT-001",
Expand All @@ -10,7 +10,7 @@
"repoSpec": "openapi.yaml",
"repoCommit": null,
"publishedSpec": "https://api.wave.online/openapi.json",
"repoOperationsDigest": "370de508452ec90d02d457c2aa66d1c5c3b1811957b23d14beeea6037ff7ba41"
"repoOperationsDigest": "c444b8649ef29f806ee2a0a178ed1fca487582e7e10ddd5e7f4fa651b1c98c8a"
},
"headline": {
"repoVersion": "1.1.0",
Expand All @@ -26,7 +26,7 @@
"liveProbed": 0,
"sharedDrift": 0,
"draftNotYetPublished": 0,
"allowlisted": 10,
"allowlisted": 11,
"lapsedAllowlistEntries": 0,
"unmatchedAllowlistEntries": 0
},
Expand Down Expand Up @@ -94,6 +94,12 @@
"direction": "shared-drift",
"justification": "Measured live 2026-09-05: an unauthenticated POST to https://api.wave.online/v1/voice/generate with { text } returns raw audio/mpeg bytes directly, not the JSON envelope. openapi.yaml's 200 description was rewritten to state that audio/mpeg is the default and the application/json shapes (VoiceSynthesisInline / VoiceGeneration) are returned only when the request opts out of it; the published document still carries the older, vaguer three-shapes description. This is a documentation-precision improvement grounded in live behavior, not a behavioral change this repo needs to walk back. Keyed on the exact current published 200 description so a real future change to the service's generated shape still surfaces as drift."
},
{
"path": "/enhance",
"method": "POST",
"direction": "shared-drift",
"justification": "Measured live 2026-09-05: an unauthenticated POST to https://api.wave.online/v1/enhance returns 402 (x402 challenge, atomicAmount 600000 matching this repo's documented wave_enhance_minutes meter), confirming the route is genuinely live and priced. The published /openapi.json document still carries the auto-generated skills-index draft placeholder (x-schema-status: draft, operationId enhance, application/json additionalProperties:true body) because the service's own spec generation has not caught up to the real handler. openapi.yaml has been hand-documented ahead of that (operationId enhanceVideo, binary video body, model/url query params, per-job response headers) from the actual endpoint behavior, not guessed. Exempt only while the published operation still claims x-schema-status: draft; the moment the service promotes it out of draft, the two shapes get compared for real."
},
{
"path": "/identity/resolve",
"method": "GET",
Expand Down Expand Up @@ -139,12 +145,14 @@
"GET /videos/{videoId}/chapters",
"POST /videos/{videoId}/chapters",
"POST /videos/{videoId}/chapters/detect",
"POST /studio-ai/enhancements",
"GET /realtime/connect",
"POST /realtime/channels/{channel}/publish",
"GET /realtime/channels/{channel}/presence",
"GET /realtime/channels/{channel}/history",
"POST /braid/publish",
"DELETE /braid/publish/{ns}",
"POST /enhance",
"POST /av/remux",
"POST /av/demux",
"POST /moq/publish/{ns}/{track}",
Expand Down Expand Up @@ -208,7 +216,6 @@
"POST /embeddings",
"POST /encode",
"POST /engagement",
"POST /enhance",
"POST /example",
"POST /experiments",
"POST /fleet",
Expand Down Expand Up @@ -310,11 +317,12 @@
"POST /zoom-integration"
],
"operationIdsSynthesized": 0,
"errorResponsesInjected": 799,
"errorResponsesInjected": 797,
"errorResponsesOverwritten": [
"GET /videos/{videoId}/chapters (404)",
"POST /videos/{videoId}/chapters (404)",
"POST /videos/{videoId}/chapters/detect (404)"
"POST /videos/{videoId}/chapters/detect (404)",
"POST /enhance (400, 401)"
],
"parametersStripped": 77
},
Expand Down
Loading
Loading