mirror of
https://github.com/block/buzz.git
synced 2026-10-30 22:11:36 +00:00
db7da12f0efccbc5909fb1394de94751d9323e68
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bfc3848558 |
fix(desktop): shared npub identity foundation (canonicalNpub, PubKey gate, strict parser) (#7488)
🤖 ## Summary Identity keys in the desktop app are displayed as raw 64-character hex. A person's key shows up as something like `953d3363…` — unreadable, impossible to recognize as the same identity on another screen, and a hazard when copied by hand. Nostr (the protocol Buzz runs on) has a human-readable spelling for identity keys — the `npub1…` form — but the desktop app did not use it consistently. This is the foundation of the desktop npub changes: it adds the shared pieces every identity surface builds on, and two follow-up slices stack directly on this branch — #7489 converts the identity controls (profile, settings, allowlist, workflow key fields) and #7495 converts the everyday display surfaces (mentions, member lists, sidebar, and other name fallbacks). After this change: - The shared identity widget shows the compact npub form — `npub1j57...fjmv` — instead of a hex prefix, everywhere it renders (for example the owned-agent public-key row on a profile). Copying it puts the full npub on the clipboard. - Copy is a real interaction, verified end-to-end: both popover variants put the exact canonical npub on the actual clipboard — never the raw hex the popover also lists, never a truncation — and a portaled popover's clicks no longer steal focus from the new-DM To-field mid-copy. Pointer copy, a natural Space-then-Enter path, and inner/outer Escape are covered. - Anything that isn't a valid identity key fails neutrally: short or corrupt values — including degenerate values that technically encode to a checksum-valid npub but aren't real identity keys — show "Unavailable" with no copy button, instead of a misleading value. - Both valid npub spellings display: all-lowercase `npub1…` and all-uppercase `NPUB1…` (Bech32, npub's encoding, permits either casing) both render the same canonical lowercase npub. Mixed case is rejected by the display path as written — `canonicalNpub` and the widget don't case-normalize input — while input parsing (`parsePubkeyInput`) keeps its trim-and-lowercase normalization and accepts mixed-case npubs; both paths require the decoded payload to be exactly a 64-character identity key. - Identity-key input is strict on payload: an npub whose decoded payload isn't exactly a 64-character identity key is rejected, matching the validation the app's Rust side already applies to agent allowlists. Intentional scope boundary: only surfaces that render through the shared widget change here. Outer profile copy, settings identity cards, the respond-to allowlist, and workflow key fields still show hex — they move to npub in the controls follow-up (#7489). Nothing else changes identity representation: display names, private keys, event IDs, and the hex the app stores, sends, and matches internally are untouched; only the user-facing spelling of an identity key changes. ## Details - `desktop/src/shared/lib/pubkey.ts` — `canonicalNpub()`: strict canonical full-npub helper (64-char hex in any case, or a checksum-validated npub, returns the canonical npub; anything else returns `null`); `truncateNpub()`: the compact display form; existing exports unchanged. - `desktop/src/shared/ui/PubKey.tsx` — the shared widget's identity gate validates through `canonicalNpub`; the popover copies the npub only. - `desktop/src/shared/lib/nostrUtils.ts` — `parsePubkeyInput` rejects npubs whose payload is not exactly a 64-character identity key. - `desktop/src/features/messages/ui/NewMessageScreen.tsx` — the To-field focuses its search input only for clicks that land inside the field itself, so portaled recipient popovers keep their focus while open (a popover click previously dismissed it mid-copy). - Unit suites cover the helper, widget, and parser (including the degenerate-encode and uppercase regressions); the e2e specs that render these rows assert the npub display. ### Related issue - Fixes: N/A. Searched existing issues/PRs for npub identity display — no existing match. - Stack: #7489 is based on this branch and builds on these primitives; it does not stand alone on main. ### Testing At head `b3310c248` (base: main `44316ff72`; 12 files, +440/−39): - Focused unit suites (pubkey, PubKey, parsePubkeyInput): 20/20 green; mutation-checked — removing the decoded-length predicate fails the short/empty checksum-valid-npub assertions in `canonicalNpub` and the widget, and a wrong-identity clipboard value fails the new copy assertions. - `pnpm typecheck` and `pnpm check`: pass; full desktop unit suite 6459/6459 at this exact head. - Targeted e2e at this exact head: 8/8 across the two specs that own the clipboard flows — `agent-access-warning.spec.ts` (compact variant, agent-access owner hint) and `pubkey-display-screenshots.spec.ts` (full variant, new-DM recipient verification: pointer copy, popover surviving the copy, inner/outer Escape, Space-then-Enter). - No Rust-side or build files change in this PR, so those results are unaffected. ### Task provenance Buzz channel: `1f0e4a3d-7e01-4efe-bb16-843b357f85c9` Task: buzz://message?channel=1f0e4a3d-7e01-4efe-bb16-843b357f85c9&id=86b34eb4bd84a1472419e9af22636c011c0fe273e3c196f967d7a36996e149b6 --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> |
||
|
|
f716eef437 |
fix(desktop): enforce shared agent access across devices (#6086)
## Summary - discover shared managed agents from authenticated relay directory records instead of treating channel membership as sufficient proof - publish and refresh access-policy changes immediately so running clients converge across machines without a restart or five-minute poll - route profile edits through the exact managed instance and stop/restart runtimes around access changes so unrelated edits cannot silently widen access - keep mention send-time revalidation and Block owner-only build enforcement fail closed - explain invalid custom provider/model configuration instead of leaving Save silently disabled ### Related issue Fixes #3204 ### Known residuals - a brand-new remote agent's first policy record can wait for the bounded directory poll when no authenticated directory coordinate exists yet; send-time mention revalidation remains fail closed - a failed remote-provider policy redeploy is recorded but cannot undeploy the older provider instance until the provider protocol gains the destructor tracked by #5570 ### Testing - full Desktop unit suite: 4,961 tests passed - focused profile editor Playwright workflow passed, including Customize access edits and prompt-only edits after tightening an instance - Desktop TypeScript, Biome formatting, file-size ratchet, Tauri checks, and pre-push suites passed - independently reviewed for authenticated directory trust, live subscription teardown, runtime revocation ordering, fail-open edit paths, and per-agent provider deployment serialization --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Signed-off-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@buzz.block.builderlab.xyz> Co-authored-by: diegorumo <diegorumo@gmail.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@buzz.block.builderlab.xyz> |
||
|
|
7fb008f934 |
fix(desktop): explain open agent access (#2561)
## Why Hack-day feedback exposed a dangerous mismatch between the UI and the underlying access model. The `Anyone` respond-to mode appeared as a neutral dropdown choice, while a Buzz agent may act with the files, accounts, and tools available on the machine where it runs. People reasonably read this as sharing a bot in a channel. The current UI did not explain that it can also share the agent's available access. ## What - Reframes `respond-to` as **agent access** in user-facing UI. - Uses plain audience labels: **Only me**, **Anyone**, and **Selected people**. - Warns for **both** sharing modes, not just `Anyone` — `Selected people` also hands host access to someone other than the owner, so only the audience phrase differs: > Anyone can use this agent to access your computer, including files, accounts, and connected tools. > Selected people can use this agent to access your computer, including files, accounts, and connected tools. - Names the machine the agent actually runs on. A provider-backed (remote) agent reads: > Anyone can use this agent to access the server it runs on, including any accounts and tools available there. The remote wording deliberately omits the owner's files — those aren't theirs to describe on a host they don't own. - Places the warning below the selector for `Anyone`, but **after** the people picker for `Selected people`, so it never sits between the user and the selection they came to make. - Removes Nostr, harness, pubkey, and `!shutdown` jargon from the primary decision copy. Direct pubkey entry remains available as an advanced path. - Replaces the green open-access avatar dot with an amber warning marker and accessible text. Selected access uses a separate blue status. - Aligns the sidebar action and profile field with the same language. - Records the shared-field disclosure contract in `desktop/src/features/agents/AGENTS.md` so future surfaces do not silently omit it. ## Design decisions **Persistent inline warning, not a confirmation modal.** The setting does not autosave; the consequence remains visible beside the selection until the person chooses **Save access**. This gives the information before commitment without adding a dismiss-and-confirm ritual that would repeat in every create/edit surface. **An unknown run location falls back to the local wording.** It does not hedge with "computer or server". A remote host requires an installed `buzz-backend-*` provider, and without one `WhereToRunSection` never renders — so "server" would name a concept the owner has never been shown. When it *is* remote, they picked that host from the selector themselves. Surfaces never synthesize a run location they don't have. **One resolution site, published through context.** `AgentDialog` resolves the run location (`runLocationForBackend` from `ManagedAgent.backend`, `runLocationForRunOn` from the create flow's `WhereToRunDraft`) and publishes it via `AgentRunLocationContext`. It is not threaded as a prop through `AgentDefinitionDialog` (1047 lines) or `AgentInstanceEditDialog` (1228 lines) — neither uses the value, and both are already over the file-size ceiling. Surfaces outside that tree (`EditRespondToDialog`) pass the prop directly. The copy follows the writing system's guidance for high-sensitivity decisions: lead with the material consequence, use plain actor/action language, keep helper text adjacent and persistent, and never rely on color alone. ## Scope Desktop only. The web and mobile clients do not currently expose this setting. No protocol, gate, runtime, persistence, or backend behavior changes. This does not add team-scoped remote agents. It makes the current local-or-remote access model honest while that product work remains separate. ## Validation - `pnpm exec biome check` and `pnpm exec tsc --noEmit` — clean - `lib/agentAccessWarning.test.mjs` (8/8) — every mode × run-location copy variant, both resolvers, unknown-reads-as-local, blank `runOn` is not a provider - `ui/respondToFieldContract.test.mjs` (8/8) — plain labels, both warning positions, source-order guard that the `allowlist` warning follows the picker, helper-not-inline-copy guard - `agent-access-warning.spec.ts` (3/3) — native local, provider-backed remote (asserts the server sentence and *not* "your computer"), persona-backed edit; includes a bounding-box check that the `Selected people` warning renders below the picker --------- Signed-off-by: David Hamilton <daveh@squareup.com> Signed-off-by: Clay Delk <clay.delk@gmail.com> Co-authored-by: Clay Delk <clay.delk@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |