mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
## Problem Managed agents in internal Buzz builds should answer only their owner. Previously, an agent could keep a broader access setting and respond to other people, which did not match the access policy for internal builds. This PR makes owner-only access effective for every managed agent in internal builds and makes that restriction clear in the Desktop UI. Open source builds remain configurable. ## Changes - Enforce owner-only access when any managed agent starts or is deployed from an internal build. - Show the agent access control as locked to **Only me** in Desktop, with an explanation of why it cannot be changed. - Keep Welcome teammates working under the same rule without triggering unnecessary restarts. - Leave open source build behavior unchanged. This changes effective runtime access without rewriting stored or relay-advertised settings. The companion [#4064](https://github.com/block/buzz/pull/4064) explains the restriction in-thread when someone without access mentions an agent. The enforcement will remain inactive in shipped builds until [squareup/buzz-releases#74](https://github.com/squareup/buzz-releases/pull/74) marks internal releases during the build. ## Screenshots | Before | After | | --- | --- | |  |  | ## Tests Added coverage for: - Runtime enforcement for [locally run agents](https://github.com/block/buzz/blob/e0165f52b52741a74184c9899e2b51eeec40c939/desktop/src-tauri/src/managed_agents/runtime/tests.rs#L196) and [deployed agents](https://github.com/block/buzz/blob/e0165f52b52741a74184c9899e2b51eeec40c939/desktop/src-tauri/src/commands/agents_tests.rs#L510). - The [current-build deployment path](https://github.com/block/buzz/blob/e0165f52b52741a74184c9899e2b51eeec40c939/desktop/src-tauri/src/commands/agents_tests.rs#L455), [invalid stored access](https://github.com/block/buzz/blob/e0165f52b52741a74184c9899e2b51eeec40c939/desktop/src-tauri/src/managed_agents/access_policy.rs#L98), and the [local startup guard](https://github.com/block/buzz/blob/e0165f52b52741a74184c9899e2b51eeec40c939/desktop/src-tauri/src/managed_agents/env_vars/tests.rs#L149). - Consistent enforcement across [both agent backends](https://github.com/block/buzz/blob/e0165f52b52741a74184c9899e2b51eeec40c939/desktop/src-tauri/src/managed_agents/access_policy.rs#L112). - Welcome teammates created as [locally run](https://github.com/block/buzz/blob/e0165f52b52741a74184c9899e2b51eeec40c939/desktop/src/features/onboarding/welcomeGuide.test.mjs#L384) or [deployed](https://github.com/block/buzz/blob/e0165f52b52741a74184c9899e2b51eeec40c939/desktop/src/features/onboarding/welcomeGuide.test.mjs#L393) agents, including [access-only](https://github.com/block/buzz/blob/e0165f52b52741a74184c9899e2b51eeec40c939/desktop/src/features/onboarding/welcomeKickoff.test.mjs#L202) and [runtime-related](https://github.com/block/buzz/blob/e0165f52b52741a74184c9899e2b51eeec40c939/desktop/src/features/onboarding/welcomeKickoff.test.mjs#L225) restart behavior. The full Desktop Rust and JavaScript suites, type checks, formatting, clippy, and file-size checks passed. Playwright E2E was not run. --- Originated from Buzz channel [buzz-agent-control](buzz://channel?id=cf5dada7-e26a-4887-ae41-b3bd5f42d3b2). Supersedes #2537. --------- Signed-off-by: Tom Brow <tomb@block.xyz> Signed-off-by: npub1tquskdu6yc4h8l7xxtceculxw600grekeq0xg2ukqfrwl7vrzg3quz3gmp <58390b379a262b73ffc632f19c73e6769ef40f36c81e642b960246eff9831222@buzz.block.builderlab.xyz> Co-authored-by: npub1tquskdu6yc4h8l7xxtceculxw600grekeq0xg2ukqfrwl7vrzg3quz3gmp <58390b379a262b73ffc632f19c73e6769ef40f36c81e642b960246eff9831222@buzz.block.builderlab.xyz> Co-authored-by: Amp <amp@ampcode.com>
145 lines
6.8 KiB
Rust
145 lines
6.8 KiB
Rust
// Shared schema, included from the same source the runtime command parses with,
|
|
// so the build-time validation below and the runtime parse cannot drift.
|
|
include!("src/commands/reconnect_hook_config.rs");
|
|
// Same source of truth the runtime filters with, so a baked build env cannot
|
|
// carry a reserved key the runtime believes it already rejected.
|
|
include!("src/managed_agents/reserved_env_keys.rs");
|
|
|
|
use base64::Engine as _;
|
|
|
|
fn main() {
|
|
println!("cargo:rerun-if-env-changed=BUZZ_RELAY_URL");
|
|
println!("cargo:rerun-if-env-changed=BUZZ_RELAY_HTTP");
|
|
println!("cargo:rerun-if-env-changed=BUZZ_UPDATER_PUBLIC_KEY");
|
|
println!("cargo:rerun-if-env-changed=BUZZ_UPDATER_ENDPOINT");
|
|
println!("cargo:rerun-if-env-changed=BUZZ_BUILD_BUZZ_AGENT_PROVIDER");
|
|
println!("cargo:rerun-if-env-changed=BUZZ_BUILD_BUZZ_AGENT_MODEL");
|
|
println!("cargo:rerun-if-env-changed=BUZZ_BUILD_AGENT_ENV");
|
|
println!("cargo:rerun-if-env-changed=BUZZ_BUILD_RELAY_RECONNECT_CMD");
|
|
println!("cargo:rerun-if-env-changed=BUZZ_BUILD_AGENT_ACCESS_OWNER_ONLY");
|
|
println!("cargo:rerun-if-env-changed=BUZZ_BUILD_AUTO_CONNECT_DEFAULT_RELAY");
|
|
println!("cargo:rustc-check-cfg=cfg(buzz_updater_enabled)");
|
|
|
|
// Explicit owner-only agent-access capability. Release packaging sets this
|
|
// presence-only marker; OSS/custom builds leave agent access configurable.
|
|
if std::env::var("BUZZ_BUILD_AGENT_ACCESS_OWNER_ONLY").is_ok() {
|
|
println!("cargo:rustc-env=BUZZ_DESKTOP_BUILD_AGENT_ACCESS_OWNER_ONLY=1");
|
|
}
|
|
|
|
if let Ok(relay_url) = std::env::var("BUZZ_RELAY_URL") {
|
|
println!("cargo:rustc-env=BUZZ_DESKTOP_BUILD_RELAY_URL={relay_url}");
|
|
}
|
|
|
|
if let Ok(relay_http) = std::env::var("BUZZ_RELAY_HTTP") {
|
|
println!("cargo:rustc-env=BUZZ_DESKTOP_BUILD_RELAY_HTTP={relay_http}");
|
|
}
|
|
|
|
if let Ok(provider) = std::env::var("BUZZ_BUILD_BUZZ_AGENT_PROVIDER") {
|
|
println!("cargo:rustc-env=BUZZ_DESKTOP_BUILD_BUZZ_AGENT_PROVIDER={provider}");
|
|
}
|
|
|
|
if let Ok(model) = std::env::var("BUZZ_BUILD_BUZZ_AGENT_MODEL") {
|
|
println!("cargo:rustc-env=BUZZ_DESKTOP_BUILD_BUZZ_AGENT_MODEL={model}");
|
|
}
|
|
|
|
// Generic KEY=VALUE pairs to inject into every spawned agent process.
|
|
// Newline-delimited; each line must be non-empty and contain exactly one
|
|
// `=` separator with a non-empty key. OSS builds leave this unset.
|
|
// The validated value is base64-encoded before emitting so the single-line
|
|
// Cargo build-script output carries all pairs (Cargo output is line-oriented;
|
|
// a raw multiline value would be silently truncated to the first line).
|
|
if let Ok(raw) = std::env::var("BUZZ_BUILD_AGENT_ENV") {
|
|
for (line_no, line) in raw.lines().enumerate() {
|
|
let line = line.trim();
|
|
if line.is_empty() {
|
|
continue;
|
|
}
|
|
let eq = line.find('=').unwrap_or_else(|| {
|
|
panic!(
|
|
"BUZZ_BUILD_AGENT_ENV line {}: missing '=' separator in {:?}",
|
|
line_no + 1,
|
|
line
|
|
)
|
|
});
|
|
let key = &line[..eq];
|
|
if key.is_empty() {
|
|
panic!(
|
|
"BUZZ_BUILD_AGENT_ENV line {}: key must not be empty in {:?}",
|
|
line_no + 1,
|
|
line
|
|
);
|
|
}
|
|
// The baked env is written into every spawned agent's environment
|
|
// LAST (see `managed_agents/runtime.rs`), after Buzz sets the
|
|
// access gates and identity vars. A baked reserved key would
|
|
// therefore silently override the gate the UI promises, so reject
|
|
// it at build time instead of shipping a binary that bypasses its
|
|
// own enforcement.
|
|
if is_reserved_env_key(key) {
|
|
panic!(
|
|
"BUZZ_BUILD_AGENT_ENV line {}: `{}` is reserved by Buzz and cannot be baked \
|
|
into a build (it would override Buzz's own identity/access env)",
|
|
line_no + 1,
|
|
key
|
|
);
|
|
}
|
|
}
|
|
let encoded = base64::engine::general_purpose::STANDARD.encode(raw.as_bytes());
|
|
println!("cargo:rustc-env=BUZZ_DESKTOP_BUILD_AGENT_ENV={encoded}");
|
|
}
|
|
|
|
if let Ok(val) = std::env::var("BUZZ_BUILD_RELAY_RECONNECT_CMD") {
|
|
let parsed: serde_json::Value = serde_json::from_str(&val)
|
|
.unwrap_or_else(|e| panic!("BUZZ_BUILD_RELAY_RECONNECT_CMD is not valid JSON: {e}"));
|
|
serde_json::from_value::<ReconnectHookConfig>(parsed).unwrap_or_else(|e| {
|
|
panic!("BUZZ_BUILD_RELAY_RECONNECT_CMD doesn't match ReconnectHookConfig: {e}")
|
|
});
|
|
println!("cargo:rustc-env=BUZZ_DESKTOP_BUILD_RELAY_RECONNECT_CMD={val}");
|
|
}
|
|
|
|
// Presence-only release capability: internal desktop builds opt into
|
|
// auto-connecting their configured default relay on first run. OSS builds
|
|
// leave this unset and retain explicit community selection.
|
|
if std::env::var("BUZZ_BUILD_AUTO_CONNECT_DEFAULT_RELAY").is_ok() {
|
|
println!("cargo:rustc-env=BUZZ_DESKTOP_BUILD_AUTO_CONNECT_DEFAULT_RELAY=1");
|
|
}
|
|
|
|
let updater_public_key = std::env::var("BUZZ_UPDATER_PUBLIC_KEY")
|
|
.ok()
|
|
.map(|value| value.trim().to_string())
|
|
.filter(|value| !value.is_empty());
|
|
let updater_endpoint = std::env::var("BUZZ_UPDATER_ENDPOINT")
|
|
.ok()
|
|
.map(|value| value.trim().to_string())
|
|
.filter(|value| !value.is_empty());
|
|
|
|
if updater_public_key.is_some() && updater_endpoint.is_some() {
|
|
println!("cargo:rustc-cfg=buzz_updater_enabled");
|
|
}
|
|
|
|
// Cargo test executables get no embedded Windows manifest (tauri_build
|
|
// attaches one to bin targets only), so the loader binds comctl32 v5, which
|
|
// lacks TaskDialogIndirect (statically imported via tauri-plugin-dialog/rfd)
|
|
// and debug test exes die at load with STATUS_ENTRYPOINT_NOT_FOUND. Declaring
|
|
// the Common Controls v6 dependency makes link.exe emit a side-by-side
|
|
// <exe>.manifest that the loader honors for manifest-less executables;
|
|
// binaries with an embedded manifest (the real app) ignore it.
|
|
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows")
|
|
&& std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc")
|
|
{
|
|
println!(
|
|
"cargo:rustc-link-arg=/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"
|
|
);
|
|
}
|
|
|
|
tauri_build::try_build(
|
|
tauri_build::Attributes::new().plugin(
|
|
"websocket",
|
|
tauri_build::InlinedPlugin::new()
|
|
.commands(&["connect", "send", "disconnect", "disconnect_all"])
|
|
.default_permission(tauri_build::DefaultPermissionRule::AllowAllCommands),
|
|
),
|
|
)
|
|
.expect("failed to build Tauri application");
|
|
}
|