Repository navigation
Releases: voidrunner3074/aastro
Release list
0.11.1
Fixed
http2: offbroke every TLS connection with an HTTP/2-capable peer instead of pinning it to HTTP/1.1 - on the
data port clients got the connection dropped mid-request, on an upstream every request failed with 502.offonly
restrictedhttp.Server/http.Transport.Protocols, while the TLS config still hardcoded ALPN
NextProtos: [h2, http/1.1], so the handshake kept negotiatingh2that the HTTP layer then couldn't speak. ALPN
now advertises onlyhttp/1.1whenhttp2: off, on both the server and the upstream side.autoandonare
unaffected, as is anything without TLS (no ALPN there, so no HTTP/2 to negotiate in the first place).
0.11.0
Added
http2: auto | on | offongateway.serverand per-upstreamtransport.auto(the default)
is exactly today's behavior: HTTP/2 negotiated over TLS, HTTP/1.1 otherwise - existing configs
are unaffected.offpins either side to HTTP/1.1 even when TLS is on - the escape hatch for a
backend with a flaky HTTP/2 stack, or a quick killswitch on the data port (e.g. for HTTP/2 Rapid
Reset-style concerns) without touching TLS config.onrequirestls.enabledat the same scope- the gateway has no cleartext HTTP/2 (h2c) support - and is rejected by config validation
otherwise.
- the gateway has no cleartext HTTP/2 (h2c) support - and is rejected by config validation
Changed
- Internal: upstream transports now set HTTP/2 via
http.Transport.Protocolsinstead of the
deprecatedgolang.org/x/net/http2.ConfigureTransport, dropping the direct dependency on
golang.org/x/net(now indirect-only, pulled in transitively). Not user-facing by itself -
http2: autobehaves exactly as before - but it's what made the explicit on/off knob above
straightforward to add on the client side; the data port gets the equivalent treatment via the
samehttp.Server.Protocolsfield.
0.10.0
Fixed
- Retries never fired for an upstream without an explicit
method:-retry_on_statuses/timeout retries were
silently a no-op for the common case of relying on the flow's own method.shouldRetryjudged idempotency against
the raw (often empty) config value instead of the effective method used for the actual request. (The 0.9.0 entry
below describing this as fixed was itself wrong: the switch cases changed, but the call site never did.) - The
TEresponse header was never stripped despite being listed in the hop-by-hop set - the map key was the literal
string"TE", butnet/httpalways stores and iterates response headers innet/textprotocanonical form, which
forTEisTe(no hyphen to separate words, so only the first letter is capitalized). The lookup silently missed
every time, leaking the header to the client. Every other entry in that set was already canonical. policy.header_blacklistentries were matched by exact string, not canonicalized -header_blacklist: ["x-secret"]
silently never blocked a response header thatnet/http(correctly) stores asX-Secret. Same root cause as the
TEfix above: HTTP header names are case-insensitive, but the code was comparing them as case-sensitive strings.
Changed
- Breaking: flow option
passthroughrenamed tostreaming(config field, OpenAPIx-aastroextension, and
aastroctl openapi import --mode). Existing configs and specs must renamepassthrough: truetostreaming: true
before upgrading. - Breaking: a flow with exactly one upstream is no longer wrapped in the JSON response envelope. Its upstream's
status, headers, and body are now forwarded to the client as-is - on success, and on a client error or redirect
(2xx/3xx/4xx), including bodies that aren't valid JSON, which previously got swallowed into an
UPSTREAM_CLIENT_ERROR/UPSTREAM_REDIRECTenvelope. Only a genuine gateway-side failure (upstream unavailable,
5xx, timeout, policy violation) still returns a gateway-authored error body - see the envelope removal entry
below for its current shape, which by the end of this release also covers multi-upstream flows. Single-upstream
flows are now handled by their own dispatch path, bypassing the scatter/aggregate machinery entirely - see
Router.dispatch/Router.buildProxyResponse. aggregationis no longer required for a single-upstream flow, since it's never read for one - only a flow with
more than one upstream must declare it (config validation enforces this in place of the old struct-tag check).aastroctl openapi import --mode enveloperenamed to--mode proxy, matching the response shape above; scaffolded
(single-upstream) flows no longer get a meaninglessaggregation: {strategy: array}block.- Internal: the several independent switch statements over the upstream error classification (retry eligibility,
circuit breaker signal, policy applicability, client error code) were consolidated into one table
(kindTableinupstream_error.go), checked exhaustive at startup. Not user-facing, but worth knowing if you're
extending upstream error handling: add the new kind's row there rather than hunting down every switch. - Breaking: the
{"data": ..., "errors": ..., "meta": ...}envelope is gone entirely, including for
multi-upstream (aggregating) flows - a client no longer unwraps a gateway-specific shape to get at the payload:- A full or partial (
206, bestEffort) success returns the aggregated data itself as the body - exactly the
merge/array/namespaceresult, nothing wrapping it. Which upstreams failed on a partial success moves to
theX-Partial-Errorsresponse header (one value per failure, e.g.UPSTREAM_UNAVAILABLE) instead of an
errorsbody field. - A response with no data at all - every upstream failed, or the request was rejected before reaching one
(rate limit, payload too large, plugin failure, single-upstream gateway failure) - is now an
RFC 9457 Problem Details document (application/problem+json):
{"type": "about:blank", "title", "status", "detail"?, "errors": [...]}.typeis always the literal
"about:blank"- RFC 9457's own placeholder for "no further-specific type" - never a real URI, deliberately:
a dereferencable type link is reconnaissance, since it names the gateway software fronting the request and
invites probing for what that implies about the backend.errorsis the machine-readable discriminator instead
(always present, one entry per distinct underlyingClientError- more than one only for a multi-upstream
failure with several different causes);titleis a human summary of the highest-priority one and may change
wording over time.ClientResponse/ResponseMetaare removed from the Go API;WriteErrorand the
OpenAPI-generated schemas both moved toProblemDetails. - Request correlation is unaffected by this - it was already carried solely by the
X-Request-IDresponse
header, never duplicated into a body field.
- A full or partial (
- Breaking / security fix:
builtin/middlewares/auth's default realm - sent in theWWW-Authenticateheader on
every401the gateway itself returns - changed from"aastro"to"restricted". Scope note: this is about the
gateway's own runtime response to a client. OpenAPI export output and anything that only affects tracing/metrics
(e.g.gateway.service.name) are a separate concern, owned by whoever runs and publishes them - not touched here.
0.9.0
Added
policy.follow_redirects- per-upstream option controlling whether the gateway follows upstream redirects. Defaults
tofalse: a 3xx is now propagated to the client instead of being chased. Set it totrueto restore the previous
behavior, keeping in mind that the redirect is followed with the upstream's own TLS configuration and that Go strips
AuthorizationandCookieon cross-host hops.- New client error codes
UPSTREAM_CLIENT_ERRORandUPSTREAM_REDIRECT, distinguishing an upstreams answer to the
client from an upstream failure.
Changed
- Upstream responses are now classified by status class rather than the single
>= 500check. 5xx and transport
failures remain gateway failures (502, upstream body dropped); 3xx and 4xx are treated as answers addressed to the
client. - Single-upstream flows propagate the upstream status verbatim - a 404, 401, or 302 from the upstream now reaches the
client as a 404, 401, or 302. Aggregating flows are unaffected: a status is only propagated when every failed upstream
agreed on it, otherwise the response stays 502. allowed_statusesis now enforced as a contract in both directions: a status on the list is always a success (its
body flows intodata), and a status outside a non-empty list is a policy violation returning 502
UPSTREAM_MALFORMED, even for statuses that would otherwise be propagated.- Client errors and redirects no longer count as circuit breaker failures - a burst of 404s can no longer take a healthy
upstream out of rotation. - Responses with statuses that cannot carry a body (204, 304) are no longer wrapped in the JSON envelope.
- Upstream policy validation is skipped for transport-level failures, which previously produced misleading
empty body not allowedandstatus 0 not in allowed listmessages on timeouts.
Fixed
- A single-upstream flow returned 200 with the upstream's error body when the upstream answered 404, 401, or any
other non-2xx status outsideallowed_statuses. Clients received a success for a request that failed. - The circuit breaker reset itself with the very request it rejected: a denied call was recorded as a success, so an
open breaker closed after blocking a single request instead of staying open untilreset_timeout. - Retries never fired for upstreams without an explicit
method:. Retry eligibility was judged against the raw
configured method rather than the effective one, so the empty default was never considered idempotent. - Empty upstream bodies (204, or 200 with no content) were reported as
UPSTREAM_MALFORMEDduring aggregation, turning
a successful fan-out into a 206 partial response. - Aggregation error responses now carry the retried status correctly; an aborted read no longer produces a
redirect-shapederrorsentry for legitimately empty 3xx bodies.
0.8.0
Added
aastroctl openapi import— generate a gateway configuration from an OpenAPI 3.x document. Documents produced by openapi export --extensions are reconstructed losslessly: flows, aggregation, upstreams, policy, and transport are restored, with default-valued fields elided for a minimal, human-readable result. Foreign documents are scaffolded as single-upstream flows, with hosts taken from--default-hostorservers[]. Passthrough flows are detected from streamed / responses, and the rate limiter is inferred from 429 responses. Secrets are never restored: plugin/middleware configs and TLS certificate paths are reported as warnings for manual re-adding. The generated config is validated before output, so import never emits a config the gateway would reject.
0.7.0
Added
- Support for the HTTP QUERY method.
aastroctl openapi export— generate an OpenAPI 3.1 (or 3.0) document from a gateway configuration. Response statuses are derived from the actual config:206only for best-effort multi-upstream flows,409only underon_conflict: error,429only when the rate limiter is enabled. Flows guarded by the builtinauthmiddleware get abearerAuthsecurity scheme and a401response. Passthrough flows are modeled as streamed*/*responses. Optional--extensionsflag embedsx-aastrosnapshots for future config round-trip (middleware configs and secrets are never serialized). Deterministic output, git-friendly. The config is loaded through the same pipeline as the gateway, so the command doubles as a config validator.
Changed
- Client IP is now extracted only from trusted sources:
X-Forwarded-ForandX-Real-IPare honored only when the request comes from atrusted_proxiespeer, with right-to-left XFF parsing. Prevents rate limiter bypass and client IP spoofing via forged headers.
0.6.0
Added
-
Hot reload of TLS certificates. Aastro watches the directories of the configured
cert_file,
key_file, andca_filepaths and atomically swaps the in-memory material
when they change. New TLS handshakes use the new certificate; in-flight
connections are unaffected. No configuration change is required — rotation
works on the existing cert paths.Directory-level watching handles both atomic file replacement on a host
(write-to-temp-then-rename) and Kubernetes secret mounts, where the projected
files are updated via symlink swap rather than in-place writes. Certificate
rotation through cert-manager, Vault Agent, or SPIFFE/SPIRE sidecars is now
hands-off.Reloads are validated before they are applied: if a new certificate or CA
bundle on disk fails to parse, the error is logged and the previously loaded
material stays live, so a malformed rotation cannot take the listener down.
0.5.1
Fixed
- Retries:
retry_on_statusesis now honored for 5xx responses. Previously any
status ≥ 500 was coerced into an error and retried unconditionally, ignoring the
configured status list. 5xx and non-5xx are now governed by the same rule. - Retries: non-retryable failures (
internal,body_too_large,policy_violation,
circuit_open,canceled,read_error) are no longer retried — they previously
looped untilmax_retrieswas exhausted with no chance of a different outcome. - Upstream errors: response body and headers of 5xx responses are no longer
discarded. They are now read and preserved on the upstream response, so error
detail is available to logging and downstream handling.
Added
- Retries: idempotency guard. Status-based retries now apply only to idempotent
methods (GET, HEAD, OPTIONS, TRACE, PUT, DELETE). Non-idempotent requests (e.g. POST, PATCH)
are no longer replayed on a retryable status, preventing duplicate side effects.
Transport-failure retries are unaffected.
Changed
- Retries: retry decision logic consolidated into a single explicit rule.
Transport-level failures (timeout,connection) are always retryable;
status-based retries are driven solely byretry_on_statuses; everything else
is terminal. - Used goccy/go-json instead of stdlib encoding/json
0.5.0
Added
- New command-line interface and ctl for stub plugins initialization. For more information about the CLI, see the documentation.
Changed
- Project was renamed from Kono to Aastro
0.4.1
Changed
Changed final image to chainguard/wolfi-base
Fixed
Used UUID v7 instead of ULID for request id