fix(relay): publish owner roles on open relays

Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757
2026-07-14 17:45:07 -04:00
parent fdbfac6b26
commit a4b16bac33
4 changed files with 31 additions and 5 deletions
+1 -1
View File
@@ -394,7 +394,7 @@ pub async fn transfer_community(
// Best-effort NIP-43 membership snapshot publication — same pattern as
// provision_community. The DB mutation is already committed; a publication
// failure must not turn a success into an HTTP error.
if state.config.require_relay_membership {
if state.config.can_publish_membership_metadata() {
if let Some(host) = state
.db
.lookup_community_host(community)
+23
View File
@@ -269,6 +269,14 @@ fn ensure_git_repo_path(
}
impl Config {
/// Whether this relay can publish durable, relay-signed membership metadata.
///
/// Open relays use the same metadata to expose owner/admin capabilities to
/// clients without enforcing membership for reads or writes.
pub fn can_publish_membership_metadata(&self) -> bool {
self.relay_private_key.is_some()
}
/// Loads configuration from environment variables, falling back to development defaults.
pub fn from_env() -> Result<Self, ConfigError> {
let bind_addr_raw =
@@ -683,6 +691,21 @@ mod tests {
// value set by `invalid_bind_addr_returns_error`, causing a flaky failure.
static ENV_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[test]
fn stable_signing_key_enables_membership_metadata_on_open_relay() {
let _guard = ENV_MUTEX.lock().unwrap();
std::env::set_var(
"BUZZ_RELAY_PRIVATE_KEY",
"0000000000000000000000000000000000000000000000000000000000000001",
);
std::env::remove_var("BUZZ_REQUIRE_RELAY_MEMBERSHIP");
let config = Config::from_env().expect("config");
std::env::remove_var("BUZZ_RELAY_PRIVATE_KEY");
assert!(!config.require_relay_membership);
assert!(config.can_publish_membership_metadata());
}
#[test]
fn defaults_are_valid() {
let _guard = ENV_MUTEX.lock().unwrap();
@@ -212,7 +212,7 @@ async fn publish_membership_snapshot_if_required(
community: buzz_core::CommunityId,
host: &str,
) {
if !state.config.require_relay_membership {
if !state.config.can_publish_membership_metadata() {
return;
}
+6 -3
View File
@@ -452,9 +452,12 @@ async fn main() -> anyhow::Result<()> {
);
}
// NIP-43: publish the initial membership list on startup so clients can
// REQ kind:13534 immediately without waiting for the next membership change.
if config.require_relay_membership {
// NIP-43 membership metadata: publish the initial role snapshot so clients
// can discover owner/admin capabilities immediately. Closed relays require
// this for NIP-43 enforcement; open relays with a stable signing key publish
// the same metadata so owner-only settings remain discoverable without
// turning membership enforcement on.
if config.require_relay_membership || config.can_publish_membership_metadata() {
let startup_state = Arc::clone(&state);
tokio::spawn(async move {
// Resolve the deployment's community from the configured relay URL