Releases: starwalkn/aastro
Release list
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
0.4.0
⚠️ Breaking Changes
The configuration schema has been restructured to separate concerns by listener and
responsibility. Existing 0.3.x configs will fail to load with a clear validation error
pointing at the missing sections.
Admin endpoints moved to a top-level admin section. Previously server.admin_port,
server.admin_bind_addr, and server.pprof lived inside server. They are now grouped
under their own section, since admin is a separate listener with separate semantics
(never TLS-terminated, bound to localhost by default, distinct timeouts).
# Before (0.3.x)
gateway:
server:
port: 7805
admin_port: 9090
admin_bind_addr: 127.0.0.1
pprof:
enabled: true
# After (0.4.0)
gateway:
server:
port: 7805
admin:
port: 9090
bind_addr: 127.0.0.1
enable_pprof: trueObservability moved to a top-level observability section. metrics and tracing
were previously nested under server. They are not server concerns - metrics is either
scraped from the admin port (Prometheus exporter) or pushed to OTLP, and tracing is
always push-only.
# Before (0.3.x)
gateway:
server:
metrics: { ... }
tracing: { ... }
# After (0.4.0)
gateway:
observability:
metrics: { ... }
tracing: { ... }pprof no longer has its own port. Previously pprof.port opened a separate listener.
pprof endpoints now live on the admin port under /debug/pprof/, controlled by
admin.enable_pprof. One fewer port to manage and to expose through network policy.
Flow field max_parallel_upstreams renamed to parallel_upstreams. Aligns docs
with actual behaviour and the code field name. Same semantics, same default
(2 × NumCPU).
Added
mTLS support, end-to-end. Both the data port (inbound mTLS) and individual upstreams
(outbound mTLS) can now be configured with client certificate authentication, custom CA
bundles, configurable minimum TLS version (1.2 or 1.3), and SNI override.
gateway:
server:
tls:
enabled: true
cert_file: /etc/kono/server.crt
key_file: /etc/kono/server.key
client_auth: require # none | optional | require
client_ca_file: /etc/kono/client-ca.crt
min_version: "1.2"
routing:
flows:
- upstreams:
- tls:
enabled: true
cert_file: /etc/kono/clients/users.crt
key_file: /etc/kono/clients/users.key
ca_file: /etc/kono/internal-ca.crt
server_name: user-service.internalLiveness and readiness probes. New /__ready endpoint on the admin port returns
200 while the gateway is accepting traffic and 503 once graceful shutdown begins.
This lets Kubernetes (or any orchestrator with readiness probes) remove the pod from
service endpoints before the data port stops accepting connections, enabling true
zero-downtime deploys. /__health continues to return 200 while the process is alive
and never checks dependencies.
Configurable admin timeout. New admin.timeout field (default 5m) controls
read/write timeout on the admin port. Replaces a previously hard-coded constant. The
generous default exists to accommodate long pprof captures (/debug/pprof/profile,
/debug/pprof/trace); production data-port timeouts remain short.
Configurable header timeouts. New server.header_timeout and admin.header_timeout
fields (default 5s each) set http.Server.ReadHeaderTimeout on the respective listeners.
Defends against Slowloris-style attacks where a client trickles request headers slowly
to exhaust the server.
Structured TLS handshake logging. http.Server.ErrorLog is now wired through the
gateway's zap logger on both data and admin listeners. TLS handshake failures (failed
client certificate verification, version mismatch, unsupported cipher) now appear in
the same structured log stream as the rest of the application instead of escaping to
stderr via the standard logger.
Changed
Admin listener binds to 127.0.0.1 by default. Previously bound to all interfaces.
The admin port carries diagnostic endpoints (/__health, /__ready, /metrics,
/debug/pprof/) that should not be exposed externally. Set admin.bind_addr: 0.0.0.0
explicitly if Prometheus scrapes from outside the pod network.
/metrics endpoint moved to the admin port. When metrics.exporter: prometheus,
the endpoint is now served on admin.port rather than the data port. This means
Prometheus can scrape Kono over plain HTTP without needing a client certificate, even
when the data port enforces mTLS.
Health probe response format changed. /__health now returns
{"status": "ok"} with Content-Type: application/json instead of plain text OK.
Consistent with the rest of the gateway's response format.
TLS 1.0 and 1.1 are not selectable. min_version accepts only "1.2" or "1.3".
RFC 8996 deprecated 1.0 and 1.1 in 2021, and they are disabled in modern clients
regardless of server configuration.
Migration Notes
For most users, the migration is mechanical: move admin_port, admin_bind_addr, and
pprof from server into a new top-level admin section, move metrics and tracing
from server into a new observability section. If you relied on metrics being on
the data port - point Prometheus at the admin port instead.
If you ran pprof on a separate port: that port can be closed, pprof now lives on the
admin port. Update any internal documentation or scrape configs.
0.3.1
Added
- Add
WWW-Authenticateheader to auth middleware
Changed
- Golang build version ldflags will now automatically come from the docker meta step outputs
- Sliding window rate limiter instead of fixed-window
Fixed
- Added missing masker plugin and cors middleware to image
Thanks to @na1tto for improving the auth middleware!