Tweak config format

This commit is contained in:
Jon Staab
2025-09-29 12:07:29 -07:00
parent 7eb6d22362
commit 001771cb6c
6 changed files with 54 additions and 32 deletions
+7 -9
View File
@@ -12,21 +12,19 @@ Zooid supports a few environment variables, which configure shared resources lik
- `PORT` - the port the server will listen on for all requests. Defaults to `3334`. - `PORT` - the port the server will listen on for all requests. Defaults to `3334`.
- `DATA` - the location of the directory for storing database files and media. Defaults to `./data`. - `DATA` - the location of the directory for storing database files and media. Defaults to `./data`.
- `CONF` - the location of the directory for storing relay configuration files. Defaults to `./conf`.
## Configuration ## Configuration
Configuration files are written using [toml](https://toml.io) files placed in the `./config` directory. The name of the configuration file should be the hostname the relay serves, for example `relay.example.com`. Config files contain the following sections: Configuration files are written using [toml](https://toml.io) files placed in the `CONF` directory. Top level configuration options are required:
### `[self]`
Contains information for populating the relay's `nip11` document.
Required:
- `host` - a hostname to serve this relay on.
- `schema` - a string that identifies this relay. This cannot be changed, and must be usable as a sqlite identifier. - `schema` - a string that identifies this relay. This cannot be changed, and must be usable as a sqlite identifier.
- `secret` - the nostr secret key of the relay. Will be used to populate the relay's NIP 11 `self` field and sign generated events. - `secret` - the nostr secret key of the relay. Will be used to populate the relay's NIP 11 `self` field and sign generated events.
Optional: ### `[info]`
Contains information for populating the relay's `nip11` document.
- `name` - the name of your relay. - `name` - the name of your relay.
- `icon` - an icon for your relay. - `icon` - an icon for your relay.
@@ -37,7 +35,7 @@ Optional:
Contains policy and access related configuration. Contains policy and access related configuration.
- `strip_signatures` - whether to remove signatures when serving events. This requires clients/users to trust the relay to properly authenticate signatures. Be cautious about using this; a malicious relay will be able to execute all kinds of attacks, including potentially serving events unrelated to a community use case. - `strip_signatures` - whether to remove signatures when serving events to non-admins. This requires clients/users to trust the relay to properly authenticate signatures. Be cautious about using this; a malicious relay will be able to execute all kinds of attacks, including potentially serving events unrelated to a community use case.
### `[groups]` ### `[groups]`
+21 -9
View File
@@ -15,12 +15,12 @@ type Role struct {
} }
type Config struct { type Config struct {
Host string Host string `toml:"host"`
Secret nostr.SecretKey Schema string `toml:"schema"`
Self struct { Secret string `toml:"secret"`
Info struct {
Name string `toml:"name"` Name string `toml:"name"`
Icon string `toml:"icon"` Icon string `toml:"icon"`
Schema string `toml:"schema"`
Secret string `toml:"secret"` Secret string `toml:"secret"`
Pubkey string `toml:"pubkey"` Pubkey string `toml:"pubkey"`
Description string `toml:"description"` Description string `toml:"description"`
@@ -46,6 +46,9 @@ type Config struct {
} `toml:"blossom"` } `toml:"blossom"`
Roles map[string]Role `toml:"roles"` Roles map[string]Role `toml:"roles"`
// Private/parsed values
secret nostr.SecretKey
} }
func LoadConfig(hostname string) (*Config, error) { func LoadConfig(hostname string) (*Config, error) {
@@ -56,23 +59,32 @@ func LoadConfig(hostname string) (*Config, error) {
return nil, fmt.Errorf("failed to parse config file %s: %w", path, err) return nil, fmt.Errorf("failed to parse config file %s: %w", path, err)
} }
secret, err := nostr.SecretKeyFromHex(config.Self.Secret) secret, err := nostr.SecretKeyFromHex(config.Secret)
if err != nil { if err != nil {
return nil, err return nil, err
} }
config.Host = hostname // Make the secret... secret
config.Secret = secret config.Secret = ""
config.secret = secret
return &config, nil return &config, nil
} }
func (config *Config) GetSelf() nostr.PubKey {
return config.secret.Public()
}
func (config *Config) IsSelf(pubkey nostr.PubKey) bool { func (config *Config) IsSelf(pubkey nostr.PubKey) bool {
return pubkey == nostr.MustSecretKeyFromHex(config.Self.Secret).Public() return pubkey == config.GetSelf()
}
func (config *Config) Sign(event *nostr.Event) error {
return event.Sign(config.secret)
} }
func (config *Config) IsOwner(pubkey nostr.PubKey) bool { func (config *Config) IsOwner(pubkey nostr.PubKey) bool {
return pubkey == nostr.MustPubKeyFromHex(config.Self.Pubkey) return pubkey == nostr.MustPubKeyFromHex(config.Info.Pubkey)
} }
func (config *Config) GetRolesForPubkey(pubkey nostr.PubKey) []Role { func (config *Config) GetRolesForPubkey(pubkey nostr.PubKey) []Role {
+1
View File
@@ -17,6 +17,7 @@ func Env(k string, fallback ...string) (v string) {
env["PORT"] = "3334" env["PORT"] = "3334"
env["DATA"] = "./data" env["DATA"] = "./data"
env["CONF"] = "./conf"
for _, item := range os.Environ() { for _, item := range os.Environ() {
parts := strings.SplitN(item, "=", 2) parts := strings.SplitN(item, "=", 2)
+4 -1
View File
@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"iter" "iter"
"log"
"fiatjaf.com/nostr" "fiatjaf.com/nostr"
"fiatjaf.com/nostr/eventstore" "fiatjaf.com/nostr/eventstore"
@@ -356,7 +357,9 @@ func (events *EventStore) GetOrCreateApplicationSpecificData(d string) nostr.Eve
}, },
} }
event.Sign(events.Config.Secret) if err := events.Config.Sign(&event); err != nil {
log.Println("Failed to sign application specific event: %w", err)
}
events.SaveEvent(event) events.SaveEvent(event)
+19 -11
View File
@@ -5,6 +5,7 @@ import (
"iter" "iter"
"log" "log"
"net/http" "net/http"
"os"
"path/filepath" "path/filepath"
"slices" "slices"
"strings" "strings"
@@ -50,6 +51,12 @@ func GetInstance(hostname string) (*Instance, error) {
} }
func MonitorInstances() { func MonitorInstances() {
dir := Env("CONF")
if err := os.MkdirAll(dir, 0755); err != nil {
log.Fatal("Failed to create config directory: %v", err)
}
watcher, err := fsnotify.NewWatcher() watcher, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
log.Printf("Failed to create file watcher: %v", err) log.Printf("Failed to create file watcher: %v", err)
@@ -57,7 +64,7 @@ func MonitorInstances() {
} }
defer watcher.Close() defer watcher.Close()
if err := watcher.Add("./config"); err != nil { if err := watcher.Add(dir); err != nil {
log.Printf("Failed to watch config directory: %v", err) log.Printf("Failed to watch config directory: %v", err)
return return
} }
@@ -108,7 +115,7 @@ func MakeInstance(hostname string) (*Instance, error) {
return nil, err return nil, err
} }
pubkey, err := nostr.PubKeyFromHex(config.Self.Pubkey) pubkey, err := nostr.PubKeyFromHex(config.Info.Pubkey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -116,7 +123,7 @@ func MakeInstance(hostname string) (*Instance, error) {
events := &EventStore{ events := &EventStore{
Config: config, Config: config,
Schema: &Schema{ Schema: &Schema{
Name: slug.Make(config.Self.Schema), Name: slug.Make(config.Schema),
}, },
} }
@@ -139,10 +146,10 @@ func MakeInstance(hostname string) (*Instance, error) {
} }
instance.Relay.Negentropy = true instance.Relay.Negentropy = true
instance.Relay.Info.Name = config.Self.Name instance.Relay.Info.Name = config.Info.Name
instance.Relay.Info.Icon = config.Self.Icon instance.Relay.Info.Icon = config.Info.Icon
instance.Relay.Info.PubKey = &pubkey instance.Relay.Info.PubKey = &pubkey
instance.Relay.Info.Description = config.Self.Description instance.Relay.Info.Description = config.Info.Description
// instance.Relay.Info.Self = nostr.GetPublicKey(secret) // instance.Relay.Info.Self = nostr.GetPublicKey(secret)
instance.Relay.Info.Software = "https://github.com/coracle-social/zooid" instance.Relay.Info.Software = "https://github.com/coracle-social/zooid"
instance.Relay.Info.Version = "v0.1.0" instance.Relay.Info.Version = "v0.1.0"
@@ -285,11 +292,12 @@ func (instance *Instance) GenerateInviteEvent(pubkey nostr.PubKey) nostr.Event {
}, },
} }
event.Sign(instance.Config.Secret) if err := instance.Config.Sign(&event); err != nil {
log.Printf("Failed to sign invite event: %v", err)
}
err := instance.Events.SaveEvent(event) if err := instance.Events.SaveEvent(event); err != nil {
if err != nil { log.Printf("Failed to save invite event: %v", err)
log.Printf("Failed to generate invite event: %v", err)
} }
return event return event
@@ -427,7 +435,7 @@ func (instance *Instance) DeleteEvent(ctx context.Context, id nostr.ID) error {
func (instance *Instance) OnEventSaved(ctx context.Context, event nostr.Event) { func (instance *Instance) OnEventSaved(ctx context.Context, event nostr.Event) {
addEvent := func(newEvent nostr.Event) { addEvent := func(newEvent nostr.Event) {
if err := newEvent.Sign(instance.Config.Secret); err != nil { if err := instance.Config.Sign(&newEvent); err != nil {
log.Println(err) log.Println(err)
} else { } else {
if err := instance.Events.SaveEvent(newEvent); err != nil { if err := instance.Events.SaveEvent(newEvent); err != nil {
+2 -2
View File
@@ -167,12 +167,12 @@ func (m *ManagementStore) Enable(instance *Instance) {
reasons := make([]nip86.PubKeyReason, 0) reasons := make([]nip86.PubKeyReason, 0)
reasons = append(reasons, nip86.PubKeyReason{ reasons = append(reasons, nip86.PubKeyReason{
PubKey: nostr.MustPubKeyFromHex(m.Config.Self.Pubkey), PubKey: nostr.MustPubKeyFromHex(m.Config.Info.Pubkey),
Reason: "relay owner", Reason: "relay owner",
}) })
reasons = append(reasons, nip86.PubKeyReason{ reasons = append(reasons, nip86.PubKeyReason{
PubKey: m.Config.Secret.Public(), PubKey: m.Config.GetSelf(),
Reason: "relay self", Reason: "relay self",
}) })