Skip to content

Releases: alternet-dev/wavefront

v0.7.4

15 Jun 22:26
1f0148a

Choose a tag to compare

v0.7.4 — CORS pass-through for browser-facing deployments

v0.7.4 makes wavefront usable as a browser-facing gateway. A cross-origin SPA
call carrying Authorization triggers a CORS preflight, and wavefront had no CORS
handling: the OPTIONS preflight 404'd as an unbound route, and proxied responses
dropped the upstream's Access-Control-* headers — so every cross-origin browser
call through the gateway was blocked.

  • CORS pass-through (the upstream owns CORS). wavefront does not own CORS;
    the upstream is the single source of truth. A preflight — OPTIONS with Origin
    and Access-Control-Request-Method — is forwarded to the default upstream and
    its response relayed, bypassing routing, negotiation, and the codec, so it no
    longer 404s. On a proxied response, the upstream's Access-Control-* and Vary
    headers are carried through verbatim, on every response derived from an
    upstream reply: success, 204/205, declared error, the aid envelope, and the
    capability-ceiling 502. Origin already reaches the upstream as a forwarded
    client header, so it echoes the correct allow-origin. No new configuration or
    dependencies — the allowed-origins list stays in the backend (#147).

    Deliberate gap: a wavefront-originated response synthesized before any upstream
    call (unsupported_contract_version, unknown_route, unsupported_media_type) has
    no upstream CORS to mirror and carries none. Those are misconfigured-request /
    development conditions, not the normal browser flow.

  • feat: forward CORS preflights and relay upstream Access-Control-* (#148)

v0.7.3

10 Jun 23:32
d4aabcd

Choose a tag to compare

v0.7.3 — container self-probe + image HEALTHCHECK

v0.7.3 restores the orchestrator readiness signal for the container image. The
runtime image is distroless — no shell, no wget/curl — so an exec-style
container healthcheck (CMD-SHELL wget …) failed before it could probe anything
and reported a permanent false unhealthy; consumers had to drop their
healthcheck entirely.

  • wavefront probe --ready — the binary probes itself. The standard
    distroless pattern: the binary GETs its own ops listener's /ready in-process
    and maps the outcome to the healthcheck exit-code contract — 200 → exit 0,
    anything else (non-200, connection refused, timeout) → exit 1, usage error →
    exit 2. The ops address resolves from the same WAVEFRONT_METRICS_ADDR the
    server binds (only that var — probing never fails because serving-only
    required vars are absent), and a wildcard listen host (0.0.0.0, ::) is
    rewritten to loopback for the dial. No new dependencies (#145 closed).

  • Built-in image HEALTHCHECK. The Dockerfile declares the exec-form
    healthcheck — HEALTHCHECK --interval=10s --timeout=3s --start-period=5s
    CMD ["/wavefront", "probe", "--ready"] — so compose consumers inherit a
    working readiness signal with no per-deployment configuration. Kubernetes
    users keep pointing an httpGet probe at the ops port (no exec needed). A new
    image-health CI job builds the image, starts it against a generated bundle,
    and asserts container health flips to healthy.

  • feat: add a binary self-probe and image HEALTHCHECK (#146)

v0.7.2

10 Jun 22:35
56d9ca9

Choose a tag to compare

v0.7.2 — typed non-200 successes

v0.7.2 fixes a silent typing gap in the generator: a route whose success status
is not literally 200 lost its declared response type. The fix is additive — a
bundle whose successes are all 200 is byte-identical; only silently-untyped
non-200 routes gain their declared schema. The release also ships a
clean-auditing Homebrew formula.

  • The success response binds from the declared 2xx. wavefront-bundle add
    picked the success schema from the literal "200" response key only, while the
    per-status error collection deliberately skips all 2xx — so a route whose
    success is 201/202 (FastAPI emits the schema under the route's status_code,
    e.g. 202 for an async endpoint) fell through both collectors and silently
    bound the synthetic Empty, dropping its type. The success now binds from the
    declared 2xx; if a hand-authored spec declares several 2xx for one operation,
    the numerically-lowest wins, deterministically. A declared 2xx with no content
    at all (a 204, or a bare description-only entry) is genuinely bodyless and
    still binds Empty; a 2xx declaring only non-JSON content is still rejected
    (#143 closed).

  • Clean-auditing Homebrew formula. The formula rendered at release time now
    passes brew test-bot's tap-syntax audit: the redundant version line is
    dropped (inferable from the URL), the dual license is declared as
    any_of: ["MIT", "Apache-2.0"], and macOS-on-Intel resolves a URL for
    brew readall while depends_on arch: :arm64 keeps the formula
    Apple-Silicon-only on macOS (#142).

  • fix: bind the success response_message from the declared 2xx (#144)

  • release: emit a clean-auditing Homebrew formula (#142)

v0.7.1

01 Jun 02:32
8883449

Choose a tag to compare

v0.7.1 — generator coverage for real FastAPI surfaces

v0.7.1 widens the generator's coverage of real FastAPI/OpenAPI surfaces and
brings the CI and release workflows off the deprecated GitHub Actions Node 20
runtime. The generator changes are additive — they only accept shapes that
v0.7.0 hard-rejected — so any bundle that builds today is byte-identical.

  • Non-object components lower inline. A component that is an enum, typed
    dict, union, or open object — referenced by a field, the way FastAPI emits a
    str, Enum (e.g. JobPolicyMode) — now lowers the same as the inline form
    (scalar, map, google.protobuf.Value, google.protobuf.Struct) instead of being
    rejected with "must be an object with properties". A scalar enum lowers to its
    underlying scalar (string/int32), never a proto3 enum: protojson rejects
    unknown enum names, so typing the enum would let an old contract reject a value
    a newer backend added — the version skew wavefront exists to remove. A
    non-object component bound directly as a body stays a hard error (#133).

  • Bare {"type":"object"} lowers to Struct. A bare object with no properties
    and no additionalProperties — the shape FastAPI emits for ValidationError.ctx
    — lowers to google.protobuf.Struct like any open object, singular and as an
    array item (#134).

  • Illegal proto-identifier names are sanitized. A field key or component
    name with a character outside [A-Za-z0-9_] (a dotted Pydantic alias, or
    Pydantic v2's -Input/-Output schema split) no longer aborts descriptor
    validation: the proto identifier is sanitized while the original wire key is
    preserved via json_name (no wire-shape change), and collisions are
    disambiguated so two distinct names never fuse (#135).

  • CI and release workflows on Node 24. Every actions/* and docker/* action
    in the CI and release workflows is bumped off the deprecated Node 20 runtime
    to its current Node 24 major, and the release workflow's force-node24 stopgap
    is removed (#136).

  • feat: sanitize illegal proto-identifier names in the generator (#138)

  • feat: lower bare {"type":"object"} to google.protobuf.Struct (#139)

  • feat: lower non-object components inline like the equivalent inline schema (#140)

  • ci: migrate GitHub Actions off the deprecated Node 20 runtime (#141)

v0.7.0

31 May 09:57
051c01f

Choose a tag to compare

v0.7.0 — generator coverage + richer error typing

v0.7.0 widens what wavefront expresses on both ends of the contract. The
generator bundles a broader range of real FastAPI/OpenAPI surfaces — empty
objects, typed and open dictionaries, and polymorphic unions — and the proxy
stops collapsing every non-success into one envelope: a declared (route, status)
is now a typed, status-preserving contract response. Generator output stays
byte-reproducible — the well-known-type descriptors are embedded in a layer only
when a schema actually uses them.

  • Empty object schemas bundle as zero-field messages. A component that is an
    explicitly-empty object ({"type":"object","properties":{}}) — the shape
    FastAPI emits for a fieldless model — bundles as a zero-field proto message
    instead of being rejected. A bare {"type":"object"} with no properties key is
    still a hard error, the open/untyped-object boundary (#118 closed).

  • Typed additionalProperties lowers to a proto3 map. A typed dictionary
    (additionalProperties: <scalar|$ref>, no declared properties of its own)
    lowers to map<string, V> (#119 closed).

  • Open objects and untagged unions lower to well-known types. An open object
    (additionalProperties: true | {}) lowers to google.protobuf.Struct; an untagged
    anyOf/oneOf union or an unconstrained {} schema lowers to google.protobuf.Value
    (repeated as array items). google/protobuf/struct.proto is embedded in the
    layer only when used, so bundles that need neither stay byte-identical. This
    types FastAPI's HTTPValidationError.loc: list[str | int] (#121).

  • Discriminated unions lower to Struct. A tagged anyOf/oneOf (one carrying a
    discriminator) serializes flattened — the discriminator is a sibling property
    and the member's fields sit at the top level — which a proto3 oneof cannot
    carry (protojson encodes a oneof wrapped under the member key). It lowers to
    google.protobuf.Struct, which relays the object verbatim (#129 closed).

  • Upstream-origin status codes and typed error bodies. wavefront no longer
    collapses every non-success to a fixed wavefront.v0.Error envelope. A declared
    (route, status) is a first-class typed contract response: the upstream status
    is preserved and no X-Wavefront-Error header is set. Undeclared statuses fall
    to the graceful aid envelope. wavefront honors whatever status the upstream
    sends except a hard capability ceiling — 206/207/208/226 and any 3xx map to
    502. A per-route strict: true flips the undeclared-status default to a hard
    502. resolution.yaml field transforms (rename/default/optionalize/coerce) fire
    per (status, message) pair, not only on 2xx (#53 closed).

  • fix: accept empty object schemas as zero-field messages (#122)

  • feat: lower a typed-dict additionalProperties to map<string,V> (#123)

  • feat: bind per-status error_messages and strict in the bundle loader (#124)

  • feat: collect and bind declared error response types in the generator (#125)

  • feat: honor upstream status, type declared errors, relay the rest (#126)

  • feat: apply field transforms per declared error status (#127)

  • test: exercise per-status error transforms across a version chain (#128)

  • feat: lower untagged unions and open objects to well-known types (#130)

  • feat: lower discriminated unions to google.protobuf.Struct (#131)

v0.6.1

30 May 04:13
e881623

Choose a tag to compare

v0.6.1 — OpenAPI 3.1 surface tolerance

wavefront-bundle add now accepts three OpenAPI 3.1 shapes that v0.6.0 rejected,
without loosening the fail-loud contract for genuinely unsupported constructs.
These are the shapes a real FastAPI surface emits: in the reference consumer's
schema they account for nearly every anyOf, additionalProperties, and empty
inline-schema occurrence. Generator-only — the proxy data plane is unchanged.

  • Nullable anyOf lowers to proto3 optional. The OpenAPI 3.1 nullable idiom
    anyOf: [T, {type: null}] — where T is a scalar or a $ref — now lowers to a
    proto3 optional field, the same result as 3.0's nullable: true. A genuine
    non-nullable union (anyOf of two distinct types) is still a hard error
    directing you to a $ref with a discriminator field (#112 closed).

  • additionalProperties: false is a no-op. An explicit
    additionalProperties: false matches proto3's closed-message default and is now
    accepted as a no-op, producing byte-identical descriptors to omitting it. The
    unsupported forms — additionalProperties: true and the typed-dict
    additionalProperties: {schema} — remain hard errors (#113 closed).

  • Empty inline schema is bodyless. An empty inline schema: {} carries no
    shape, so it is treated as semantically bodyless and binds the synthetic
    wavefront.gen.v.Empty message (the path introduced in v0.6.0) instead
    of being rejected as an unsupported inline schema. A non-empty inline schema is
    still a hard error (#114 closed).

  • fix: lower the OpenAPI 3.1 nullable anyOf shape to proto3 optional (#117)

  • fix: tolerate additionalProperties: false as a no-op (#116)

  • fix: bind synthetic Empty for an empty inline schema {} (#115)

v0.6.0

30 May 00:50
37c8a0f

Choose a tag to compare

v0.6.0 — real-world surface readiness

The generator and proxy now cover the read side of a real HTTP surface:
bodyless operations — GET reads, path-only POSTs, and 204 responses — bundle
and proxy end-to-end, where v0.5 rejected them. wavefront also becomes visible
in distributed traces, emitting its own attributed span per request, and the
release workflow now renders these notes from the tag annotation itself. The
span ships without the OpenTelemetry SDK — a hand-rolled OTLP/HTTP exporter
keeps the server's dependency tree free of opentelemetry and grpc (#92 resolved
by approach).

  • Bodyless operations end-to-end. wavefront-bundle add no longer rejects
    operations with no request body or no 200 response: each layer synthesizes a
    single wavefront.gen.v.Empty message, and bodyless operations bind it
    for the request and/or response side. At runtime a request_message of Empty
    produces a clean bodyless upstream call (no body, no Content-Type — not {}
    with application/json), and a response_message of Empty emits no response
    body. Path and query parameters still pass through verbatim (#105 closed).

  • wavefront-attributed OpenTelemetry span. With
    WAVEFRONT_TRACES_OTLP_ENDPOINT set, wavefront emits one wavefront.proxy span
    per request to an OTLP/HTTP collector, continuing the inbound W3C traceparent
    (trace id preserved, the inbound span becomes the parent, fresh child span id)
    while still forwarding the client's traceparent/tracestate untouched. Each
    span carries contract_version, target, and transform_outcome attributes and a
    service.name resource (WAVEFRONT_TRACES_SERVICE_NAME, default wavefront);
    WAVEFRONT_TRACES_SAMPLE_RATIO in (0,1] sets the root-trace sample probability.
    Emission is non-blocking and best-effort — a full queue or a collector error
    drops the span, never the request. Unset endpoint = disabled, zero overhead.
    Implemented as a hand-rolled OTLP/JSON exporter, no OTel SDK (#71, #92
    closed).

  • Release notes from the tag annotation. The release workflow extracts the
    annotated-tag body explicitly (git tag -l --format='%(contents)' into
    --notes-file) instead of --notes-from-tag, which had silently fallen back to
    the commit message on v0.5.0 (#106 closed).

  • feat: synthesize a synthetic Empty message for bodyless operations (#107)

  • feat: send bodyless upstream requests / empty responses for Empty (#111)

  • feat: emit a wavefront-attributed OTLP span per request (#110)

  • fix: extract release notes from the tag annotation explicitly (#109)

v0.5.0

29 May 20:51
9a5ae06

Choose a tag to compare

v0.5.0 — consumer surface

The wire matches what the docs claim, the generator can emit multi-route
bundles, the proxy serves them end-to-end, and brew install delivers
wavefront-bundle as a fast binary download. The TypeScript-specific codegen
was considered, scoped to message emission, then dropped — replaced by
docs/client.md showing how to wire any language's protoc-gen-X against the
bundle directly. OTel SDK adoption was deferred after a dependency-footprint
analysis (#92).

  • Wire-contract conformance. Five new X-Wavefront-Error codes:
    internal_error, unknown_route, upstream_status (bounded passthrough set
    401/403/404/405/409/410/422/429/451), unsupported_media_type, unavailable.
    2xx fidelity preserved; upstream redirects refused; panic recovery; strict
    (path, method) route gating. New wavefront_stdlib_boundary_total metric
    labelled by status (#39, #54 closed).

  • Multi-route bundle generator. wavefront-bundle add now walks every
    paths.. operation in the input OpenAPI; the proxy serves multi-route
    bundles via (path, method, version) negotiation. --force flag for
    same-info.version re-add (#77 closed).

  • Per-language client integration guide. docs/client.md documents how
    to wire the bundle into TypeScript / Go / Python / Rust applications using
    each language's standard protoc-gen-X. No wavefront-specific codegen
    surface (#72 closed).

  • Homebrew binary distribution. brew install alternet-dev/tap/wavefront-bundle
    becomes a fast binary download instead of a source build; per-arch matrix
    for darwin-arm64 / linux-arm64 / linux-amd64; formula scoped to the CLI
    only (server stays container-only). Intel-Mac (x86_64-apple-darwin) is
    intentionally not built — the macos-13 runner queue is too slow to gate a
    release on; Intel-Mac users use the Linux binary via Rosetta or build from
    source (#73 closed).

  • Server timeout + 100-continue posture. WAVEFRONT_READ_HEADER_TIMEOUT_MS
    and WAVEFRONT_READ_TIMEOUT_MS env-configurable; oversized declared bodies
    rejected with 413 before any 100 Continue is sent.

  • server: panic-recovery middleware and shared wire-error helper (#75)

  • feat: unknown_route 404 envelope on unmatched routes (#76)

  • feat: wavefront-bundle gen-ts-client subcommand skeleton (#78) [reverted in #100]

  • feat: per-arch binary build matrix for wavefront-bundle releases (#79)

  • feat: preserve upstream 2xx status on response (#80)

  • feat: TS message-class emission via protoc-gen-es (#81) [reverted in #100]

  • build: render-homebrew-formula.sh for wavefront-bundle releases (#82)

  • feat: upstream-status passthrough for bounded 4xx set (#83)

  • feat: emit unsupported_media_type 415 for wrong Content-Type (#84)

  • feat: refuse upstream redirects (3xx -> 502) (#85)

  • feat: env-configurable data-plane read timeouts; 413 before 100 Continue (#86)

  • feat: emit 503 unavailable on proxy path when bundle not loaded (#87)

  • feat: defense-in-depth check for proto-package collisions across bundle layers (#88)

  • test: set Content-Type on redirect tests so 415 gate doesn't intercept (#90)

  • feat: multi-route bundle generator emission core (#93)

  • build: wire render-homebrew-formula.sh into update-tap, rename formula to wavefront-bundle (#94)

  • feat: observability for stdlib-boundary HTTP responses (labelled by status) (#96)

  • docs: protocol.md and concepts.md updates for wire-contract conformance (#97)

  • feat: serve multi-route bundles end-to-end (#98)

  • feat: --force flag for wavefront-bundle add (#99)

  • docs: client.md per-language integration guide (drop gen-ts-client subcommand) (#100)

  • docs: AGENTS.md — correct stale 'no in-place reload' claim (#103)

  • build: drop Intel-Mac (x86_64-apple-darwin) from release matrix (#104)

  • OTel SDK adoption — dropped after dep-footprint analysis (#92, plus upstream open-telemetry/opentelemetry-go#8385 and #8386)

  • Per-language client codegen revisit — captured in #102

  • Intercept stdlib-boundary statuses with the wire envelope — captured in #91

  • Bundlegen polish (confidence knobs + intent files) — captured in #74

  • Inbound HTTP/2 / h2c / HTTP/3 — captured in #101

v0.4.0

25 May 03:35
4cd7d46

Choose a tag to compare

v0.4.0 — drift detection, observability, and hot reload

bundle draft-shim auto-drafts a transform-shim override from two layers'
OpenAPI documents: confident stanzas for unambiguous changes, candidates
blocks for ambiguous renames the operator must resolve before bundle
verify accepts the result. Per-request structured logging and labelled
metrics (contract_version, transform_outcome) let an operator slice
traffic and failures by cohort. SIGHUP swaps the bundle in place;
buf breaking gates wavefront.v0.Error against breaking edits.

v0.3.0

25 May 03:35
7b60a25

Choose a tag to compare

v0.3.0 — multi-version bundle

One committed bundle carries every supported contract version as an immutable
per-version layer. Adds per-version resolution (a backend route, or a
transform shim), transform-shim chains across versions, multi-target routing,
and the wavefront-bundle add / remove / retire / verify tooling.