mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-09 16:00:59 +00:00
These two env vars were documented as relay overrides in v6 docs since
March 18 (CONFIGURATION.md, RELAY.md, and the frontend-served doc copy)
but no code ever read them. Operators trying to bootstrap relay headlessly
saw no effect.
Implement them rather than remove the documentation. Headless and
container deployments now have a real path to enable relay and point it
at a private endpoint without going through Settings → Relay.
internal/relay/config_env.go:
- ApplyEnvOverrides(*Config) mutates relay.Config in place.
- PULSE_RELAY_ENABLED accepts true/false/yes/no/1/0/on/off (case-
insensitive). Unrecognized values log a warning and leave the file
value untouched — important so "unset" reads differently from
"explicit false."
- PULSE_RELAY_SERVER goes through the existing validateRelayServerURL
check; invalid URLs log a warning and fall through.
internal/config/persistence_relay.go:
LoadRelayConfig calls ApplyEnvOverrides after the file load and after
the default-fallback when relay.enc is absent, so the env override
applies on every load.
Tests cover unset / true / false / garbage-bool / valid-URL / invalid-URL
/ both-together / nil-config paths in the relay package, plus two
end-to-end tests in internal/config that prove the override flows through
LoadRelayConfig against a real persisted file and against the
missing-file default branch.
Restore the env-var docs with the correct default URL (the full
wss://relay.pulserelay.pro/ws/instance, not the bare hostname the
original aspirational table claimed) and add an explicit precedence note:
saving from the UI after an env override persists the env-effective state
to disk, so clearing the env alone does not revert.
Add internal/relay/config_env_test.go to the relay-runtime registry's
desktop-relay-runtime exact_files so the new code surface is proof-tracked.
Update the matching pin in subsystem_lookup_test.py. Extend the
relay-runtime contract Extension Point 3 to document the override
semantics LoadRelayConfig must satisfy.
117 lines
3.5 KiB
Go
117 lines
3.5 KiB
Go
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/relay"
|
|
)
|
|
|
|
func TestConfigPersistenceLoadRelayConfigMissingFileReturnsDefault(t *testing.T) {
|
|
cp := NewConfigPersistence(t.TempDir())
|
|
|
|
cfg, err := cp.LoadRelayConfig()
|
|
if err != nil {
|
|
t.Fatalf("LoadRelayConfig() error = %v", err)
|
|
}
|
|
if cfg == nil {
|
|
t.Fatal("LoadRelayConfig() returned nil config")
|
|
}
|
|
if cfg.Enabled {
|
|
t.Fatalf("LoadRelayConfig() enabled = true, want false")
|
|
}
|
|
if cfg.ServerURL != relay.DefaultServerURL {
|
|
t.Fatalf("LoadRelayConfig() server_url = %q, want %q", cfg.ServerURL, relay.DefaultServerURL)
|
|
}
|
|
}
|
|
|
|
func TestConfigPersistenceLoadRelayConfigMigratesPlaintextFile(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cp := NewConfigPersistence(tempDir)
|
|
|
|
plaintext := &relay.Config{
|
|
Enabled: true,
|
|
ServerURL: "wss://relay.example.com/ws/instance",
|
|
InstanceSecret: "relay-instance-secret",
|
|
IdentityPrivateKey: "private-key",
|
|
IdentityPublicKey: "public-key",
|
|
IdentityFingerprint: "fingerprint",
|
|
}
|
|
raw, err := json.Marshal(plaintext)
|
|
if err != nil {
|
|
t.Fatalf("marshal plaintext relay config: %v", err)
|
|
}
|
|
|
|
filePath := filepath.Join(tempDir, "relay.enc")
|
|
if err := os.WriteFile(filePath, raw, 0o600); err != nil {
|
|
t.Fatalf("write plaintext relay config: %v", err)
|
|
}
|
|
|
|
cfg, err := cp.LoadRelayConfig()
|
|
if err != nil {
|
|
t.Fatalf("LoadRelayConfig() error = %v", err)
|
|
}
|
|
if cfg == nil {
|
|
t.Fatal("LoadRelayConfig() returned nil config")
|
|
}
|
|
if cfg.InstanceSecret != plaintext.InstanceSecret {
|
|
t.Fatalf("LoadRelayConfig() instance_secret = %q, want %q", cfg.InstanceSecret, plaintext.InstanceSecret)
|
|
}
|
|
if cfg.IdentityPrivateKey != plaintext.IdentityPrivateKey {
|
|
t.Fatalf("LoadRelayConfig() identity_private_key = %q, want %q", cfg.IdentityPrivateKey, plaintext.IdentityPrivateKey)
|
|
}
|
|
|
|
rewritten, err := os.ReadFile(filePath)
|
|
if err != nil {
|
|
t.Fatalf("read rewritten relay config: %v", err)
|
|
}
|
|
if bytes.Equal(rewritten, raw) {
|
|
t.Fatal("expected plaintext relay config to be rewritten encrypted")
|
|
}
|
|
}
|
|
|
|
func TestConfigPersistenceLoadRelayConfigAppliesEnvOverrideOverFile(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cp := NewConfigPersistence(tempDir)
|
|
|
|
// File says disabled with the default URL.
|
|
if err := cp.SaveRelayConfig(*relay.DefaultConfig()); err != nil {
|
|
t.Fatalf("SaveRelayConfig() error = %v", err)
|
|
}
|
|
|
|
// Env says enable + redirect to a private endpoint.
|
|
t.Setenv(relay.EnvRelayEnabled, "true")
|
|
t.Setenv(relay.EnvRelayServerURL, "wss://relay.internal.example/ws/instance")
|
|
|
|
cfg, err := cp.LoadRelayConfig()
|
|
if err != nil {
|
|
t.Fatalf("LoadRelayConfig() error = %v", err)
|
|
}
|
|
if !cfg.Enabled {
|
|
t.Fatalf("LoadRelayConfig() enabled = false, want true (env override should beat file)")
|
|
}
|
|
if cfg.ServerURL != "wss://relay.internal.example/ws/instance" {
|
|
t.Fatalf("LoadRelayConfig() server_url = %q, want env override", cfg.ServerURL)
|
|
}
|
|
}
|
|
|
|
func TestConfigPersistenceLoadRelayConfigAppliesEnvOverrideWithNoFile(t *testing.T) {
|
|
cp := NewConfigPersistence(t.TempDir())
|
|
|
|
t.Setenv(relay.EnvRelayEnabled, "yes")
|
|
t.Setenv(relay.EnvRelayServerURL, "wss://relay.internal.example/ws/instance")
|
|
|
|
cfg, err := cp.LoadRelayConfig()
|
|
if err != nil {
|
|
t.Fatalf("LoadRelayConfig() error = %v", err)
|
|
}
|
|
if !cfg.Enabled {
|
|
t.Fatalf("LoadRelayConfig() enabled = false, want true (env override on default-when-missing)")
|
|
}
|
|
if cfg.ServerURL != "wss://relay.internal.example/ws/instance" {
|
|
t.Fatalf("LoadRelayConfig() server_url = %q, want env override", cfg.ServerURL)
|
|
}
|
|
}
|