mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-06-16 22:54:12 +00:00
fix(gateway): plumb ntfy_base_url into gateway config so push fan-out activates (#858)
The ntfy fan-out (publish each push to every active push node so a round-robin-DNS-pinned subscriber receives it) was coded but INERT: the gateway's cfg.NtfyBaseURL was never populated, so the fan-out resolver was never built and pushes went single-host (the ~87% loss the bug describes). The orchestrator already derives https://push.<dnsZone> for the ntfy server + Caddy reverse-proxy but never put it in node.yaml's http_gateway. Same regression class as the v0.122.42 secrets_encryption_key fix (consumer landed; template + parse field + node->gateway mapping were missed). Plumb it through all four layers: render it under http_gateway (derived as push.<dnsZone>, matching the ntfy host), parse it in HTTPGatewayConfig, map it onto gateway.Config. Rolling-upgrade safe: Phase 4 regen runs under the new binary (post-swap), so an old binary never reads a node.yaml with the new field. DecodeStrict regression guard added (mirrors secrets_encryption_key).
This commit is contained in:
parent
3779ba9502
commit
e3b2f08a0a
@ -235,6 +235,29 @@ http_gateway:
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecodeStrict_ntfyBaseURL guards the same v0.122.42-class boot crash for
|
||||
// the bugboard #858 ntfy fan-out: Phase 4 now emits `ntfy_base_url` under
|
||||
// http_gateway, so HTTPGatewayConfig MUST carry a matching field or
|
||||
// KnownFields(true) rejects the whole node.yaml and orama-node crash-loops.
|
||||
// If someone deletes the parse field, the render tests still pass but
|
||||
// production crash-loops — this guard catches that.
|
||||
func TestDecodeStrict_ntfyBaseURL(t *testing.T) {
|
||||
yamlInput := `
|
||||
node:
|
||||
id: "test-node"
|
||||
http_gateway:
|
||||
enabled: true
|
||||
ntfy_base_url: "https://push.dbrs.space"
|
||||
`
|
||||
var cfg Config
|
||||
if err := DecodeStrict(strings.NewReader(yamlInput), &cfg); err != nil {
|
||||
t.Fatalf("node.yaml with ntfy_base_url must parse (bugboard #858): %v", err)
|
||||
}
|
||||
if cfg.HTTPGateway.NtfyBaseURL != "https://push.dbrs.space" {
|
||||
t.Errorf("NtfyBaseURL = %q, want https://push.dbrs.space", cfg.HTTPGateway.NtfyBaseURL)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecodeStrict_sniRouterBlock guards against a recurrence of the
|
||||
// v0.122.42-class boot crash for the feat-124 stealth SNI router: Phase 4
|
||||
// always emits a top-level `sni_router:` block into node.yaml, so the root
|
||||
|
||||
@ -30,6 +30,16 @@ type HTTPGatewayConfig struct {
|
||||
// and the node→gateway mapping were missed).
|
||||
SecretsEncryptionKey string `yaml:"secrets_encryption_key"`
|
||||
|
||||
// NtfyBaseURL is the shared self-hosted ntfy base URL (e.g.
|
||||
// "https://push.orama-devnet.network"). When set, the push ntfy provider
|
||||
// fans each publish out to every active push node so a subscriber pinned
|
||||
// to any instance by round-robin DNS receives it (bugboard #858). Rendered
|
||||
// under http_gateway by Phase 4 config generation as "https://push."+dnsZone
|
||||
// — matching the ntfy server + Caddy reverse-proxy host. Empty → no fan-out
|
||||
// (single-host delivery, the ~87% loss the fix exists to remove). MUST exist
|
||||
// here or the node→gateway mapping cannot populate gateway.Config.NtfyBaseURL.
|
||||
NtfyBaseURL string `yaml:"ntfy_base_url"`
|
||||
|
||||
// WebRTC configuration (optional, enabled per-namespace)
|
||||
WebRTC WebRTCConfig `yaml:"webrtc"`
|
||||
}
|
||||
|
||||
@ -220,6 +220,18 @@ func (cg *ConfigGenerator) GenerateNodeConfig(peerAddresses []string, vpsIP stri
|
||||
data.SecretsEncryptionKey = strings.TrimSpace(string(keyBytes))
|
||||
}
|
||||
|
||||
// Shared self-hosted ntfy base URL (bugboard #858). Derive it the SAME way
|
||||
// the orchestrator derives the ntfy server + Caddy reverse-proxy host
|
||||
// (push.<dnsZone>, dnsZone = baseDomain or the node domain), so the gateway's
|
||||
// NtfyBaseURL matches and the push provider fans each publish out to every
|
||||
// active push node instead of single-host delivery. Without this the fan-out
|
||||
// code is inert and ~87% of publishes never reach a pinned subscriber.
|
||||
if dnsZone := baseDomain; dnsZone != "" {
|
||||
data.NtfyBaseURL = "https://push." + dnsZone
|
||||
} else if domain != "" {
|
||||
data.NtfyBaseURL = "https://push." + domain
|
||||
}
|
||||
|
||||
// WebRTC/TURN config (feat-124 #913). The TURN secret lives in the secrets
|
||||
// dir so it survives Phase4 config regeneration; turn_domain/sfu_port/enabled
|
||||
// are operator-set values that only exist in the previous node.yaml, so we
|
||||
|
||||
37
core/pkg/environments/production/config_ntfy_test.go
Normal file
37
core/pkg/environments/production/config_ntfy_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package production
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Bugboard #858 — the ntfy fan-out only activates when the gateway config
|
||||
// carries NtfyBaseURL. The fan-out consumer (dependencies.go) shipped, but the
|
||||
// template + parse field + node→gateway mapping were missed, so cfg.NtfyBaseURL
|
||||
// stayed empty and every publish went single-host (~87% loss). These pin that
|
||||
// the generated node.yaml now renders ntfy_base_url under http_gateway, derived
|
||||
// as push.<dnsZone> to match the ntfy server + Caddy reverse-proxy host.
|
||||
|
||||
func TestGenerateNodeConfig_rendersNtfyBaseURL_fromBaseDomain(t *testing.T) {
|
||||
cg := NewConfigGenerator(t.TempDir())
|
||||
out, err := cg.GenerateNodeConfig(nil, "10.0.0.5", "", "node-1.dbrs.space", "dbrs.space", false)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateNodeConfig failed: %v", err)
|
||||
}
|
||||
if !strings.Contains(out, `ntfy_base_url: "https://push.dbrs.space"`) {
|
||||
t.Errorf("node.yaml missing ntfy_base_url derived from base domain\n---\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateNodeConfig_ntfyBaseURL_fallsBackToDomain(t *testing.T) {
|
||||
// No base domain → derive from the node domain (matches the orchestrator's
|
||||
// dnsZone := baseDomain; if empty -> domain).
|
||||
cg := NewConfigGenerator(t.TempDir())
|
||||
out, err := cg.GenerateNodeConfig(nil, "10.0.0.5", "", "anchor.example.net", "", false)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateNodeConfig failed: %v", err)
|
||||
}
|
||||
if !strings.Contains(out, `ntfy_base_url: "https://push.anchor.example.net"`) {
|
||||
t.Errorf("node.yaml missing ntfy_base_url fallback to node domain\n---\n%s", out)
|
||||
}
|
||||
}
|
||||
@ -102,6 +102,14 @@ http_gateway:
|
||||
# (bugboard #837). Sourced from ~/.orama/secrets/secrets-encryption-key.
|
||||
secrets_encryption_key: "{{.SecretsEncryptionKey}}"
|
||||
{{- end}}
|
||||
{{- if .NtfyBaseURL}}
|
||||
# Shared self-hosted ntfy base URL (bugboard #858). Same push.<dnsZone> host
|
||||
# the ntfy server + Caddy reverse-proxy use, so the gateway's push provider
|
||||
# fans each publish out to every active push node (each node runs an
|
||||
# independent ntfy with no shared store; without fan-out a subscriber pinned
|
||||
# to a different instance than the publish lands on misses it ~87% of the time).
|
||||
ntfy_base_url: "{{.NtfyBaseURL}}"
|
||||
{{- end}}
|
||||
{{- if .TURNSecret}}
|
||||
# WebRTC/TURN config (feat-124 #913). turn_secret is sourced from
|
||||
# ~/.orama/secrets/turn-secret so it survives config regeneration;
|
||||
|
||||
@ -56,6 +56,12 @@ type NodeConfigData struct {
|
||||
// stays disabled until the key is configured).
|
||||
SecretsEncryptionKey string
|
||||
|
||||
// NtfyBaseURL is the shared self-hosted ntfy base URL (e.g.
|
||||
// "https://push.<dnsZone>"), rendered under http_gateway as ntfy_base_url.
|
||||
// When set, the gateway's push provider fans each ntfy publish out to every
|
||||
// active push node (bugboard #858). Empty → omitted (single-host delivery).
|
||||
NtfyBaseURL string
|
||||
|
||||
// WebRTC/TURN configuration, rendered under http_gateway.webrtc when
|
||||
// WebRTCEnabled is true (feat-124 #913). TURNSecret is sourced from
|
||||
// ~/.orama/secrets/turn-secret so it survives Phase4 config regeneration;
|
||||
|
||||
@ -85,6 +85,7 @@ func (n *Node) startHTTPGateway(ctx context.Context) error {
|
||||
ClusterSecret: clusterSecret,
|
||||
APIKeyHMACSecret: apiKeyHMACSecret,
|
||||
SecretsEncryptionKey: secretsEncryptionKey,
|
||||
NtfyBaseURL: n.config.HTTPGateway.NtfyBaseURL,
|
||||
WebRTCEnabled: n.config.HTTPGateway.WebRTC.Enabled,
|
||||
SFUPort: n.config.HTTPGateway.WebRTC.SFUPort,
|
||||
TURNDomain: n.config.HTTPGateway.WebRTC.TURNDomain,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user