diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27cdbf883c..d4826d985f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1051,24 +1051,16 @@ jobs: - name: Build mesh llama native libraries if: steps.llama_cache.outputs.cache-hit != 'true' env: - MESH_REV: ${{ steps.mesh_rev.outputs.rev }} + MESH_REV_SHORT: ${{ steps.mesh_rev.outputs.short }} run: | set -euo pipefail - # Check out the pinned mesh-llm rev directly instead of scraping - # Cargo's git/checkouts layout: `cargo fetch` only guarantees the bare - # clone under git/db, so the checkout dir the old `find` looked for is - # frequently absent and the step failed with "checkout not found". - MESH_ROOT="${RUNNER_TEMP:-/tmp}/mesh-llm-$MESH_REV" - rm -rf "$MESH_ROOT" - mkdir -p "$MESH_ROOT" - git -C "$MESH_ROOT" init -q - git -C "$MESH_ROOT" remote add origin https://github.com/Mesh-LLM/mesh-llm.git - git -C "$MESH_ROOT" fetch -q --depth 1 origin "$MESH_REV" - git -C "$MESH_ROOT" checkout -q --detach FETCH_HEAD - [[ -x "$MESH_ROOT/scripts/build-llama.sh" ]] || { - echo "::error::mesh-llm $MESH_REV checked out but scripts/build-llama.sh is missing" + cargo fetch --manifest-path desktop/src-tauri/Cargo.toml + SHORT="$MESH_REV_SHORT" + MESH_ROOT=$(find "${CARGO_HOME:-$HOME/.cargo}/git/checkouts" -path "*/$SHORT" -type d -name "$SHORT" | head -1) + if [[ -z "$MESH_ROOT" ]]; then + echo "::error::mesh-llm checkout for $SHORT not found after cargo fetch" exit 1 - } + fi export LLAMA_STAGE_BACKEND=metal export LLAMA_STAGE_BUILD_DIR="$GITHUB_WORKSPACE/.cache/mesh-llama/build-stage-abi-metal" export CMAKE_OSX_DEPLOYMENT_TARGET=10.15 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index baff3be241..52f21b28bc 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -401,16 +401,8 @@ jobs: platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} outputs: type=image,name=ghcr.io/block/buzz-push-gateway,push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }} - # Buildcache lives in the namespace of whichever org runs the - # workflow: GHCR login uses github.repository_owner, so a fork can - # only write its own namespace. Hardcoding block/ made every fork run - # fail the cache export with "permission_denied: The requested - # installation does not exist". Reads still fall back to block's - # public cache so a fork's first build is still warm. - cache-from: | - type=registry,ref=ghcr.io/${{ github.repository_owner }}/buzz-push-gateway-buildcache:${{ matrix.arch }} - type=registry,ref=ghcr.io/block/buzz-push-gateway-buildcache:${{ matrix.arch }} - cache-to: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && format('type=registry,ref=ghcr.io/{0}/buzz-push-gateway-buildcache:{1},mode=max,compression=zstd', github.repository_owner, matrix.arch) || '' }} + cache-from: type=registry,ref=ghcr.io/block/buzz-push-gateway-buildcache:${{ matrix.arch }} + cache-to: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && format('type=registry,ref=ghcr.io/block/buzz-push-gateway-buildcache:{0},mode=max,compression=zstd', matrix.arch) || '' }} - name: Export digest if: github.event_name != 'pull_request' env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8f89124a7e..c613924e57 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -155,24 +155,16 @@ jobs: - name: Build mesh llama native libraries if: steps.llama_cache.outputs.cache-hit != 'true' env: - MESH_REV: ${{ steps.mesh_rev.outputs.rev }} + MESH_REV_SHORT: ${{ steps.mesh_rev.outputs.short }} run: | set -euo pipefail - # Check out the pinned mesh-llm rev directly instead of scraping - # Cargo's git/checkouts layout: `cargo fetch` only guarantees the bare - # clone under git/db, so the checkout dir the old `find` looked for is - # frequently absent and the step failed with "checkout not found". - MESH_ROOT="${RUNNER_TEMP:-/tmp}/mesh-llm-$MESH_REV" - rm -rf "$MESH_ROOT" - mkdir -p "$MESH_ROOT" - git -C "$MESH_ROOT" init -q - git -C "$MESH_ROOT" remote add origin https://github.com/Mesh-LLM/mesh-llm.git - git -C "$MESH_ROOT" fetch -q --depth 1 origin "$MESH_REV" - git -C "$MESH_ROOT" checkout -q --detach FETCH_HEAD - [[ -x "$MESH_ROOT/scripts/build-llama.sh" ]] || { - echo "::error::mesh-llm $MESH_REV checked out but scripts/build-llama.sh is missing" + cargo fetch --manifest-path desktop/src-tauri/Cargo.toml + SHORT="$MESH_REV_SHORT" + MESH_ROOT=$(find "${CARGO_HOME:-$HOME/.cargo}/git/checkouts" -path "*/$SHORT" -type d -name "$SHORT" | head -1) + if [[ -z "$MESH_ROOT" ]]; then + echo "::error::mesh-llm checkout for $SHORT not found after cargo fetch" exit 1 - } + fi export LLAMA_STAGE_BACKEND=metal export LLAMA_STAGE_BUILD_DIR="$GITHUB_WORKSPACE/.cache/mesh-llama/build-stage-abi-metal" export CMAKE_OSX_DEPLOYMENT_TARGET=10.15 diff --git a/.github/workflows/signed-macos-canary.yml b/.github/workflows/signed-macos-canary.yml index 9edda197a8..fb0656028a 100644 --- a/.github/workflows/signed-macos-canary.yml +++ b/.github/workflows/signed-macos-canary.yml @@ -116,24 +116,15 @@ jobs: - name: Build mesh llama native libraries if: steps.llama_cache.outputs.cache-hit != 'true' env: - MESH_REV: ${{ steps.mesh_rev.outputs.rev }} + MESH_REV_SHORT: ${{ steps.mesh_rev.outputs.short }} run: | set -euo pipefail - # Check out the pinned mesh-llm rev directly instead of scraping - # Cargo's git/checkouts layout: `cargo fetch` only guarantees the bare - # clone under git/db, so the checkout dir the old `find` looked for is - # frequently absent and the step failed with "checkout not found". - MESH_ROOT="${RUNNER_TEMP:-/tmp}/mesh-llm-$MESH_REV" - rm -rf "$MESH_ROOT" - mkdir -p "$MESH_ROOT" - git -C "$MESH_ROOT" init -q - git -C "$MESH_ROOT" remote add origin https://github.com/Mesh-LLM/mesh-llm.git - git -C "$MESH_ROOT" fetch -q --depth 1 origin "$MESH_REV" - git -C "$MESH_ROOT" checkout -q --detach FETCH_HEAD - [[ -x "$MESH_ROOT/scripts/build-llama.sh" ]] || { - echo "::error::mesh-llm $MESH_REV checked out but scripts/build-llama.sh is missing" + cargo fetch --manifest-path desktop/src-tauri/Cargo.toml + MESH_ROOT=$(find "${CARGO_HOME:-$HOME/.cargo}/git/checkouts" -path "*/$MESH_REV_SHORT" -type d -name "$MESH_REV_SHORT" | head -1) + if [[ -z "$MESH_ROOT" ]]; then + echo "::error::mesh-llm checkout for $MESH_REV_SHORT not found after cargo fetch" exit 1 - } + fi export LLAMA_STAGE_BACKEND=metal export LLAMA_STAGE_BUILD_DIR="$GITHUB_WORKSPACE/.cache/mesh-llama/build-stage-abi-metal" export CMAKE_OSX_DEPLOYMENT_TARGET=10.15 diff --git a/Cargo.lock b/Cargo.lock index 518c8e912a..3b60dc4579 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,7 +103,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -114,7 +114,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1536,7 +1536,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2385,7 +2385,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2623,7 +2623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3006,7 +3006,7 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link 0.1.3", + "windows-link 0.2.1", "windows-result 0.4.1", ] @@ -5457,9 +5457,9 @@ dependencies = [ [[package]] name = "nostr" -version = "0.44.7" +version = "0.44.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d3d987ea7078dc36947cde532637c472a229426702e4331dd7667325378bd9" +checksum = "e826dd648489de2c5b293920e20b92932ef820302007c1987c758d4d06eeb2cf" dependencies = [ "base64", "bech32", @@ -5501,9 +5501,9 @@ dependencies = [ [[package]] name = "nostr-relay-pool" -version = "0.44.3" +version = "0.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85c54d6ca9aae4ae2bf19a7663ba9db5f45f783f1d24aff55f006386b8b99a1" +checksum = "91b2c039df4f96c4bf7dae52a74fd5516ad6dda83a11c0c69dea91b5255a4f37" dependencies = [ "async-utility", "async-wsocket", @@ -5547,7 +5547,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7502,7 +7502,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7561,7 +7561,7 @@ dependencies = [ "security-framework 3.7.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7824,7 +7824,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8274,7 +8274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -8721,7 +8721,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8734,7 +8734,7 @@ dependencies = [ "parking_lot", "rustix 1.1.4", "signal-hook", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -9436,7 +9436,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -9977,7 +9977,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/crates/buzz-relay/src/api/invites.rs b/crates/buzz-relay/src/api/invites.rs index 4e5d58a086..6104171cca 100644 --- a/crates/buzz-relay/src/api/invites.rs +++ b/crates/buzz-relay/src/api/invites.rs @@ -24,9 +24,7 @@ use axum::{ use serde::Deserialize; use serde_json::Value; -use crate::handlers::side_effects::{ - join_open_channel_member, publish_nip43_member_added, publish_nip43_membership_list, -}; +use crate::handlers::side_effects::{publish_nip43_member_added, publish_nip43_membership_list}; use buzz_core::invite::{ hash_v2_code, validate_v2_code, DEFAULT_INVITE_TTL_SECS, MAX_INVITE_TTL_SECS, MAX_INVITE_USES, MIN_INVITE_TTL_SECS, V2_PREFIX, @@ -47,41 +45,6 @@ const CLAIM_RATE_LIMIT: u32 = 10; /// bound is required in addition to expiry. pub(crate) const CLAIM_RATE_CACHE_CAPACITY: u64 = 10_000; -fn general_channel_rank(name: &str) -> Option { - match name.trim().to_ascii_lowercase().as_str() { - // Intent communities pin General first in lexical channel lists. - "0-general" => Some(0), - "general" => Some(1), - _ => None, - } -} - -async fn auto_join_general_channel( - tenant: &buzz_core::tenant::TenantContext, - state: &Arc, - member_pubkey: &[u8], -) -> anyhow::Result> { - let channels = state - .db - .list_channels(tenant.community(), Some("open")) - .await?; - let general = channels - .iter() - .filter(|channel| { - channel.channel_type == "stream" - && channel.archived_at.is_none() - && general_channel_rank(&channel.name).is_some() - }) - .min_by_key(|channel| general_channel_rank(&channel.name)); - - let Some(general) = general else { - return Ok(None); - }; - - join_open_channel_member(tenant, general.id, member_pubkey, state).await?; - Ok(Some(general.id)) -} - /// Body for `POST /api/invites`. #[derive(Debug, Default, Deserialize)] pub struct MintInviteRequest { @@ -443,13 +406,6 @@ pub async fn claim_invite( member = %claimer_hex, "relay member added via v2 invite" ); - if let Err(e) = auto_join_general_channel(&tenant, &state, &pubkey.to_bytes()).await - { - tracing::warn!( - member = %claimer_hex, - "failed to auto-join General after v2 invite claim: {e}" - ); - } // NIP-43 side effects only on Joined, never on other outcomes. if let Err(e) = publish_nip43_member_added(&tenant, &state, &claimer_hex).await { tracing::warn!( @@ -528,12 +484,6 @@ pub async fn claim_invite( member = %claimer_hex, "relay member added via invite" ); - if let Err(e) = auto_join_general_channel(&tenant, &state, &pubkey.to_bytes()).await { - tracing::warn!( - member = %claimer_hex, - "failed to auto-join General after invite claim: {e}" - ); - } if let Err(e) = publish_nip43_member_added(&tenant, &state, &claimer_hex).await { tracing::warn!("failed to publish NIP-43 member-added delta after claim: {e}"); } @@ -581,22 +531,12 @@ mod tests { use std::sync::Arc; use std::time::Duration; - use super::{ - claim_key_rate_limited, general_channel_rank, CLAIM_RATE_LIMIT, MAX_INVITE_USES, - MIN_INVITE_TTL_SECS, - }; + use super::{claim_key_rate_limited, CLAIM_RATE_LIMIT, MAX_INVITE_USES, MIN_INVITE_TTL_SECS}; use axum::{ body::{to_bytes, Body}, http::{header, Request, StatusCode}, }; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; - - #[test] - fn canonical_general_alias_is_preferred_for_invite_auto_join() { - assert_eq!(general_channel_rank("0-general"), Some(0)); - assert_eq!(general_channel_rank(" General "), Some(1)); - assert_eq!(general_channel_rank("welcome-everyone"), None); - } use hmac::{Hmac, KeyInit, Mac}; use nostr::{EventBuilder, EventId, Keys, Kind, Tag}; use serde_json::Value; diff --git a/crates/buzz-relay/src/handlers/side_effects.rs b/crates/buzz-relay/src/handlers/side_effects.rs index 029738114c..65d04ef0ba 100644 --- a/crates/buzz-relay/src/handlers/side_effects.rs +++ b/crates/buzz-relay/src/handlers/side_effects.rs @@ -1928,17 +1928,15 @@ async fn handle_delete_group( Ok(()) } -/// Enroll a relay member in an open channel and publish the same membership -/// side effects as a signed kind:9021 self-join. -/// -/// Invite redemption uses this after relay admission so desktop and mobile -/// clients observe the canonical General membership immediately. -pub async fn join_open_channel_member( +async fn handle_join_request( tenant: &TenantContext, - channel_id: Uuid, - actor_bytes: &[u8], + event: &Event, state: &Arc, -) -> anyhow::Result { +) -> anyhow::Result<()> { + let channel_id = + extract_h_tag_channel(event).ok_or_else(|| anyhow::anyhow!("missing h tag"))?; + let actor_bytes = event.pubkey.to_bytes().to_vec(); + // Only open channels allow self-join via kind:9021. let channel = state .db @@ -1954,11 +1952,11 @@ pub async fn join_open_channel_member( // Skip if already an active member — prevents duplicate join notifications. // Fail closed on DB errors rather than falling through to add_member. if state - .is_member_cached(tenant.community(), channel_id, actor_bytes) + .is_member_cached(tenant.community(), channel_id, &actor_bytes) .await? { - info!(channel = %channel_id, "open-channel join — already a member, skipping"); - return Ok(false); + info!(channel = %channel_id, "kind:9021 join — already a member, skipping"); + return Ok(()); } // Add as member (idempotent — add_member handles duplicates). @@ -1967,14 +1965,14 @@ pub async fn join_open_channel_member( .add_member( tenant.community(), channel_id, - actor_bytes, + &actor_bytes, buzz_db::channel::MemberRole::Member, None, ) .await?; - state.invalidate_membership(tenant, channel_id, actor_bytes); + state.invalidate_membership(tenant, channel_id, &actor_bytes); - let actor_hex = hex::encode(actor_bytes); + let actor_hex = hex::encode(&actor_bytes); emit_system_message( tenant, state, @@ -1995,8 +1993,8 @@ pub async fn join_open_channel_member( tenant, state, channel_id, - actor_bytes, - actor_bytes, + &actor_bytes, + &actor_bytes, buzz_core::kind::KIND_MEMBER_ADDED_NOTIFICATION, ) .await @@ -2004,20 +2002,6 @@ pub async fn join_open_channel_member( warn!("membership notification for join failed: {e}"); } - info!(channel = %channel_id, "open-channel join processed"); - Ok(true) -} - -async fn handle_join_request( - tenant: &TenantContext, - event: &Event, - state: &Arc, -) -> anyhow::Result<()> { - let channel_id = - extract_h_tag_channel(event).ok_or_else(|| anyhow::anyhow!("missing h tag"))?; - let actor_bytes = event.pubkey.to_bytes(); - - join_open_channel_member(tenant, channel_id, &actor_bytes, state).await?; info!(channel = %channel_id, "kind:9021 join processed"); Ok(()) } diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index e13965240d..66553ef595 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -172,7 +172,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -183,7 +183,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2327,7 +2327,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2608,7 +2608,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5551,7 +5551,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5917,9 +5917,9 @@ dependencies = [ [[package]] name = "nostr" -version = "0.44.7" +version = "0.44.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d3d987ea7078dc36947cde532637c472a229426702e4331dd7667325378bd9" +checksum = "e826dd648489de2c5b293920e20b92932ef820302007c1987c758d4d06eeb2cf" dependencies = [ "base64 0.22.1", "bech32", @@ -5961,9 +5961,9 @@ dependencies = [ [[package]] name = "nostr-relay-pool" -version = "0.44.3" +version = "0.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85c54d6ca9aae4ae2bf19a7663ba9db5f45f783f1d24aff55f006386b8b99a1" +checksum = "91b2c039df4f96c4bf7dae52a74fd5516ad6dda83a11c0c69dea91b5255a4f37" dependencies = [ "async-utility", "async-wsocket", @@ -6021,7 +6021,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7593,7 +7593,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8287,7 +8287,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8346,7 +8346,7 @@ dependencies = [ "security-framework 3.7.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8602,7 +8602,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -9159,7 +9159,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10150,10 +10150,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.3", + "getrandom 0.3.4", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -10175,7 +10175,7 @@ dependencies = [ "parking_lot", "rustix 1.1.4", "signal-hook", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10874,7 +10874,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10975,7 +10975,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/desktop/src-tauri/src/commands/channels.rs b/desktop/src-tauri/src/commands/channels.rs index 9a9568e336..59c80c4807 100644 --- a/desktop/src-tauri/src/commands/channels.rs +++ b/desktop/src-tauri/src/commands/channels.rs @@ -16,7 +16,6 @@ const STARTER_CHANNEL_NAMESPACE: uuid::Uuid = uuid::uuid!("3ce33bea-8f09-5f1b-9c struct StarterChannelSpec { slug: &'static str, name: &'static str, - aliases: &'static [&'static str], description: &'static str, } @@ -24,16 +23,11 @@ const STARTER_CHANNELS: &[StarterChannelSpec] = &[ StarterChannelSpec { slug: "general", name: "general", - // Existing communities may pin General first in lexical channel lists - // by naming it `0-general`. Treat that established channel as the - // starter instead of creating a duplicate `general` channel. - aliases: &["0-general"], description: "General conversation and community updates.", }, StarterChannelSpec { slug: "welcome-everyone", name: "welcome-everyone", - aliases: &[], description: "Say hi, ask a question, or share what brought you here.", }, ]; @@ -485,12 +479,7 @@ fn is_duplicate_channel_rejection(error: &str) -> bool { } fn is_matching_starter_channel(channel: &ChannelInfo, spec: &StarterChannelSpec) -> bool { - let normalized_name = normalize_channel_name(&channel.name); - (normalized_name == normalize_channel_name(spec.name) - || spec - .aliases - .iter() - .any(|alias| normalized_name == normalize_channel_name(alias))) + normalize_channel_name(&channel.name) == normalize_channel_name(spec.name) && channel.channel_type == "stream" && channel.visibility == "open" && channel.archived_at.is_none() diff --git a/desktop/src-tauri/src/commands/channels_tests.rs b/desktop/src-tauri/src/commands/channels_tests.rs index 7afcc1552e..5b65695a91 100644 --- a/desktop/src-tauri/src/commands/channels_tests.rs +++ b/desktop/src-tauri/src/commands/channels_tests.rs @@ -290,12 +290,6 @@ fn starter_match_requires_open_unarchived_stream_by_normalized_name() { assert!(is_matching_starter_channel(&channel, spec)); - channel.name = "0-General".to_string(); - assert!( - is_matching_starter_channel(&channel, spec), - "the canonical pinned General alias must be reused and joined" - ); - channel.visibility = "private".to_string(); assert!(!is_matching_starter_channel(&channel, spec)); diff --git a/desktop/src/features/onboarding/welcome.test.mjs b/desktop/src/features/onboarding/welcome.test.mjs index 11cf79ba5a..e0a57a4586 100644 --- a/desktop/src/features/onboarding/welcome.test.mjs +++ b/desktop/src/features/onboarding/welcome.test.mjs @@ -305,66 +305,6 @@ test("ensureStarterChannels reuses existing open starter channels", async () => assert.equal(ensureCalls, 0); }); -test("ensureStarterChannels reuses the canonical pinned 0-general channel", async () => { - const general = makeChannel({ - id: "canonical-general-channel", - name: "0-general", - visibility: "open", - }); - const welcomeEveryone = makeChannel({ - id: "welcome-everyone-channel", - name: "welcome-everyone", - visibility: "open", - }); - let ensureCalls = 0; - - const result = await ensureStarterChannels({ - getChannels: async () => [general, welcomeEveryone], - ensureStarterChannels: async () => { - ensureCalls += 1; - return []; - }, - }); - - assert.equal(result.generalChannel, general); - assert.equal(result.welcomeChannel, welcomeEveryone); - assert.equal(ensureCalls, 0); -}); - -test("ensureStarterChannels prefers 0-general when both Generals exist", async () => { - // Production holds BOTH `general` and `0-general`. Listed with the legacy `general` - // FIRST, so a lookup whose preference came from channel order would pick it and - // disagree with the relay's invite auto-join, which ranks `0-general` first. - const legacyGeneral = makeChannel({ - id: "legacy-general-channel", - name: "general", - visibility: "open", - }); - const canonicalGeneral = makeChannel({ - id: "canonical-general-channel", - name: "0-general", - visibility: "open", - }); - const welcomeEveryone = makeChannel({ - id: "welcome-everyone-channel", - name: "welcome-everyone", - visibility: "open", - }); - let ensureCalls = 0; - - const result = await ensureStarterChannels({ - getChannels: async () => [legacyGeneral, canonicalGeneral, welcomeEveryone], - ensureStarterChannels: async () => { - ensureCalls += 1; - return []; - }, - }); - - assert.equal(result.generalChannel, canonicalGeneral); - assert.equal(result.welcomeChannel, welcomeEveryone); - assert.equal(ensureCalls, 0); -}); - test("ensureStarterChannels resumes when one starter channel is missing", async () => { const general = makeChannel({ id: "general-channel", diff --git a/desktop/src/features/onboarding/welcome.ts b/desktop/src/features/onboarding/welcome.ts index a5b61f76dd..359c8341b2 100644 --- a/desktop/src/features/onboarding/welcome.ts +++ b/desktop/src/features/onboarding/welcome.ts @@ -9,7 +9,6 @@ export const WELCOME_CHANNEL_NAME = "Welcome"; export const WELCOME_CHANNEL_DESCRIPTION = "A private channel for getting oriented in this community."; export const STARTER_GENERAL_CHANNEL_NAME = "general"; -export const STARTER_GENERAL_CHANNEL_ALIASES = ["0-general"] as const; export const STARTER_GENERAL_CHANNEL_DESCRIPTION = "General conversation and community updates."; export const STARTER_WELCOME_CHANNEL_NAME = "welcome-everyone"; @@ -78,31 +77,11 @@ function isOpenStreamStarterChannel(channel: Channel, name: string) { ); } -// Resolve a starter channel by name, preferring `aliases` in the order given and -// falling back to `name` last. -// -// The preference MUST come from the candidate order here, not from the order of -// `channels`. A single `find` over a combined `name || aliases.some(...)` predicate -// returns whichever candidate happens to appear first in the relay's channel list, so -// a community holding BOTH `0-general` and `general` (production does) could send -// desktop onboarding to a different General than the relay's invite auto-join picked — -// that side ranks `0-general` first via `general_channel_rank` -// (crates/buzz-relay/src/api/invites.rs). Order-dependent agreement between two -// independent implementations is not agreement. -function findStarterChannel( - channels: Channel[], - name: string, - aliases: readonly string[] = [], -) { - for (const candidate of [...aliases, name]) { - const match = channels.find((channel) => - isOpenStreamStarterChannel(channel, candidate), - ); - if (match) { - return match; - } - } - return null; +function findStarterChannel(channels: Channel[], name: string) { + return ( + channels.find((channel) => isOpenStreamStarterChannel(channel, name)) ?? + null + ); } export function findStarterChannels( @@ -111,7 +90,6 @@ export function findStarterChannels( const generalChannel = findStarterChannel( channels, STARTER_GENERAL_CHANNEL_NAME, - STARTER_GENERAL_CHANNEL_ALIASES, ); const welcomeChannel = findStarterChannel( channels, diff --git a/desktop/src/features/settings/ui/SettingsPanels.tsx b/desktop/src/features/settings/ui/SettingsPanels.tsx index c195bbdb70..e74d1f3837 100644 --- a/desktop/src/features/settings/ui/SettingsPanels.tsx +++ b/desktop/src/features/settings/ui/SettingsPanels.tsx @@ -201,7 +201,7 @@ export const settingsSections: SettingsSectionDescriptor[] = [ }, { value: "community-members", - label: "Members & invites", + label: "Invites", icon: Ticket, }, { diff --git a/desktop/tests/e2e/invites-settings-screenshots.spec.ts b/desktop/tests/e2e/invites-settings-screenshots.spec.ts index 447b8c8266..654aec56fe 100644 --- a/desktop/tests/e2e/invites-settings-screenshots.spec.ts +++ b/desktop/tests/e2e/invites-settings-screenshots.spec.ts @@ -31,7 +31,7 @@ test("capture: consolidated invites settings", async ({ page }) => { await expect( page.getByTestId("settings-nav-community-members"), - ).toContainText("Members & invites"); + ).toContainText("Invites"); await expect( page.getByRole("heading", { name: "Invites", exact: true }), ).toBeVisible();