- Restore the standard primary CTA treatment for general onboarding steps.
- Add a dedicated inverted CTA style for dark backup-security surfaces.
- Apply the white button treatment only to backup creation, file selection, verification, and password-reset actions.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Give shared in-step primary actions a white background with dark labels.
- Add coordinated hover colors that preserve contrast across onboarding surfaces.
- Update the shared CTA documentation to describe the inverted treatment.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Add a key-to-password-to-lock timeline to the onboarding backup form.
- Animate four connector dots in a downward pulse with reduced-motion support.
- Preserve the compact settings variant and hide decoration in short windows.
- Keep balanced spacing and semantic security-theme colors across the illustration.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Increase contrast for the spotlight password field border and focus ring against its white surface.
- Apply dark default and hover colors to the password generator and visibility controls.
- Remove the obsolete dark-surface icon treatment from the light password field.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Give the spotlight password field a white background, subtle border, rounded corners, and dark password and placeholder text.
- Keep the saved-password mask legible against the light input surface.
- Render password generator controls in the standard popover surface instead of the dark textured security treatment.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Use the resolved identity storage backend to name the system keychain in the initial backup guidance.
- Keep local-file and unknown storage states accurate without exposing boot-time identity creation.
- Explain in the backup options card that the computer may request the user's password when Buzz needs to read the key.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Add generated compact light and dark noisy surfaces with reusable tone and size options for cards, popovers, and confirmation dialogs.
- Keep the dark backup option and password panels unboxed, reduce their padding, and top-align parallel card titles for a clearer visual hierarchy.
- Explain identity keys and password-protected backup files in plain language while preserving distinct device, password-manager, and portable-backup choices.
- Normalize onboarding primary, secondary, and icon controls and add reduced-motion-safe transitions between backup creation and verification stages.
- Update focused onboarding E2E coverage for current navigation, compact texture contracts, title alignment, and narrow viewport geometry.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Track whether the active identity is stored in the system keyring, local fallback file, environment, or ephemeral memory.
- Expose storage metadata through the Tauri identity response and shared TypeScript identity model.
- Add a responsive first information card beside the two backup actions without revealing boot-time identity creation.
- Show keychain password education only when secure storage succeeded and accurate neutral copy for fallback states.
- Extend identity persistence tests and split storage types into focused modules to preserve file-size limits.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Replace the password-backup surface's onboarding Skip and Next actions with a dedicated Back control.
- Keep Backup key and Back in the existing bottom-docked CTA location while preserving inline settings behavior.
- Route Back to Backup options with the correct backward transition instead of advancing onboarding.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Replace the standalone password-backup step with copy and encrypted-file options inside the identity-key stage.
- Add a midnight security theme, return-to-onboarding control, and downward transition back into the core yellow flow.
- Restore seven-page onboarding pagination and keep backup progress available when users revisit the security view.
- Update onboarding E2E coverage and screenshots for the inline options, direct setup path, and password-backup return behavior.
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
**Category:** improvement
**User Impact:** Users can create a password-protected key backup, save
it locally, and prove that it works before finishing onboarding or
signing out.
**Problem:** Tyler's NIP-49 foundation made encrypted local backup
possible, but the end-to-end ceremony still needed clearer progression,
trustworthy verification, and a Settings treatment consistent with the
rest of Identity. **Solution:** This stack adds a guided
create/download/test flow, verifies the actual saved backup through Rust
without returning secret key material, minimizes password lifetime in
the webview, and restores warm, plain-language presentation across
onboarding and Settings.
> [!NOTE]
> This PR is intentionally stacked on #2937 (`eva/nip49-local-backup`).
Review only the 19 commits in this stack.
<details>
<summary>File changes</summary>
**desktop/src-tauri/src/commands/identity.rs**
Adds off-thread verification of NIP-49 backups and returns public
identity details only.
**desktop/src-tauri/src/commands/identity_key_backup_tests.rs**
Covers verification, identity matching, and failure behavior at the
command boundary.
**desktop/src-tauri/src/egress_guard_tests.rs**
Keeps the NIP-49 source inventory aligned with the new verification
path.
**desktop/src-tauri/src/key_backup.rs**
Supports real backup decryption for verification while zeroizing
submitted passwords.
**desktop/src-tauri/src/key_backup_tests.rs**
Exercises valid, wrong-password, damaged, and different-identity
backups.
**desktop/src-tauri/src/lib.rs**
Registers the verification command with the desktop runtime.
**desktop/src/features/communities/ui/WelcomeSetup.tsx**
Routes community onboarding through the revised backup ceremony.
**desktop/src/features/onboarding/lib/encryptedBackup.test.mjs**
Covers the hardened backup state model and password-clearing behavior.
**desktop/src/features/onboarding/lib/encryptedBackup.ts**
Models creation and verification with opaque request correlation and
short-lived passwords.
**desktop/src/features/onboarding/ui/BackupStep.tsx**
Presents the backup choice clearly and advances into the dedicated
download step.
**desktop/src/features/onboarding/ui/BackupTestFlow.tsx**
Adds the polished select-file, enter-password, verification, error, and
success experience.
**desktop/src/features/onboarding/ui/CommunityOnboardingFlow.tsx**
Connects community onboarding to the updated backup steps.
**desktop/src/features/onboarding/ui/DownloadKeyStep.tsx**
Adds the dedicated encrypted-backup download step and pre-creation skip
escape hatch.
**desktop/src/features/onboarding/ui/EncryptedBackupCreator.tsx**
Guides password generation, backup creation, native saving, safe retry,
and re-download.
**desktop/src/features/onboarding/ui/MachineOnboardingFlow.tsx**
Sequences chooser, download, exact-file test, and setup progression.
**desktop/src/features/onboarding/ui/NostrKeyImportForm.tsx**
Aligns encrypted-key import behavior with the backup flow.
**desktop/src/features/onboarding/ui/NsecMaskedDisplay.tsx**
Improves masked/revealed key presentation and safely wraps long private
keys.
**desktop/src/features/onboarding/ui/OnboardingChrome.tsx**
Supports the revised onboarding layout and transitions.
**desktop/src/features/onboarding/ui/SetupStep.tsx**
Integrates the completed backup ceremony with final setup.
**desktop/src/features/onboarding/ui/onboardingFlowSteps.test.mjs**
Updates flow-level assertions for the new step sequence.
**desktop/src/features/settings/ui/EncryptedBackupRow.tsx**
Adds sibling Create and Test rows that match the surrounding Identity
settings rhythm.
**desktop/src/features/settings/ui/ProfileSettingsCard.tsx**
Places password-backup controls in the Identity card.
**desktop/src/features/settings/ui/SignOutSection.tsx**
Uses explicit backup self-attestation plus the existing wipe phrase
without requiring a raw-key reveal first.
**desktop/src/shared/api/tauriIdentity.ts**
Exposes typed backup verification to the frontend.
**desktop/src/shared/lib/ncryptsecSourceScan.test.mjs**
Updates the frontend source allowlist for the verification path.
**desktop/src/testing/e2eBridge.ts**
Models creation, saving, and verification for browser coverage.
**desktop/tests/e2e/onboarding-backup.spec.ts**
Covers backup creation, exact-file testing, errors, retries, and
completion.
**desktop/tests/e2e/onboarding-docked-cta-screenshots.spec.ts**
Updates docked CTA visual coverage for the revised progression.
**desktop/tests/e2e/onboarding.spec.ts**
Keeps broader onboarding coverage aligned with the backup ceremony.
**desktop/tests/e2e/profile-nsec-reveal.spec.ts**
Covers Settings create/test behavior and updated success copy.
**desktop/tests/e2e/signout-confirmation.spec.ts**
Covers backup self-attestation, wipe phrase confirmation, and reset
behavior.
**desktop/tests/helpers/fileDrag.ts**
Adds reusable file-drop support for backup test coverage.
**desktop/tests/helpers/onboarding.ts**
Routes tests through chooser, download, and skip behavior consistently.
</details>
## Reproduction steps
1. Start fresh onboarding and choose the password-protected backup path.
2. Continue to **Backup your key**, create a password, save the
`.ncryptsec` file, and confirm that **Next** remains gated until testing
succeeds.
3. Select that exact file, enter its password, and verify the **Your
backup works!** success state.
4. Open **Settings → Identity** and confirm that **Create password
backup** and **Test password backup** appear as sibling rows.
5. Test a valid backup, then test wrong-password and different-identity
cases to confirm clear, non-secret-bearing outcomes.
6. Open sign out and confirm that backup self-attestation plus typing
`wipe all my data` remain required.
## Screenshots
### Onboarding flow
| 1. Creating your key | 2. Key created | 3. Create backup password |
|---|---|---|
| <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/fe5ebf60-02ad-4584-8e64-ff137bafac02"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/0b8c7c6c-2ae5-494e-bb81-0a7b47ea1b66"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/9fa8f6c0-832a-46ec-9af8-55dcae0adbb8"
/> |
| **4. Select the saved backup** | **5. Enter its password** | **6.
Verification succeeds** |
| <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/7ed1528c-6a1f-4411-bd3e-c57413320cdf"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/088ec3ed-2735-4b9a-a0c5-6d176b3f06c5"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/7081ce32-9371-46b2-9692-23ff77c39d35"
/> |
### Settings flow
| Create and Test tools | Tested backup success |
|---|---|
| <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/978ab672-5510-46d8-9116-6df4f1bda34c"
/> | <img width="1280" height="720" alt="image"
src="https://github.com/user-attachments/assets/56a1764d-3c3e-40e2-9f4b-962c5e96a36c"
/> |
## Verification
- Desktop typecheck
- Full desktop JS: 3,733/3,733
- `pnpm check`
- Rust desktop lib: 1,862 passed, 14 ignored; diagnostic: 3/3
- Focused Playwright: 16/16 with one worker
Native save/cancel remains the residual smoke-test risk: browser E2E
mocks the KDF/native save-dialog boundary, while real decrypt and
identity matching are covered in Rust.
---------
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
Max's clean-room pass found the wipe -> import-from-file leg unreachable:
the file input accepted only .key,text/plain (macOS greys out the
identity.ncryptsec our own save dialog emits), and the first-launch
spotlight variant rendered no file input at all, leaving paste-only
recovery exactly where a returning user holds a backup file.
Accept .ncryptsec in the shared hidden file input, hoist it out of the
default-variant branch, and give the spotlight variant an 'Import from
a file' button wired to the same picker. Drop-zone behavior in the
default variant is unchanged. E2E covers the spotlight file path end to
end (accept attribute asserted explicitly, since setInputFiles bypasses
it) through decrypt-and-continue.
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Tyler's spec names the settings entry "Password Backup". Rename the
row title and align its description (password, not passphrase) plus
the two doc comments that referenced the old row name. No behavior
change; test ids unchanged.
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
## Context
Adding an existing community by relay URL could fail with `Community
rejected: Load failed` even when its WebSocket endpoint was reachable.
The Add Community flow fetched `/api/join-policy` from the WebView, so a
relay without a matching CORS allowance blocked the policy request
before the app could join it.
## Summary
This bug fix fetches join policies through Tauri's native networking
layer for direct URL joins. Invite-code discovery, policy acceptance,
and signed invite claims remain on the WebView path so those operations
can migrate together later.
## Changes
- Uses native networking for Add Community and first-community direct
URL join-policy requests.
- Validates relay schemes, rejects URLs containing credentials, and
refuses redirects.
- Bounds declared and chunked native responses before JSON parsing.
- Preserves existing `404`, non-success status, malformed JSON, and
absent-policy behavior.
- Requires every join-policy caller to choose its transport explicitly.
Public relays using Buzz's default permissive CORS configuration are not
known to be affected.
### Related issue
Related to #2872.
### Testing
#### Reviewer-reproducible examples
End-to-end red/green requires a relay with restrictive CORS and a Buzz
identity authorized to join it.
##### Red: `main`
From a clean checkout of `main`:
```bash
. ./bin/activate-hermit
just staging
```
In Buzz Desktop:
1. Add another community so the restrictive-CORS relay can be removed.
2. Remove that relay.
3. Open Add Community and enter the relay's WebSocket URL.
4. Select Add Community.
Observed result:
```text
Community rejected: Load failed
```
##### Green: this PR
From a clean checkout of this branch:
```bash
. ./bin/activate-hermit
just staging
```
Repeat the same steps above.
Observed result:
```text
The community rejoins successfully.
```
Supporting checks:
- Six native join-policy tests, including oversized declared and chunked
responses.
- Four TypeScript API tests, including the native command contract.
- E2E build and four focused onboarding and sidebar Playwright tests.
- Full `just ci` and pre-push suites.
- Builderbot, Kalvin, and minimize-diff review fanout found no
actionable issues after the final rebase.
**Category:** improvement
**User Impact:** Users can understand, install, authenticate, and manage
agent runtimes from one progressively disclosed Agents settings
experience.
**Problem:** Runtime health and custom harness management were split
across overlapping settings surfaces, exposing low-level configuration
too early while leaving setup and authentication states hard to
understand. **Solution:** Consolidate those operations into one stable
runtime list and an Add runtimes catalog, with task-oriented state
labels, direct setup actions, and custom configuration contained in a
dedicated form.
<details>
<summary>File changes</summary>
**desktop/playwright.config.ts**
Registers the visual coverage needed for the redesigned runtime catalog.
**desktop/public/harness-logos/CREDITS.md**
Documents bundled runtime-mark provenance and the decision not to ship
the withdrawn OpenAI mark.
**desktop/public/runtime-icons/codex.png**
Removes the obsolete Codex bitmap in favor of the neutral fallback.
**desktop/public/runtime-icons/goose.svg**
Removes the old Goose asset now replaced by the theme-adaptive mark.
**desktop/src-tauri/src/managed_agents/discovery.rs**
Aligns runtime discovery guidance with the new task-oriented setup
language.
**desktop/src-tauri/src/managed_agents/discovery/runtime_metadata.rs**
Updates runtime metadata used by the redesigned settings states.
**desktop/src/features/agents/ui/runtimeAvailabilityWarning.test.mjs**
Updates availability-warning expectations for the revised runtime
guidance.
**desktop/src/features/onboarding/assets/harness-logos/chatgpt.png**
Removes the redundant bitmap from the unified runtime icon pipeline.
**desktop/src/features/onboarding/assets/harness-logos/goose.png**
Removes the redundant Goose bitmap.
**desktop/src/features/onboarding/ui/HarnessMarks.tsx**
Adds theme-adaptive bundled runtime marks with safe fallbacks.
**desktop/src/features/onboarding/ui/RuntimeIcon.tsx**
Centralizes runtime logo rendering so settings and catalog rows cannot
drift.
**desktop/src/features/onboarding/ui/SetupStep.tsx**
Aligns onboarding runtime setup copy with the settings terminology.
**desktop/src/features/onboarding/ui/presetLogos.test.mjs**
Guards bundled-logo behavior and prevents the withdrawn Codex mark from
returning.
**desktop/src/features/settings/ui/CustomHarnessForm.tsx**
Reworks custom runtime creation and editing into a clear, dedicated
catalog form.
**desktop/src/features/settings/ui/HarnessCatalogDialog.tsx**
Introduces the Add runtimes master-detail catalog, grouped setup states,
loading treatment, and pinned actions.
**desktop/src/features/settings/ui/HarnessManagementCard.tsx**
Removes the superseded standalone custom-harness management surface.
**desktop/src/features/settings/ui/HarnessRow.tsx**
Provides stable operational runtime rows with install, update,
authentication, edit, and delete behavior.
**desktop/src/features/settings/ui/HarnessesSettingsPanel.tsx**
Consolidates runtime health and custom management into one Agents
settings panel.
**desktop/src/features/settings/ui/SettingsPanels.tsx**
Wires the consolidated panel into Agents settings.
**desktop/src/features/settings/ui/harnessCatalogCopy.ts**
Adds restrained, source-annotated runtime descriptions and setup
guidance.
**desktop/src/features/settings/ui/harnessCatalogLogic.test.mjs**
Covers grouping, state labels, stable row order, actions, and adapter
warnings.
**desktop/src/features/settings/ui/harnessCatalogLogic.ts**
Centralizes catalog grouping, actions, status labels, and runtime-safe
warning copy.
**desktop/src/features/settings/ui/harnessGalleryLogic.test.mjs**
Removes obsolete gallery-only tests after consolidation.
**desktop/src/features/settings/ui/harnessGalleryLogic.ts**
Retains only the shared custom-runtime safety logic needed by the new
surface.
**desktop/src/shared/ui/config-nudge-attachment.tsx**
Points configuration nudges to Agent runtimes with matching terminology.
**desktop/src/testing/e2eBridge.ts**
Adds deterministic runtime states for authentication and catalog E2E
coverage.
**desktop/tests/e2e/doctor-states.spec.ts**
Verifies ready, setup-required, node-gated, authentication, loading, and
error contracts.
**desktop/tests/e2e/harness-catalog-screenshots.spec.ts**
Captures whole-pane visual states for the runtime catalog experience.
**desktop/tests/e2e/harness-management.spec.ts**
Exercises catalog actions and custom runtime create, edit,
authentication, and deletion flows.
**desktop/tests/e2e/onboarding-agent-defaults.spec.ts**
Updates default-runtime expectations for the consolidated experience.
**desktop/tests/e2e/profile.spec.ts**
Aligns profile navigation assertions with the new settings surface.
</details>
## Reproduction steps
1. Open Settings → Agents and inspect Agent runtimes; ready, signed-out,
installable, and setup-required runtimes should have stable rows and
explicit actions.
2. Open Add runtimes and browse the Setup and Installed groups; select
entries to see sourced guidance and a pinned Install or Setup guide
action.
3. Select Custom harness, create a runtime, then edit and delete it;
verify required-field gating and the blast-radius confirmation.
4. Exercise a signed-out runtime and connect it; the row should move
from Sign-in needed to Ready without reordering.
5. Resize the window and switch themes to verify responsive layout and
adaptive bundled marks.
## Screenshots
| Mixed runtime states | Goose not installed | Sign-in needed |
|---|---|---|
| <img width="980" height="1000" alt="image"
src="https://github.com/user-attachments/assets/28705b39-1d91-440a-a954-fb2d7d8ee759"
/> | <img width="980" height="1000" alt="image"
src="https://github.com/user-attachments/assets/e65ce8e1-8c85-4272-afb5-e6001b94d560"
/> | <img width="980" height="1000" alt="image"
src="https://github.com/user-attachments/assets/3987f355-960d-4744-8c26-b3746944a33c"
/> |
| Add runtimes catalog | Custom runtime | Setup guide |
|---|---|---|
| <img width="896" height="672" alt="image"
src="https://github.com/user-attachments/assets/a2f32c8a-4dff-4929-af96-b07346933335"
/> | <img width="896" height="672" alt="image"
src="https://github.com/user-attachments/assets/b2e85a0c-3940-4875-a138-4035423c9de7"
/> | <img width="896" height="672" alt="image"
src="https://github.com/user-attachments/assets/5a690c1c-368e-4ee3-98b9-ffefdc3cf804"
/> |
Full 13-state screenshot matrix and review evidence:
https://buzz.block.builderlab.xyz/channels/c5af9e3e-4317-4853-b3e3-ed9c15bc511d?event=ec88f6e472f2b08d6318ae76fd754fd4f218385f67cfe59b73034c4bc34c9252
---------
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
## Problem
With macOS in dark mode (the default for fresh profiles is to follow the
system scheme), the onboarding "What's an identity key?" help dialog
renders
its title in near-white on the always-white textured card, making it
unreadable. The body paragraphs stay readable because they use the fixed
olive `--buzz-onboarding-backup-ink`; only the `text-foreground` title
(and
the close button's hover color) flip with the theme.
## Cause
The dialog's `DialogContent` carries `buzz-onboarding-neutral-theme` but
is
portaled outside the `buzz-startup-shell` subtree, so in dark mode it
matches
`.dark .buzz-onboarding-neutral-theme:not(.buzz-startup-shell)`
(`components.css`), which flips `--foreground` to `0 0% 98%`. The
textured
powder card (`buzz-card-textured`) has no dark variant — it is baked
light —
so the near-white title disappears against it.
## Fix
One attribute: pin the dialog to the light neutral theme with
`data-system-color-scheme="light"`. This is the established pattern for
always-light onboarding dialogs (`HostedCommunityOnboarding.tsx`, the
`CommunityOnboardingFlow.tsx` avatar dialog), and the pinned-light CSS
rule
already exists and out-specifies the dark-mode flip. No new CSS.
## Testing
- Added a dark-mode regression test to
`tests/e2e/identity-key-help.spec.ts`
(already registered in the Playwright smoke project): emulates
`prefers-color-scheme: dark`, opens the dialog, and asserts the title
resolves to the pinned light-neutral ink `rgb(23, 23, 23)`. Before the
fix
it rendered `rgb(250, 250, 250)`.
- Manual repro: macOS appearance set to Dark → fresh profile → machine
onboarding → click "What's an identity key?".
Before/after screenshots are in the comment below.
Signed-off-by: Michael Pfister <pfista@gmail.com>
## Summary
- backing out of the backup step and clicking the CTA again reuses the
existing key
- the button still said "Create a new identity key", which is misleading
for a security-sensitive action
- when a key is already loaded, label it "Continue with existing
identity key"
Closes#2318
## Test plan
- [ ] first-run: button still says "Create a new identity key"
- [ ] create key → backup → back: button says "Continue with existing
identity key" and shows the same key
Made with [Cursor](https://cursor.com)
---------
Signed-off-by: Taksh <takshkothari09@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
> [!NOTE]
> **Part 1 of a multi-PR onboarding rework.** This PR only restores the
hidden harnesses, adjusts the card layout, and gates onboarding
completion on valid config. A follow-up PR (stacked on this branch)
restructures the flow: the harness page becomes a single-choice "pick
your default harness" step, and install/sign-in/provider setup moves to
the following page. Review this one on its own terms — the flow rework
is intentionally not here.
## Summary
- Restore **Goose** and **Buzz Agent** to onboarding harness selection,
reverting the launch-only restriction from #2233
- The restore is the single centralized allowlist
(`ONBOARDING_RUNTIME_ORDER` in `onboardingRuntimeSelection.ts`) that
#2233 deliberately set up for this moment — setup cards, readiness
handoff, and the defaults harness picker all derive from it
- Lay the four harness cards out as a single row at `lg` and above
(`lg:grid-cols-4`); below 1024px (including the app's 800px minimum
window width) the grid is 2×2, and 1-up on narrow viewports
- **Gate onboarding Finish on actual config validity** (review finding):
the defaults page rendered provider/model/credential fields for
provider-required harnesses but Finish only checked that a harness was
selected — a fresh user picking Buzz Agent could persist a default that
fails at first spawn. The Finish gate now consumes `AgentConfigFields`'
existing `onValidityChange` signal. Baked build env and runtime-file
config satisfy the gate, so internal builds and existing Goose users are
never blocked
- Update the unit + E2E specs that pinned the hidden behavior, plus two
new E2E cases pinning the Finish gate (blocked-until-configured, and
baked-env never blocked)
- AGENTS.md rule 7 updated to document the completion gate
## Testing
- `onboardingRuntimeSelection.test.mjs` — 4 passed
- `pnpm typecheck` — clean
- `pnpm exec playwright test tests/e2e/onboarding-agent-defaults.spec.ts
--project=smoke` — 21 passed
- `onboarding-docked-cta-screenshots.spec.ts` — 3 passed
- Biome — clean
## Known cosmetic issue (deferred to PR 2)
At the app's minimum window size (800×500) the 2×2 grid extends past the
visible area and the footer CTA overlaps card space. Next still
hit-tests correctly. PR 2 redesigns this page entirely (cards become a
single-choice chooser with no inline setup), so this is deferred rather
than patched twice.
---------
Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
Blocker 1: import_identity persisted-before-cleanup ordering. New
commit_imported_identity helper runs durable persistence FIRST; a failed
different-key import now leaves both the old in-memory identity and its
valid canonical identity.ncryptsec intact. Stale-backup cleanup runs after
the durable commit and is deliberately best-effort (logged, not surfaced
as a half-applied-import error). Regression tests cover the failure path
and prove cleanup cannot precede persist.
Blocker 2: the /events inventory tripwire is now site-granular. Each
inventoried file pairs an expected non-comment /events occurrence count
with an expected egress-guard call count, so an unguarded ninth site in an
already-listed file (or a deleted guard call) fails the scan. The scan
core is pure over (path, content) pairs, with mutation-style tests
demonstrating all three drift classes.
Hardening: bech32 permits an all-uppercase encoding, so the egress guard
now rejects NCRYPTSEC1... too (mixed case stays unblocked - it cannot
decode), and both import classifiers (Rust recover_keys_from_input, TS
classifyKeyImportInput/isPlausibleNcryptsec) route uppercase-valid blobs
to the encrypted path consistently.
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Frontend half of PLANS/NIP49_LOCAL_BACKUP_PLAN.md Rev 3 (D3):
- BackupStep: encrypted by default. The default path never invokes
get_nsec — the webview only ever sees the finished ncryptsec1 blob
returned by create_ncryptsec_backup. The raw key path survives behind
an explicit 'Show raw key instead' click with its previous
loading/error/skip semantics.
- EncryptedBackupCreator (shared by onboarding + settings): generated
6-word passphrase default with cannot-be-recovered copy, 'choose my
own' = min 12 chars + confirmation, create -> masked display + copy +
'Save a copy…' via the native dialog.
- encryptedBackup.ts: pure reducer/validation model, unit-tested without
a DOM; keyImportInput.ts: HRP classification + submit gating.
- NsecMaskedDisplay: kind='nsec'|'ncryptsec' drives labels/aria/testids;
existing nsec call sites unchanged.
- NostrKeyImportForm: ncryptsec1 paste switches to encrypted mode
(passphrase field, no npub preview possible), decrypt errors surface;
raw nsec flow untouched. import_identity plumbs the optional password
through OnboardingFlow/MachineOnboardingFlow/KeyringLockedScreen.
- ProfileSettingsCard: 'Encrypted backup' row alongside the raw reveal.
- ncryptsecSourceScan.test.mjs: TS-side source-allowlist tripwire
mirroring the Rust scan (defense-in-depth per plan D4).
- e2e: mock bridge learns the three backup commands + ncryptsec import;
onboarding-backup.spec.ts covers the encrypted happy path (asserting
get_nsec is never called), custom-passphrase validation, raw fallback,
and error/retry; onboarding.spec.ts adds encrypted-import happy path
including a wrong-passphrase rejection.
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
When Desktop stops or restarts a managed-agent pair, the harness is
SIGKILLed after 1s and `turn_completed` never lands.
`activeAgentTurnsStore` then sees the "all turns silent at once" pattern
and delays badge cleanup for up to 3 min (the pause that protects live
badges during transient relay-stream gaps). This is unnecessary when
Desktop itself issued the kill — there is no relay-gap ambiguity.
## What changed
**`activeAgentTurnsStore.ts`** — new `clearActiveTurnsForAgent(pubkey)`
- Tombstones every live turn for the agent via `recordTerminal` (blocks
in-flight `turn_liveness` frames from resurrecting them via
`resurrectTurn`)
- Removes the agent's entry from `activeTurnsByAgent`
- Preserves `lastProcessed` (watermark) — full-buffer replay after the
clear is a no-op
- Preserves `clockOffsetByAgent` — still valid, harmless
**`managedAgentRuntimeHooks.ts`** — clearing at the successful-stop
boundary
- New `clearActiveTurnsForAgentOnStop(pubkey, relayUrl?)` — relay-scope
gate: only clears when the stopped pair's relay matches the active
community (pair-scoped), or when an active community is configured
(agent-wide ops)
- New `restartManagedAgentPair(pubkey, relayUrl, stop, clear, start)` —
dependency-injected stop → clear → start sequence; the `restart` branch
of `useManagedAgentRuntimeAction`'s `mutationFn` is a single call into
it. The clear fires after a successful stop and before start begins, so
the badge is gone even when start fails, a failed stop clears nothing,
and no clear can run after the new process is spawned (genuinely-new
turns are never wiped)
- `useManagedAgentRuntimeAction.onSuccess` clears for `stop` actions,
before the query-cache update
**`managedAgentControlActions.ts`** — `onStopped` callback on
`respawnManagedAgentWithRules`, invoked after the stop promise resolves
and before start begins
**`welcomeKickoff.ts`** — same `onStopped` boundary on
`restartWelcomeTeammate`
**Call sites covered (all stop/restart UI paths):**
- `useManagedAgentRuntimeAction` — pair-scoped stop (`onSuccess`) and
restart (`restartManagedAgentPair` in `mutationFn`); Members-sidebar +
settings card
- `useMembersSidebarActions.handleRespawnAll` — via `onStopped`
- `useMembersSidebarActions.handleStopAll` — direct local-stop branch
- `useMembersSidebarActions.handleLifecycleAction` — local-stop fallback
branch
- `useAgentLifecycleActions.handleAgentPrimaryAction` — Agents-tab stop
- `useAgentLifecycleActions.handleAgentRestart` — via `onStopped`
- `useManagedAgentActions.handleStop` / `handleBulkStopRunning` — Agents
screen
- `useAutoRestartPolicy` — inline, between stop and start
- `restartWelcomeTeammate` call site — via `onStopped`
Provider agents are excluded at each site: they go through `!shutdown`
(relay message), not a direct harness kill.
## Tests
Twelve behavior tests across three files:
- `activeAgentTurnsStore.test.mjs` (6) — clear removes the agent's turns
and notifies subscribers, other agents untouched; full-buffer replay
after clear is a no-op (watermark preserved); late `turn_liveness` frame
with timestamp ≤ clear time does not resurrect (tombstone); new
`turn_started` after clear is tracked normally; badge gone when stop
succeeds even if start fails; new frame during start-pending does not
resurrect the cleared badge
- `managedAgentControlActions.test.mjs` (3) — `onStopped` fires on
stop-success/start-failure; does not fire on stop-failure; strict stop →
`onStopped` → start ordering
- `managedAgentRuntimeHooks.test.mjs` (3) — pair-restart seam: clear ran
when start fails (rejection propagates); stop failure invokes neither
clear nor start; strict stop → clear → start ordering
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Why
Installing the Codex, Claude, or Goose desktop app does not install the
command-line harness Buzz needs. The current UI makes that distinction
unclear, links some missing-CLI states to adapter documentation, and can
report a successful install from the installer exit code even when
runtime discovery still fails. On Windows, Buzz also invokes Goose's
Bash installer, which writes the executable somewhere Buzz does not
discover.
## What
- distinguish missing vendor CLIs from missing or outdated ACP adapters
in runtime metadata and UI guidance
- link Codex, Claude Code, and Goose missing-CLI states to their
official CLI installation documentation
- explain in Settings, onboarding, and agent configuration that the
desktop app alone is not sufficient
- use Goose's official PowerShell installer on Windows
- refresh PATH and rediscover the requested runtime after installation,
keeping the control retryable if the runtime is still unavailable
- add Rust and Playwright regression coverage for Windows installer
selection, CLI/adapter guidance, false-success prevention, verified
installs, and onboarding copy
## Risk Assessment
Medium. This changes desktop onboarding and runtime installation
behavior. Successful installs now require the runtime catalog to verify
availability; previously hidden discovery failures will surface as
actionable errors instead of a false success state.
## References
- [Codex CLI installation](https://developers.openai.com/codex/cli/)
- [Claude Code
installation](https://code.claude.com/docs/en/getting-started)
- [Goose
installation](https://goose-docs.ai/docs/getting-started/installation/)
- Follow-up to #2563 and #2587
## Validation
- `just desktop-typecheck`
- `just desktop-test` — 3,455 passed
- focused Rust post-install verification tests
- focused Playwright Doctor/onboarding coverage (in progress; CI and
local sequential rerun will provide final results)
Generated with Codex
---------
Signed-off-by: Atish Patel <atish@squareup.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Goose <opensource@block.xyz>