seed default channel(s) on boot
ci / build (pull_request) Successful in 23s
ci / image (pull_request) Skipped

fresh relay seeds configured open channels (RELAY_DEFAULT_CHANNELS,
default general) so a client always has a room. relay-signed 9007
through the normal pipeline; idempotent; never reopens a closed group.

also adds mission/roadmap and separation rationale to the readme.
This commit is contained in:
Robert Goodall
2026-07-25 08:11:39 -04:00
parent 294616642f
commit 8e9235a820
7 changed files with 220 additions and 15 deletions
+48 -11
View File
@@ -3,6 +3,7 @@ package main
import (
"context"
"encoding/json"
"log"
"net/http"
"fiatjaf.com/nostr"
@@ -24,18 +25,23 @@ type relayConfig struct {
description string
contact string
motd string
fedCfg federation.Config
// defaultChannels are group ids seeded as open channels on boot so a fresh
// relay is never empty — a client always has at least one room to join.
defaultChannels []string
fedCfg federation.Config
}
// relayApp is a fully-wired relay: the khatru relay, its HTTP handler, the group
// state authority, and (when configured) the federator. main starts/stops these;
// tests assert against them.
type relayApp struct {
relay *khatru.Relay
handler http.Handler
groups *group.State
federator *federation.Federator
pubKey nostr.PubKey
relay *khatru.Relay
handler http.Handler
groups *group.State
federator *federation.Federator
pubKey nostr.PubKey
secretKey nostr.SecretKey
defaultChannels []string
}
// buildRelay assembles the relay from its internal packages and wires khatru's
@@ -146,11 +152,42 @@ func buildRelay(cfg relayConfig) *relayApp {
})
return &relayApp{
relay: relay,
handler: mux,
groups: groups,
federator: fed,
pubKey: pk,
relay: relay,
handler: mux,
groups: groups,
federator: fed,
pubKey: pk,
secretKey: cfg.secretKey,
defaultChannels: cfg.defaultChannels,
}
}
// SeedDefaultChannels creates any configured default channel that doesn't
// already exist, so a fresh relay always has at least one room to join. Each
// missing channel is created by the relay identity via a signed kind-9007
// create-group event pushed through the normal add pipeline (OnEvent → store →
// OnEventSaved), so it persists, replays on restart, and publishes its 39000/
// 39001/39002 lists exactly like a client-created group. Existing channels are
// left untouched. Call after Rebuild so it only seeds what's genuinely missing.
func (app *relayApp) SeedDefaultChannels(ctx context.Context) {
for _, id := range app.defaultChannels {
if id == "" || app.groups.Has(id) {
continue
}
evt := nostr.Event{
Kind: nostr.KindSimpleGroupCreateGroup,
CreatedAt: nostr.Now(),
Tags: nostr.Tags{{"h", id}},
}
if err := evt.Sign(app.secretKey); err != nil {
log.Printf("could not sign default-channel create for %q: %v", id, err)
continue
}
if _, err := app.relay.AddEvent(ctx, evt); err != nil {
log.Printf("could not seed default channel %q: %v", id, err)
continue
}
log.Printf("seeded default channel %q", id)
}
}