mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
chore(mesh): upgrade MeshLLM to 0.76.2 (#7559)
## Summary Upgrade Buzz from MeshLLM `v0.76.0-rc9` to the latest stable [v0.76.2 release](https://github.com/Mesh-LLM/mesh-llm/releases/tag/v0.76.2) (via v0.76.1). - Update all eight Mesh git dependencies across desktop and relay smoke examples; both lockfiles resolve all 47 Mesh packages to `a0c1e66b0ac037dd56544b9e2d94969ea43d694f` (tag `v0.76.2`). All non-Mesh lock records are unchanged by the 0.76.1 → 0.76.2 bump. - Match Mesh's `ed25519-dalek = 3.0.0` and `rusqlite = 0.40` dependencies. - Explicitly enable rusqlite's `fallible_uint` feature, preserving Buzz's existing `u64` SQL conversions when **mesh is disabled**. Mesh-enabled builds previously supplied this transitively, so testing both configurations matters. - Temporarily pin **only** the optional `sqlx-sqlite` package to the merged upstream compatibility fix [transact-rs/sqlx#4402](https://github.com/transact-rs/sqlx/pull/4402), revision `94aafe3a68884d923b0798a767c8d7f6cfda89d2`. SQLx 0.9.0 caps `libsqlite3-sys` below 0.38, while Mesh 0.76 requires 0.38. Cargo resolves the conflict even with SQLite disabled. The fix raises that upper bound; remove the patch once released. Buzz's active PostgreSQL driver stays on crates.io SQLx 0.9.0. - Preserve the explicit Linux keyring 3 `crypto-rust` feature so Secret Service sessions retain DH encryption independently of Mesh keyring 4. - Preserve the dedicated examples-only `buzz-mesh-smoke` crate and lifecycle workflow wiring, keeping Mesh/native-runtime dev dependencies out of relay CI builds. - v0.76.1 → v0.76.2 upstream delta: self-updater fix for composed product-v2 bundles (Mesh-LLM/mesh-llm#1844) plus release plumbing — no API changes affecting Buzz. - Merged `origin/main` (`7c789dee0`) without rewriting shared PR history. ### Validation at `ae4adba15` (local macOS ARM64, Hermit toolchain) - **Passed:** `cargo test --locked -p buzz-mesh-smoke --all-targets` (all six example harnesses compile; no live harness execution). - **Passed:** desktop Tauri workspace tests, both default and `--features mesh-llm` graphs (3,296 tests with mesh enabled, 0 failures). - **Passed:** `just desktop-tauri-clippy` (both feature graphs, `-D warnings`), root workspace clippy `-D warnings`, `cargo fmt --all --check`. - **Passed:** `--locked` metadata resolution for both workspaces; lockfile diff audited to touch only Mesh package records. - **Passed:** full pre-push hook suite on the final push (branch-skew, rust-tests, desktop-tauri-checks, file-size-check) — no hooks bypassed. - **Not run:** live Mesh inference/admission smoke or installed-app UI exercise. CI lifecycle smoke coverage applies. Originating Buzz conversation: buzz://message?channel=dffbc117-845a-4c02-bc3f-eaeeefc4deaa&id=670f9c262e1ec0a4df5600b3173a0c5de2abe17e08ba9ccfdd8eff109fadf1a8 --------- Signed-off-by: Thinker <75d8a808fa21bb8d1812e080cf471db601c6e8e6a62dcd516d37531e83a7bb77@meshllm.communities.buzz.xyz> Signed-off-by: Michael Neale <michael.neale@gmail.com> Co-authored-by: Thinker <75d8a808fa21bb8d1812e080cf471db601c6e8e6a62dcd516d37531e83a7bb77@meshllm.communities.buzz.xyz> Co-authored-by: Michael Neale <michael.neale@gmail.com> Co-authored-by: Galadriel <b81c841ed20a8e17caf4e6fac2f6f3bc61d304c84b8394720f6d00f37538c47e@meshllm.communities.buzz.xyz> Co-authored-by: Gu <gu@buzz>
This commit is contained in:
4
.github/workflows/mesh-lifecycle.yml
vendored
4
.github/workflows/mesh-lifecycle.yml
vendored
@@ -12,7 +12,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'crates/buzz-relay/examples/mesh_*.rs'
|
- 'crates/buzz-mesh-smoke/**'
|
||||||
- 'crates/buzz-relay/Cargo.toml'
|
- 'crates/buzz-relay/Cargo.toml'
|
||||||
- 'crates/buzz-admin/**'
|
- 'crates/buzz-admin/**'
|
||||||
- 'crates/buzz-test-client/**'
|
- 'crates/buzz-test-client/**'
|
||||||
@@ -24,7 +24,7 @@ on:
|
|||||||
- '.github/workflows/mesh-lifecycle.yml'
|
- '.github/workflows/mesh-lifecycle.yml'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'crates/buzz-relay/examples/mesh_*.rs'
|
- 'crates/buzz-mesh-smoke/**'
|
||||||
- 'crates/buzz-relay/Cargo.toml'
|
- 'crates/buzz-relay/Cargo.toml'
|
||||||
- 'crates/buzz-admin/**'
|
- 'crates/buzz-admin/**'
|
||||||
- 'crates/buzz-test-client/**'
|
- 'crates/buzz-test-client/**'
|
||||||
|
|||||||
1156
Cargo.lock
generated
1156
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,7 @@ members = [
|
|||||||
"crates/git-sign-nostr",
|
"crates/git-sign-nostr",
|
||||||
"crates/buzz-pair-relay",
|
"crates/buzz-pair-relay",
|
||||||
"crates/buzz-relay-mesh",
|
"crates/buzz-relay-mesh",
|
||||||
|
"crates/buzz-mesh-smoke",
|
||||||
"crates/buzz-dev-mcp",
|
"crates/buzz-dev-mcp",
|
||||||
"crates/buzz-voice",
|
"crates/buzz-voice",
|
||||||
"crates/buzz-backend-kubernetes",
|
"crates/buzz-backend-kubernetes",
|
||||||
@@ -187,3 +188,8 @@ strip = true
|
|||||||
# allowlist for the auth token). Revert to crates.io once #449 lands upstream.
|
# allowlist for the auth token). Revert to crates.io once #449 lands upstream.
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
aws-creds = { git = "https://github.com/tlongwell-block/rust-s3", rev = "c9fce3620dd434c1f810101d672cf384268dbb0f" }
|
aws-creds = { git = "https://github.com/tlongwell-block/rust-s3", rev = "c9fce3620dd434c1f810101d672cf384268dbb0f" }
|
||||||
|
# MeshLLM 0.76 uses rusqlite 0.40 / libsqlite3-sys 0.38. SQLx 0.9.0's
|
||||||
|
# optional SQLite driver still caps libsqlite3-sys below 0.38, which Cargo
|
||||||
|
# resolves even though Buzz only enables PostgreSQL. Pin the merged upstream
|
||||||
|
# compatibility fix until it is released: https://github.com/transact-rs/sqlx/pull/4402
|
||||||
|
sqlx-sqlite = { git = "https://github.com/launchbadge/sqlx", rev = "94aafe3a68884d923b0798a767c8d7f6cfda89d2" }
|
||||||
|
|||||||
10
Justfile
10
Justfile
@@ -516,23 +516,23 @@ mesh-dev-fresh:
|
|||||||
mesh-e2e-hardware:
|
mesh-e2e-hardware:
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
cargo run -p buzz-relay --example mesh_serve_client_smoke
|
cargo run -p buzz-mesh-smoke --example mesh_serve_client_smoke
|
||||||
|
|
||||||
# Three isolated node processes: trusted member joins and infers; stranger is rejected.
|
# Three isolated node processes: trusted member joins and infers; stranger is rejected.
|
||||||
# Uses temp homes and explicit mesh owner keystores. Never reads the Buzz Keychain.
|
# Uses temp homes and explicit mesh owner keystores. Never reads the Buzz Keychain.
|
||||||
mesh-e2e-admission:
|
mesh-e2e-admission:
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
cargo run -p buzz-relay --example mesh_admission_smoke
|
cargo run -p buzz-mesh-smoke --example mesh_admission_smoke
|
||||||
|
|
||||||
# Full hardware confidence suite: routing, owner admission, and real agent inference.
|
# Full hardware confidence suite: routing, owner admission, and real agent inference.
|
||||||
mesh-e2e-confidence:
|
mesh-e2e-confidence:
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
cargo build --release -p buzz-agent -p buzz-dev-mcp
|
cargo build --release -p buzz-agent -p buzz-dev-mcp
|
||||||
cargo run -p buzz-relay --example mesh_serve_client_smoke
|
cargo run -p buzz-mesh-smoke --example mesh_serve_client_smoke
|
||||||
cargo run -p buzz-relay --example mesh_admission_smoke
|
cargo run -p buzz-mesh-smoke --example mesh_admission_smoke
|
||||||
cargo run -p buzz-relay --example mesh_agent_e2e
|
cargo run -p buzz-mesh-smoke --example mesh_agent_e2e
|
||||||
|
|
||||||
# Take desktop screenshots using the mock bridge
|
# Take desktop screenshots using the mock bridge
|
||||||
desktop-screenshot *ARGS:
|
desktop-screenshot *ARGS:
|
||||||
|
|||||||
40
crates/buzz-mesh-smoke/Cargo.toml
Normal file
40
crates/buzz-mesh-smoke/Cargo.toml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[package]
|
||||||
|
name = "buzz-mesh-smoke"
|
||||||
|
description = "Hardware/CI mesh-llm smoke harnesses (examples only; no library or binary ships)"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
# Examples-only crate. These harnesses link the mesh-llm SDK and native
|
||||||
|
# runtime, which is by far the most expensive subtree in the repository.
|
||||||
|
# They live here rather than in `crates/buzz-relay/examples/` because a
|
||||||
|
# dev-dependency feeds every target of its own package: with these harnesses
|
||||||
|
# under buzz-relay, `cargo nextest archive -p buzz-relay` had to build the
|
||||||
|
# whole mesh-llm + skippy + rmcp tree before it could compile a single relay
|
||||||
|
# unit test. That is what pushed the Desktop E2E Relay job past its 30 minute
|
||||||
|
# budget. None of the harnesses reference `buzz_relay::` — they drive the
|
||||||
|
# relay as an external process — so nothing is lost by isolating them.
|
||||||
|
[lib]
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.2", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] }
|
||||||
|
mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.2", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] }
|
||||||
|
# Relay-driven mesh lifecycle smoke (examples/mesh_relay_lifecycle_smoke.rs):
|
||||||
|
# the relay client for discovery notes and the exact ed25519 the mesh owner
|
||||||
|
# keys use for binding verification.
|
||||||
|
buzz-test-client = { path = "../buzz-test-client" }
|
||||||
|
ed25519-dalek = "=3.0.0"
|
||||||
|
nostr = { workspace = true }
|
||||||
|
sha2 = { workspace = true }
|
||||||
|
tokio = { workspace = true }
|
||||||
|
anyhow = { workspace = true }
|
||||||
|
hex = { workspace = true }
|
||||||
|
reqwest = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
uuid = { workspace = true }
|
||||||
10
crates/buzz-mesh-smoke/src/lib.rs
Normal file
10
crates/buzz-mesh-smoke/src/lib.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
//! Mesh smoke harnesses.
|
||||||
|
//!
|
||||||
|
//! This crate deliberately carries no library code. It exists so the
|
||||||
|
//! mesh-llm SDK and native runtime can be a dev-dependency of the smoke
|
||||||
|
//! harnesses in `examples/` without entering `buzz-relay`'s test graph,
|
||||||
|
//! where it added the whole mesh-llm + skippy + rmcp compile to every relay
|
||||||
|
//! unit test build.
|
||||||
|
//!
|
||||||
|
//! See `examples/` for the harnesses and `scripts/ci-mesh-lifecycle-smoke.sh`
|
||||||
|
//! for the CI entry point.
|
||||||
@@ -86,13 +86,10 @@ async-compression = { version = "0.4.42", features = ["tokio", "gzip"] }
|
|||||||
dev = ["buzz-auth/dev"]
|
dev = ["buzz-auth/dev"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"] }
|
# The mesh-llm smoke harnesses moved to `crates/buzz-mesh-smoke`. A
|
||||||
mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"] }
|
# dev-dependency feeds every target of its package, so keeping the mesh-llm
|
||||||
# Relay-driven mesh lifecycle smoke (examples/mesh_relay_lifecycle_smoke.rs):
|
# SDK here forced the entire mesh-llm/skippy/rmcp tree to compile before any
|
||||||
# the relay client for discovery notes and the exact ed25519 the mesh owner
|
# relay unit test could build — the Desktop E2E Relay 30m timeout.
|
||||||
# keys use for binding verification.
|
|
||||||
buzz-test-client = { path = "../buzz-test-client" }
|
|
||||||
ed25519-dalek = "=3.0.0-rc.0"
|
|
||||||
buzz-core = { workspace = true, features = ["test-utils"] }
|
buzz-core = { workspace = true, features = ["test-utils"] }
|
||||||
buzz-auth = { workspace = true, features = ["dev"] }
|
buzz-auth = { workspace = true, features = ["dev"] }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
|
|||||||
834
desktop/src-tauri/Cargo.lock
generated
834
desktop/src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,13 @@ libc = "0.2"
|
|||||||
ctrlc = { version = "3", features = ["termination"] }
|
ctrlc = { version = "3", features = ["termination"] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
keyring = { version = "3.6.3", default-features = false, features = ["sync-secret-service", "vendored"], optional = true }
|
# `crypto-rust` is what selects `EncryptionType::Dh` over `Plain` for the
|
||||||
|
# Secret Service session (keyring-3.6.3/src/secret_service.rs:140-143,333-336),
|
||||||
|
# i.e. Diffie-Hellman transit encryption of secrets over the D-Bus session bus.
|
||||||
|
# Until MeshLLM 0.76 it arrived implicitly via feature unification with
|
||||||
|
# mesh-llm's own keyring 3; mesh-llm now uses keyring 4, so request it here
|
||||||
|
# explicitly rather than inheriting it by accident.
|
||||||
|
keyring = { version = "3.6.3", default-features = false, features = ["sync-secret-service", "crypto-rust", "vendored"], optional = true }
|
||||||
# Used directly (alongside tauri-plugin-notification) so we can hold the posting
|
# Used directly (alongside tauri-plugin-notification) so we can hold the posting
|
||||||
# D-Bus connection open. GNOME 46+ dismisses a notification the moment that
|
# D-Bus connection open. GNOME 46+ dismisses a notification the moment that
|
||||||
# connection is dropped, which the plugin does immediately. Default features
|
# connection is dropped, which the plugin does immediately. Default features
|
||||||
@@ -81,7 +87,7 @@ tauri-plugin-updater = "2"
|
|||||||
tauri-plugin-process = "2"
|
tauri-plugin-process = "2"
|
||||||
infer = "0.19"
|
infer = "0.19"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
ed25519-dalek = "=3.0.0-rc.0"
|
ed25519-dalek = "=3.0.0"
|
||||||
tokio = { version = "1", features = ["fs", "sync", "rt", "macros", "time", "net", "io-util"] }
|
tokio = { version = "1", features = ["fs", "sync", "rt", "macros", "time", "net", "io-util"] }
|
||||||
tokio-tungstenite = { version = "0.29", features = ["rustls-tls-webpki-roots"] }
|
tokio-tungstenite = { version = "0.29", features = ["rustls-tls-webpki-roots"] }
|
||||||
tokio-util = { version = "0.7", features = ["rt"] }
|
tokio-util = { version = "0.7", features = ["rt"] }
|
||||||
@@ -111,14 +117,14 @@ buzz_terminal = { package = "buzz-terminal", path = "crates/buzz-terminal" }
|
|||||||
buzz_ws_client_pkg = { package = "buzz-ws-client", path = "../../crates/buzz-ws-client" }
|
buzz_ws_client_pkg = { package = "buzz-ws-client", path = "../../crates/buzz-ws-client" }
|
||||||
portable-pty = "0.9"
|
portable-pty = "0.9"
|
||||||
iroh = { version = "1.0.3", optional = true }
|
iroh = { version = "1.0.3", optional = true }
|
||||||
mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true }
|
mesh-llm-sdk = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.2", package = "mesh-llm-sdk", default-features = false, features = ["client", "serving"], optional = true }
|
||||||
mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true }
|
mesh-llm-host-runtime = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.2", package = "mesh-llm-host-runtime", default-features = false, features = ["dynamic-native-runtime"], optional = true }
|
||||||
# Model catalog + hardware survey for the Share-compute model picker (same
|
# Model catalog + hardware survey for the Share-compute model picker (same
|
||||||
# diagnose pattern as mesh-console). Lib name of mesh-llm-client is mesh_client.
|
# diagnose pattern as mesh-console). Lib name of mesh-llm-client is mesh_client.
|
||||||
mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-client", optional = true }
|
mesh-llm-client = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.2", package = "mesh-llm-client", optional = true }
|
||||||
mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-node", optional = true }
|
mesh-llm-node = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.2", package = "mesh-llm-node", optional = true }
|
||||||
mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-system", optional = true }
|
mesh-llm-system = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.2", package = "mesh-llm-system", optional = true }
|
||||||
mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.0-rc9", package = "mesh-llm-events", optional = true }
|
mesh-llm-events = { git = "https://github.com/Mesh-LLM/mesh-llm.git", tag = "v0.76.2", package = "mesh-llm-events", optional = true }
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
sha2 = "0.11"
|
sha2 = "0.11"
|
||||||
tar = "0.4"
|
tar = "0.4"
|
||||||
@@ -138,7 +144,8 @@ zip = "8"
|
|||||||
flate2 = "1"
|
flate2 = "1"
|
||||||
sherpa-onnx = "1.12"
|
sherpa-onnx = "1.12"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
rusqlite = { version = "0.37", features = ["bundled"] }
|
# Match MeshLLM's SQLite ABI; keep u64 conversions available without mesh-llm.
|
||||||
|
rusqlite = { version = "0.40", features = ["bundled", "fallible_uint"] }
|
||||||
axum = "0.8"
|
axum = "0.8"
|
||||||
rodio = "0.22"
|
rodio = "0.22"
|
||||||
earshot = "1.0"
|
earshot = "1.0"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# ci-mesh-lifecycle-smoke.sh — relay-driven mesh lifecycle smoke
|
# ci-mesh-lifecycle-smoke.sh — relay-driven mesh lifecycle smoke
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Provisions a membership-gated buzz-relay and runs the full relay-driven
|
# Provisions a membership-gated buzz-relay and runs the full relay-driven
|
||||||
# mesh lifecycle harness (crates/buzz-relay/examples/mesh_relay_lifecycle_smoke.rs):
|
# mesh lifecycle harness (crates/buzz-mesh-smoke/examples/mesh_relay_lifecycle_smoke.rs):
|
||||||
# membership → signed discovery notes → relay-derived allowlist → join →
|
# membership → signed discovery notes → relay-derived allowlist → join →
|
||||||
# inference over QUIC → stranger denied.
|
# inference over QUIC → stranger denied.
|
||||||
#
|
#
|
||||||
@@ -48,7 +48,7 @@ if [[ "${SKIP_BUILD}" == "true" ]]; then
|
|||||||
else
|
else
|
||||||
log "Building relay, admin CLI, and lifecycle harness (profile: ${CARGO_PROFILE})..."
|
log "Building relay, admin CLI, and lifecycle harness (profile: ${CARGO_PROFILE})..."
|
||||||
cargo build --profile "${CARGO_PROFILE}" -p buzz-relay -p buzz-admin -p git-credential-nostr
|
cargo build --profile "${CARGO_PROFILE}" -p buzz-relay -p buzz-admin -p git-credential-nostr
|
||||||
cargo build --profile "${CARGO_PROFILE}" -p buzz-relay --example mesh_relay_lifecycle_smoke
|
cargo build --profile "${CARGO_PROFILE}" -p buzz-mesh-smoke --example mesh_relay_lifecycle_smoke
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ADMIN_BIN="target/${CARGO_PROFILE}/buzz-admin"
|
ADMIN_BIN="target/${CARGO_PROFILE}/buzz-admin"
|
||||||
|
|||||||
Reference in New Issue
Block a user