refactor(geode): split config into StaticConfig + RuntimeConfig with seeding

Rename RelayConfig → StaticConfig and the persistence/RelayStateStore →
config/RuntimeConfig so the two configuration tiers — boot-time TOML
versus operator-mutable runtime state — are obvious from the class
names. The `persistence/` package is gone; everything related to config
lives in `config/`.

For overlapping fields (NIP-11 info doc, pubkey / kind allow-deny
lists), StaticConfig now seeds RuntimeConfig on first boot via the new
RuntimeConfig(seed = …) constructor and an AuthorizationSeed type. The
runtime file wins from then on: later edits to [authorization] in the
TOML are ignored once an admin RPC has written the snapshot. As a
consequence, KindAllowDenyPolicy and PubkeyAllowDenyPolicy are no
longer stacked in geode's policy chain — BanListPolicy already reads
the same lists from the BanStore, and double-stacking would silently
diverge after the first admin mutation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vitor Pamplona
2026-05-12 15:51:19 -04:00
parent 9c0873b178
commit b0ad540453
11 changed files with 408 additions and 173 deletions
@@ -30,12 +30,14 @@ import com.vitorpamplona.quartz.nip01Core.relay.server.policies.PassThroughPolic
* non-empty pubkey allow list, or kind disallowed / not in the kind
* allow list.
*
* This is the runtime-mutable counterpart of the static
* Functionally equivalent to (and a superset of) the static
* [com.vitorpamplona.quartz.nip01Core.relay.server.policies.KindAllowDenyPolicy] +
* [com.vitorpamplona.quartz.nip01Core.relay.server.policies.PubkeyAllowDenyPolicy]
* both layers compose: the event must clear all stacked policies.
* NIP-86 admin RPC mutations land in the [BanStore]; the static
* policies stay frozen at boot-time config values.
* [com.vitorpamplona.quartz.nip01Core.relay.server.policies.PubkeyAllowDenyPolicy].
* Callers that wire a [BanStore] should NOT also install those static
* policies: NIP-86 admin RPC mutations land in the [BanStore], so the
* static policies would silently diverge after the first admin call.
* Geode seeds the [BanStore] from `[authorization]` at first boot
* instead — see `com.vitorpamplona.geode.config.RuntimeConfig`.
*/
class BanListPolicy(
val banStore: BanStore,