Releases: HeliosDatabase/HeliosDB-Proxy
Releases · HeliosDatabase/HeliosDB-Proxy
Release list
v1.7.0
Full Changelog: v1.6.1...v1.7.0
v1.6.1
What's Changed
Full Changelog: v1.6.0...v1.6.1
v1.6.0
What's Changed
- ci: bump Node-20-deprecated actions to Node-24 majors by @danimoya in #39
- ci: split always-red Integration job (integrate-pg required + integrate-nano non-blocking) by @danimoya in #38
Full Changelog: v1.5.0...v1.6.0
v1.5.0
Minor release — interface coverage ([limits]/[anomaly]/[plugins].kv_* config), plugin-KV admin endpoints, PG-wire protocol benchmarks, and security hardening (stored-XSS fix in the admin dashboard). Feature-additive with one default-behavior change (plugin kv_set is now bounded — see Changed).
Added
[limits]config section — eleven operational safety bounds (ten
formerly-named consts plus the previously-inline 30s backend read timeout)
that were hardcoded insrc/server.rsare now tunable viaproxy.toml. Each
default reproduces the prior constant exactly, resolved once at startup, so a
config without a[limits]block is byte-for-byte unchanged. Keys (default):
max_cancel_keys(100000),startup_timeout_secs(30),
backend_write_timeout_secs(30),backend_read_timeout_secs(30),
client_write_timeout_secs(60),reprepare_timeout_secs(15),
max_prepared_statements(8192),max_prepared_bytes(67108864 / 64 MiB),
max_pending_bytes(67108864 / 64 MiB),
max_total_idle_backend_conns(8192, pool-modes), and
pool_reap_interval_secs(30).validate()rejects a0for any of these
(a safety bound, not "unbounded") with a key-named error, and caps every
*_secstimeout at one year (31536000) — a value above that would overflow
the connect-timeInstant + Durationdeadline and panic the per-connection
task, so it is refused up front.[anomaly]config section — the in-process anomaly detector previously
ran on a hardcodedAnomalyConfig::default()plus aMAX_SEEN_FINGERPRINTS
module const with no way to tune it. Its eight tunables are now exposed via
proxy.toml, defaults reproducing the prior behavior exactly:rate_window_secs
(60),spike_z_threshold(3.0),auth_window_secs(60),auth_critical_count
(10),auth_warning_count(5),event_buffer_size(1024),emit_novel_queries
(true), andmax_seen_fingerprints(100000).validate()rejects degenerate
values (windows/buffer/fingerprint-cap> 0,spike_z_thresholdfinite and
> 0,auth_critical_count >= 1,auth_warning_count <= auth_critical_count).
The detector is built once at startup, so changing[anomaly]requires a
restart (a SIGHUP reload does not rebuild it)./admin/kv/<plugin>/<key>admin endpoints — the per-plugin KV store
(KvBackend, read by plugins through theirkv_get/kv_sethost imports)
can now be read, written, listed, and deleted from outside the WASM sandbox,
so operators can push a plugin's runtime config (budgets, region maps, mask
rules, allowlists) without a restart.GET /admin/kv/<plugin>/<key>returns
{"plugin","key","value"}(404 if absent),GET /admin/kv/<plugin>/
(trailing slash) lists the namespace as{"plugin","keys":[...]},
PUTsets a value (UTF-8 body viafrom_utf8_lossy), andDELETEremoves one
(idempotent 200). A trailing-slash list accepts an optional?prefix=filter;
any query string is stripped before the plugin/key split, so?…never leaks
into a stored key, and an empty<plugin>segment (/admin/kv//<key>) is
rejected400. All four sit behind the normal admin bearer gate; the build
returns501without--features wasm-pluginsand503when no plugin
manager is attached. Four[plugins]caps bound writes and are tunable
(0= unlimited):kv_max_value_bytes(default 65536, now bounds a single
key OR value),kv_max_keys_per_plugin(default 1024),kv_max_plugins
(default 256, bounds how many<plugin>namespaces can exist so a token-holder
cannot exhaust memory by writing to unboundedly-many namespace names), and
kv_max_total_bytes(default 67108864 / 64 MiB) — a total-footprint backstop
that sums each entry's key + value bytes plus each live namespace's name bytes
and keeps the whole store within a survivable ceiling regardless of the
per-axis product (which could otherwise retain tens of GiB). A PUT
past a cap returns413(and the in-WASMkv_setreturns-1); an oversized
body is rejected before it is copied. Overwriting an existing key never trips
the key-count cap, writing to an existing namespace never trips the namespace
cap, and deleting a namespace's last key frees its slot (the reclaimed bytes
are subtracted from the total-footprint counter too).
Keys must not contain?: a query string is stripped before the plugin/key
split (so?prefix=can filter a listing), which means a plugin-created key
containing?is listable but not addressable via GET/DELETE over the admin
surface.benches/protocol.rs— a Criterion benchmark covering the PG-wire
per-query hot path that every client frame and backend response flows
through, previously uncovered by the pool/routing benches (so a regression
there was invisible to quality gate 3). Three groups —
protocol/decode_message,protocol/encode_message, and
protocol/query_text— each run over three payload sizes (a trivial
SELECT 1, a ~60-charWHEREquery, and a deterministically-built ~1 KiB
IN (...)statement) withThroughput::Bytesso a regression surfaces as
both a per-call delta and a bytes/sec change. Feature-free: it exercises only
the always-publicprotocolAPI, so it compiles under every feature set.
Changed
- Plugin
kv_setis now bounded (was unbounded in 1.4.0). The per-plugin KV
store went from an infallible unbounded write to a capped one. The in-WASM
kv_setimport keeps itsi32ABI —0on success,-1when the write is
refused (a cap breach now joins the internal-error case in returning-1);
the internal storeset()method that backs it changed from returning()to
returning abool(false= refused). The new[plugins]caps default to
kv_max_value_bytes65536,
kv_max_keys_per_plugin1024,kv_max_plugins256, andkv_max_total_bytes
67108864 (64 MiB). Upgrade impact: a plugin deployed under 1.4.0 that
stored values larger than 64 KiB, or more than 1024 keys in its namespace,
will silently start receiving-1fromkv_setafter upgrade — writes past a
cap fail instead of succeeding. Setting anykv_*cap to0disables that
cap and restores the unbounded 1.4.0 behavior for that axis (set all four to
0for byte-for-byte 1.4.0 semantics). - CI now lints test code (
cargo clippy --tests) — both clippy invocations
in.github/workflows/ci.ymlgained--tests, so#[cfg(test)]modules and
thetests/integration crate are held to the same-D warningsbar as the
library. Pre-existingclippy::field_reassign_with_defaultwarnings in test
code (convertedlet mut x = T::default(); x.field = …;sequences into
struct-literalT { field: …, ..Default::default() }initializers, behavior
unchanged) were cleared so the gate starts green. - Rewrote
docs/transaction-replay.mdanddocs/topology-providers.mdagainst
the current code. Both documents were conceptually dated. The TR deep dive is now
grounded insrc/transaction_journal.rs,src/failover_replay.rs,
src/failover_controller.rs,src/switchover_buffer.rs,src/replay/mod.rs, and the
tr_enabled/tr_mode/write_timeout_secskeys — correcting invented keys
(tr_max_journal_bytes,switchover_drain_timeout_secsnever existed), the
write_timeout_secsdefault (30, not 15), the defaulttr_mode(session), the
text-format (not binary) replay parameter path, and the fact that session-state/cursor
migration exists only as unwired library modules (src/cursor_restore.rs,
src/session_migrate.rs) not reachable from the replay path, while
FailoverController/PrimaryTrackerare library components rather than
daemon-wired. The topology doc now separates the daemon's
static-role-plus-health primary tracking (surfaced at/topology) from the
TopologyProviderlibrary abstraction, and notes the PostgreSQL provider is constructed
programmatically (not from[[nodes]]). Each doc carries a "last verified against"
commit line.
Fixed
/healthz,/livez,/readyzadmin routes — these three
Kubernetes-style probe paths were already token-exempt in the admin auth gate
but had no handler, so they fell through to the catch-all and returned404.
They now route to the same handlers as their slash-form twins (/healthz→
/health,/livez→/health/live,/readyz→/health/ready), returning
byte-for-byte identical responses. Because they are token-exempt, orchestrators
can use/livezand/readyzfor unauthenticated liveness/readiness probes
even whenadmin_tokenis set (the slash-form/health/liveand
/health/readyremain token-gated, unchanged).- Embedded admin dashboard usable with
admin_tokenset — v1.4.0 made
token-gating the recommended posture, but the embedded web UI
(src/admin_ui.html, served at/and/ui) sent noAuthorization
header on any of itsfetch()calls, so with a token set every panel
401ed — the secure configuration broke the dashboard. The UI now wraps
window.fetchonce to injectAuthorization: Bearer <token>(from the tab's
sessionStorage, keyhelios_admin_token) into every request; on a401
it prompts once per page load for the token and reloads. A token button
in the header bar clears the saved token so a wrong one can be re-entered.
The static shell (GET /,/ui) is now token-exempt so the page can load
and prompt — it carries no privileged data, and every API call it makes is
still individually gated. Without a token, behavior is unchanged (no prompt).
Security
- Stored XSS in the embedded admin dashboard (present in 1.4.0 — prioritize
this upgrade). The admin web UI (src/admin_ui.html, served at/and
/ui) interpolated backend- and attacker-derived strings intoinnerHTML
with only a partial<-escape. A crafted SQL query whose text flowed into an
anomaly `f...
v1.4.0
What's Changed
- perf(hotpath): eliminate per-query allocations + protocol crash guards (G1/M1) by @danimoya in #26
- fix(pool): COPY-hang, poisoned-park, and pool-key identity leakage (G2/M2) by @danimoya in #27
- fix(stability): admin DoS caps, health-interval guard, pre-auth timeout (G4/M4) by @danimoya in #28
- fix(auth): event-driven auth relay — slow-client deadlock + ErrorResponse blindness (G3/M3) by @danimoya in #29
- fix(mem): bound the ha-tr journal and reclaim per-session L1 caches (G5/M5) by @danimoya in #30
- perf(pool): conditional reset — skip DISCARD ALL for provably-clean connections (G2c) by @danimoya in #31
- fix(relay): idle backend watch — LISTEN/NOTIFY delivery + kill the 200ms Flush stall (G3b) by @danimoya in #32
- fix(stability): session RAII guard + DashMap sessions, health-loop supervision (G4b) by @danimoya in #33
- fix(security): HTTP/MCP/GraphQL gateway request hardening (G-A1) by @danimoya in #34
- fix(security): refuse anonymous non-loopback admin; cap admin connections (G-A2) by @danimoya in #35
- feat(edge-proxy): two-region result cache with SSE invalidation push by @danimoya in #36
- docs(readme): edge-proxy demo GIF + asciinema link by @danimoya in #37
Full Changelog: v1.3.1...v1.4.0
v1.3.1
What's Changed
- Platform-tier wiring + reliability hardening: all 17 feature flags real (0.7.0 → 1.3.0) by @danimoya in #24
- fix(admin): query-analytics feature compiles standalone (parse_limit_query gating) by @danimoya in #25
Full Changelog: v1.3.0...v1.3.1
v1.3.0
v0.6.1
Full Changelog: v0.6.0...v0.6.1
Full Changelog: v0.6.0...v0.6.1