Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ jobs:
- '.github/workflows/ci.yml'
- name: Release workflow source contract
run: scripts/test-release-ref-contract.sh
- name: Buzz read packaging contract
run: scripts/test-buzz-read-packaging.sh
- name: Relay image eligibility contract
run: scripts/test-relay-image-eligibility-workflow.sh
- name: Desktop release candidate contract
Expand Down Expand Up @@ -1206,6 +1208,7 @@ jobs:
touch "desktop/src-tauri/binaries/buzz-backend-kubernetes-$TARGET"
touch "desktop/src-tauri/binaries/buzz-dev-mcp-$TARGET"
touch "desktop/src-tauri/binaries/git-credential-nostr-$TARGET"
touch "desktop/src-tauri/binaries/buzz-read-$TARGET"
touch "desktop/src-tauri/binaries/buzz-$TARGET"
# Mesh rev is derived from Cargo.lock so a dependency bump needs no
# lockstep edit here; the cache key tracks it automatically.
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/linux-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ jobs:
- name: Build sidecars
run: |
cargo build --release -p buzz-acp -p buzz-agent -p buzz-backend-kubernetes -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --manifest-path desktop/src-tauri/Cargo.toml --target-dir target --release --bin buzz-read
./scripts/bundle-sidecars.sh

- name: Build Linux Tauri app
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/macos-intel-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ jobs:
- name: Build Intel sidecars
run: |
cargo build --release --target "$TARGET" -p buzz-acp -p buzz-agent -p buzz-backend-kubernetes -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --manifest-path desktop/src-tauri/Cargo.toml --target-dir target --release --target "$TARGET" --bin buzz-read
./scripts/bundle-sidecars.sh "$TARGET"

- name: Build unsigned Intel DMG
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ jobs:
- name: Build sidecars
run: |
cargo build --release -p buzz-acp -p buzz-agent -p buzz-backend-kubernetes -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --manifest-path desktop/src-tauri/Cargo.toml --target-dir target --release --bin buzz-read
./scripts/bundle-sidecars.sh

# Mesh rev derived from Cargo.lock (no lockstep edit on dep bump); cache key tracks it.
Expand Down Expand Up @@ -309,6 +310,7 @@ jobs:
- name: Build sidecars
run: |
cargo build --release --target "$TARGET" -p buzz-acp -p buzz-agent -p buzz-backend-kubernetes -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --manifest-path desktop/src-tauri/Cargo.toml --target-dir target --release --target "$TARGET" --bin buzz-read
./scripts/bundle-sidecars.sh "$TARGET"

- name: Build unsigned Tauri app
Expand Down Expand Up @@ -564,6 +566,7 @@ jobs:
- name: Build sidecars
run: |
cargo build --release -p buzz-acp -p buzz-agent -p buzz-backend-kubernetes -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --manifest-path desktop/src-tauri/Cargo.toml --target-dir target --release --bin buzz-read
./scripts/bundle-sidecars.sh

- name: Generate release config
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/signed-macos-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ jobs:
- name: Build sidecars
run: |
cargo build --release -p buzz-acp -p buzz-agent -p buzz-backend-kubernetes -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli
cargo build --manifest-path desktop/src-tauri/Cargo.toml --target-dir target --release --bin buzz-read
./scripts/bundle-sidecars.sh

# Mesh rev derived from Cargo.lock (no lockstep edit on dep bump); cache key tracks it.
Expand Down
4 changes: 2 additions & 2 deletions desktop/src-tauri/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ use crate::huddle::HuddleState;
pub(crate) use crate::identity_storage::{IdentityStorage, RecoveryState, ResolvedIdentity};
use crate::managed_agents::config_bridge::SessionConfigCache;
use crate::managed_agents::{ManagedAgentPairRuntime, ManagedAgentRuntimeKey};

pub struct AppState {
pub keys: Mutex<Keys>,
/// Durable backend holding `keys`. Updated after the key write and before
/// recovery flags are cleared so `get_identity` reports a consistent state.
pub(crate) identity_storage: AtomicU8,
pub(crate) identity_generation: AtomicU64,
pub http_client: reqwest::Client,
/// A no-redirect client for authenticated relay media fetches (download,
/// clipboard copy, snapshot, editor). Every caller pre-validates the URL
Expand Down Expand Up @@ -188,10 +188,10 @@ pub fn build_app_state() -> AppState {
}
None => (Keys::generate(), IdentityStorage::Ephemeral),
};

AppState {
keys: Mutex::new(keys),
identity_storage: AtomicU8::new(identity_storage as u8),
identity_generation: AtomicU64::new(0),
http_client: reqwest::Client::builder()
.resolve("localhost", std::net::SocketAddr::from(([127, 0, 0, 1], 0)))
.pool_idle_timeout(std::time::Duration::from_secs(300))
Expand Down
10 changes: 10 additions & 0 deletions desktop/src-tauri/src/bin/buzz-read.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[cfg(unix)]
fn main() {
std::process::exit(buzz_lib::run_operator_read_cli(std::env::args_os()));
}

#[cfg(not(unix))]
fn main() {
eprintln!("buzz-read: this local control surface is available on Unix systems only");
std::process::exit(1);
}
9 changes: 8 additions & 1 deletion desktop/src-tauri/src/commands/agents_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ fn deploy_payload_matches_the_shared_full_launch_fixture() {
}

#[test]
fn tauri_platform_configs_bundle_kubernetes_only_on_supported_hosts() {
fn tauri_platform_configs_bundle_unix_only_sidecars_on_supported_hosts() {
use tauri_utils::{config::parse::read_from, platform::Target};

let config_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
Expand All @@ -558,10 +558,17 @@ fn tauri_platform_configs_bundle_kubernetes_only_on_supported_hosts() {
let has_kubernetes = external_bins
.iter()
.any(|value| value == "binaries/buzz-backend-kubernetes");
let has_operator_read = external_bins
.iter()
.any(|value| value == "binaries/buzz-read");
assert_eq!(
has_kubernetes, expected,
"unexpected Kubernetes externalBin for {target}; merged {paths:?}"
);
assert_eq!(
has_operator_read, expected,
"unexpected buzz-read externalBin for {target}; merged {paths:?}"
);
}
}

Expand Down
2 changes: 2 additions & 0 deletions desktop/src-tauri/src/identity_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ impl AppState {
pub(crate) fn set_identity_storage(&self, storage: IdentityStorage) {
self.identity_storage
.store(storage as u8, std::sync::atomic::Ordering::Release);
self.identity_generation
.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
}
}

Expand Down
25 changes: 25 additions & 0 deletions desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ mod native_websocket_batch;
mod nostr_bind;
pub mod nostr_convert;
mod observed_unread;
#[cfg(unix)]
mod operator_read;
mod persona_catalog;
mod prevent_sleep;
mod ptt_shortcut;
Expand Down Expand Up @@ -84,6 +86,9 @@ use managed_agents::{
};
#[cfg(not(feature = "mesh-llm"))]
use mesh_llm_stubs::*;
#[cfg(unix)]
#[doc(hidden)]
pub use operator_read::run_operator_read_cli;
#[cfg(all(feature = "mesh-llm", target_os = "macos"))]
use shutdown::{hard_exit_after_mesh_shutdown, relaunch_after_mesh_shutdown};
use shutdown::{is_restart_request, shut_down_app};
Expand Down Expand Up @@ -410,6 +415,18 @@ pub fn run() {
let is_dev_nest = managed_agents::nest_dir()
.and_then(|p| p.file_name().map(|n| n.to_os_string()))
.is_some_and(|n| n == ".buzz-dev");

// Keep authenticated operator reads inside the already-running
// production Desktop process that owns the in-memory identity.
// The external `buzz-read` client is credentialless and can only
// submit a bounded read request through this owner-only socket.
// Start only after ensure_nest() has created the socket parent.
#[cfg(unix)]
if !recovery_mode && !is_dev_nest {
if let Err(error) = operator_read::start_operator_read_server(app_handle.clone()) {
eprintln!("buzz-desktop: operator read service unavailable: {error}");
}
}
if !reset_outcome.completed && is_dev_nest {
migration::migrate_dev_nest();
}
Expand All @@ -421,6 +438,14 @@ pub fn run() {
if let Err(error) = managed_agents::ensure_cli_symlink(parent, is_dev_nest) {
eprintln!("buzz-desktop: failed to create CLI symlink: {error}");
}
#[cfg(unix)]
if operator_read::is_trusted_production_owner(&app_handle) {
if let Err(error) = operator_read::ensure_client_symlink(parent) {
eprintln!(
"buzz-desktop: failed to create Buzz read client symlink: {error}"
);
}
}
}
}

Expand Down
Loading