mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Fix local relay auth and community persistence
Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
This commit is contained in:
parent
7c73a9522e
commit
89e0350bbf
@@ -1439,6 +1439,27 @@ impl Db {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Rebind the durable loopback community to this single-node process's
|
||||
/// current ephemeral authority while preserving its stable UUID and data.
|
||||
/// SQLite-only: production deployments never rewrite tenant host mappings.
|
||||
pub async fn rebind_single_node_community_host(
|
||||
&self,
|
||||
normalized_host: &str,
|
||||
owner_pubkey: &str,
|
||||
) -> Result<Option<CommunityRecord>> {
|
||||
if matches!(&self.backend, DbBackend::SQLite(_)) {
|
||||
return sqlite::rebind_single_node_community_host(
|
||||
self.sqlite_pool()?,
|
||||
normalized_host,
|
||||
owner_pubkey,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Err(DbError::UnsupportedBackend(
|
||||
"single-node community rebind requires SQLite",
|
||||
))
|
||||
}
|
||||
|
||||
/// Ensure a configured community host exists and return its row.
|
||||
///
|
||||
/// This is the startup/config seeding path for N=1 deployments. Migrations
|
||||
|
||||
@@ -1231,7 +1231,14 @@ async fn run_single_node(config: Config, tracer_init: telemetry::TracerInit) ->
|
||||
"Cannot derive community host from BUZZ_RELAY_URL"
|
||||
));
|
||||
}
|
||||
let community = db.ensure_configured_community(&host).await?.id;
|
||||
let community = if let Some(owner) = config.relay_owner_pubkey.as_deref() {
|
||||
match db.rebind_single_node_community_host(&host, owner).await? {
|
||||
Some(record) => record.id,
|
||||
None => db.ensure_configured_community(&host).await?.id,
|
||||
}
|
||||
} else {
|
||||
db.ensure_configured_community(&host).await?.id
|
||||
};
|
||||
if let Some(owner) = config.relay_owner_pubkey.as_deref() {
|
||||
db.bootstrap_owner(community, owner).await?;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,46 @@ use crate::{
|
||||
util::now_iso,
|
||||
};
|
||||
|
||||
pub(super) fn backfill_missing_agent_auth_tags(
|
||||
app: &AppHandle,
|
||||
state: &AppState,
|
||||
) -> Result<usize, String> {
|
||||
let owner_keys = state.signing_keys()?;
|
||||
let compat_owner = nostr::Keys::parse(&owner_keys.secret_key().to_secret_hex())
|
||||
.map_err(|e| format!("failed to bridge owner keys: {e}"))?;
|
||||
let _store_guard = state
|
||||
.managed_agents_store_lock
|
||||
.lock()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut records = load_managed_agents(app)?;
|
||||
let mut changed = 0;
|
||||
for record in &mut records {
|
||||
if record.auth_tag.is_some()
|
||||
|| record
|
||||
.pubkey
|
||||
.eq_ignore_ascii_case(&owner_keys.public_key().to_hex())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let agent = nostr::PublicKey::from_hex(&record.pubkey)
|
||||
.map_err(|e| format!("invalid managed agent pubkey {}: {e}", record.pubkey))?;
|
||||
record.auth_tag = Some(
|
||||
buzz_sdk_pkg::nip_oa::compute_auth_tag(&compat_owner, &agent, "").map_err(|e| {
|
||||
format!(
|
||||
"failed to compute NIP-OA auth tag for {}: {e}",
|
||||
record.pubkey
|
||||
)
|
||||
})?,
|
||||
);
|
||||
record.updated_at = now_iso();
|
||||
changed += 1;
|
||||
}
|
||||
if changed > 0 {
|
||||
save_managed_agents(app, &records)?;
|
||||
}
|
||||
Ok(changed)
|
||||
}
|
||||
|
||||
/// Read the workspace owner pubkey without holding the lock. Used to populate `BUZZ_ACP_AGENT_OWNER`
|
||||
/// as a fallback for legacy agent records that have no NIP-OA `auth_tag`.
|
||||
pub(super) fn workspace_owner_hex(state: &AppState) -> Result<String, String> {
|
||||
|
||||
@@ -145,7 +145,8 @@ pub async fn apply_workspace(
|
||||
None => None,
|
||||
};
|
||||
|
||||
if crate::local_relay::is_local_relay_url(&relay_url) {
|
||||
let is_local_workspace = crate::local_relay::is_local_relay_url(&relay_url);
|
||||
if is_local_workspace {
|
||||
// Only the UI-created local sentinel can launch the bundled relay.
|
||||
// An arbitrary loopback URL remains a normal remote community.
|
||||
let keys = parsed_keys.clone().unwrap_or(state.signing_keys()?);
|
||||
@@ -228,6 +229,12 @@ pub async fn apply_workspace(
|
||||
}
|
||||
|
||||
try_regenerate_nest(&app);
|
||||
if is_local_workspace {
|
||||
let changed = crate::commands::agents::backfill_missing_agent_auth_tags(&app, &state)?;
|
||||
if changed > 0 {
|
||||
eprintln!("buzz-desktop: backfilled NIP-OA auth tags for {changed} local agent(s)");
|
||||
}
|
||||
}
|
||||
|
||||
Ok::<(), String>(())
|
||||
})
|
||||
|
||||
@@ -53,6 +53,7 @@ impl LocalRelayConfig {
|
||||
("BUZZ_LOCAL_DB", sqlite_url(&db_path)),
|
||||
("BUZZ_LOCAL_MEDIA_DIR", media_dir.display().to_string()),
|
||||
("BUZZ_REQUIRE_RELAY_MEMBERSHIP", "true".to_string()),
|
||||
("BUZZ_ALLOW_NIP_OA_AUTH", "true".to_string()),
|
||||
("RELAY_OWNER_PUBKEY", self.owner_pubkey.clone()),
|
||||
("BUZZ_RELAY_PRIVATE_KEY", self.relay_private_key.clone()),
|
||||
]
|
||||
@@ -369,6 +370,7 @@ mod tests {
|
||||
env.get("BUZZ_REQUIRE_RELAY_MEMBERSHIP"),
|
||||
Some(&"true".to_string())
|
||||
);
|
||||
assert_eq!(env.get("BUZZ_ALLOW_NIP_OA_AUTH"), Some(&"true".to_string()));
|
||||
assert_eq!(env.get("RELAY_OWNER_PUBKEY"), Some(&"owner".to_string()));
|
||||
assert_eq!(
|
||||
env.get("BUZZ_RELAY_PRIVATE_KEY"),
|
||||
|
||||
Reference in New Issue
Block a user