aboutsummaryrefslogtreecommitdiffstats
path: root/relay/config.go
diff options
context:
space:
mode:
authorbndw <ben@bdw.to>2026-03-09 08:01:02 -0700
committerbndw <ben@bdw.to>2026-03-09 08:01:02 -0700
commit61a85baf87d89fcc09f9469a113a2ddc982b0a24 (patch)
treed8359ce5cbcbb9402ba92c617c4ebd702adf33e9 /relay/config.go
parentce684848e25fed3aabdde4ffba6d2d8c40afa030 (diff)
feat: phase 2 relay implementation
Implement the Axon relay as relay/ (module axon/relay). Includes: - WebSocket framing (RFC 6455, no external deps) in relay/websocket/ - Per-connection auth: challenge/response with ed25519 + allowlist check - Ingest pipeline: sig verify, dedup, ephemeral fanout, SQLite persistence - Subscription manager with prefix-matching filter fanout in relay/subscription/ - SQLite storage with WAL/cache config and UNION query builder in relay/storage/ - Graceful shutdown on SIGINT/SIGTERM - Filter/TagFilter types added to axon core package (required by relay)
Diffstat (limited to 'relay/config.go')
-rw-r--r--relay/config.go66
1 files changed, 66 insertions, 0 deletions
diff --git a/relay/config.go b/relay/config.go
new file mode 100644
index 0000000..e432b85
--- /dev/null
+++ b/relay/config.go
@@ -0,0 +1,66 @@
1package main
2
3import (
4 "encoding/hex"
5 "fmt"
6 "os"
7
8 "gopkg.in/yaml.v3"
9)
10
11// Config holds all relay configuration loaded from config.yaml.
12type Config struct {
13 Addr string `yaml:"addr"`
14 DB string `yaml:"db"`
15 RelayURL string `yaml:"relay_url"`
16 Allowlist []string `yaml:"allowlist"` // hex-encoded pubkeys
17}
18
19// DefaultConfig returns sensible defaults.
20func DefaultConfig() Config {
21 return Config{
22 Addr: ":8080",
23 DB: "axon.db",
24 RelayURL: "ws://localhost:8080",
25 }
26}
27
28// AllowlistBytes decodes the hex pubkeys in c.Allowlist and returns them as
29// raw byte slices. Returns an error if any entry is not valid 64-char hex.
30func (c *Config) AllowlistBytes() ([][]byte, error) {
31 out := make([][]byte, 0, len(c.Allowlist))
32 for _, h := range c.Allowlist {
33 b, err := hex.DecodeString(h)
34 if err != nil {
35 return nil, fmt.Errorf("config: allowlist entry %q is not valid hex: %w", h, err)
36 }
37 if len(b) != 32 {
38 return nil, fmt.Errorf("config: allowlist entry %q decoded to %d bytes, want 32", h, len(b))
39 }
40 out = append(out, b)
41 }
42 return out, nil
43}
44
45// LoadConfig reads and parses a YAML config file. Missing fields fall back to
46// DefaultConfig values.
47func LoadConfig(path string) (Config, error) {
48 cfg := DefaultConfig()
49
50 f, err := os.Open(path)
51 if err != nil {
52 if os.IsNotExist(err) {
53 // No config file — use defaults.
54 return cfg, nil
55 }
56 return cfg, fmt.Errorf("config: open %q: %w", path, err)
57 }
58 defer f.Close()
59
60 dec := yaml.NewDecoder(f)
61 dec.KnownFields(true)
62 if err := dec.Decode(&cfg); err != nil {
63 return cfg, fmt.Errorf("config: decode %q: %w", path, err)
64 }
65 return cfg, nil
66}