From b9d60e9f856eb53a1ff8a5a9c5de9f4112684302 Mon Sep 17 00:00:00 2001 From: dawn <90008@klbr.net> Date: Wed, 5 Aug 2026 18:24:13 +0300 Subject: [PATCH] [config] reject ephemeral backlinks builds backlinks hooks are compiled into every record mutation and enable_backlinks does not disable them at runtime; without its own retention machinery an ephemeral window would leak stale backlink entries. validate() now rejects HYDRANT_EPHEMERAL when the backlinks feature is compiled in, and relay mode rejects only_index_links. issue: hydrant-die --- src/config.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/config.rs b/src/config.rs index d320e37..8b6d7a3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -434,6 +434,16 @@ impl Config { )); } + // backlinks hooks are compiled directly into every record mutation; + // `enable_backlinks` does not disable them at runtime. without its own + // retention machinery an ephemeral window would leak stale entries. + #[cfg(feature = "backlinks")] + if self.ephemeral { + return Err(miette::miette!( + "the backlinks feature is not supported with HYDRANT_EPHEMERAL / ephemeral" + )); + } + #[cfg(feature = "relay")] if self.only_index_links { return Err(miette::miette!( @@ -464,6 +474,33 @@ mod tests { assert!(config.validate().is_ok()); } + #[test] + #[cfg(feature = "backlinks")] + fn ephemeral_rejected_when_backlinks_feature_enabled() { + let mut config = Config::default(); + config.ephemeral = true; + config.enable_backlinks = false; + let res = config.validate(); + assert!(res.is_err()); + let err = res.unwrap_err().to_string(); + assert!(err.contains("HYDRANT_EPHEMERAL") || err.contains("ephemeral")); + assert!(err.contains("backlinks feature")); + + config.enable_backlinks = true; + assert!(config.validate().is_err()); + + config.ephemeral = false; + assert!(config.validate().is_ok()); + } + + #[test] + #[cfg(all(feature = "indexer_stream", not(feature = "backlinks")))] + fn ephemeral_allowed_when_backlinks_feature_disabled() { + let mut config = Config::default(); + config.ephemeral = true; + assert!(config.validate().is_ok()); + } + #[test] #[cfg(all(feature = "indexer", not(feature = "indexer_stream")))] fn ephemeral_requires_stream_storage() { -- 2.51.2