Skip to content
Merged
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
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions docs/channels.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ the app-owned sidebar or session data.

The broker uses the existing authorized Buzz identity in the OS secret store (macOS
Keychain, Linux secret service) and
signs authenticated reads and channel messages in Node. No private key reaches browser JavaScript; there is
a bounded message-signing and publishing endpoint. The broker is restricted to loopback hosts, same-origin
POSTs, valid Nostr kinds/event IDs, and bounded filters. Without a configured `BUZZ_DEV_VIEWER` pin, the shell and Messages empty state remain available, while the live identity/join flow explains that it needs the development broker. Packaged builds do not include the development broker.
signs authenticated reads and channel messages in Node. In this broker mode no
private key reaches browser JavaScript; there is a bounded message-signing and
publishing endpoint. The broker is restricted to loopback hosts, same-origin
POSTs, valid Nostr kinds/event IDs, and bounded filters. Without a configured
`BUZZ_DEV_VIEWER` pin, native macOS offers [identity setup](identity.md); web and
unsupported native platforms retain the unavailable shell. The separate native
import/reveal/copy UI deliberately passes private strings through JavaScript.
Packaged builds do not include the development broker. A saved native identity
does not enable relay access: join and community profile editing stay unavailable.
The broker supports explicitly scoped typed relay origins;
see [destination routing and trust limits](communities.md#development-broker-boundary).
This is not a new native login.
Expand Down
14 changes: 9 additions & 5 deletions docs/communities.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,14 @@ publishing, media and live traffic, uses that session's destination path. A send
started in A continues in A even after B becomes selected. Connection generations
still fence obsolete work within a session; they are not persistent storage keys.
Channel-head persistence now includes community origin as well as viewer, rather
than relying solely on the relay signing key. Identity keys never enter browser
JavaScript; local preferences contain the public viewer ID only.
than relying solely on the relay signing key. In development broker mode signing keys never enter browser
JavaScript; local preferences contain the public viewer ID only. The app-owned
[native identity UI](identity.md) has deliberate import/reveal/copy interactions,
not a plugin key service.

This is the development integration, not a native identity/join implementation.
Packaged builds do not include the broker. Account import, community
Packaged builds do not include the broker. Native macOS [identity import/create](identity.md)
is available as a separate first slice, without packaged relay transport. Community
creation/removal and background connection eviction are not implemented. Native
agent enrollment has its own [local control contract](agent-control.md). Avatar
uploads reuse the development media host; packaged human-profile publication is
Expand Down Expand Up @@ -143,8 +146,9 @@ This establishes **trusted-app-origin intent, not a human gesture**; same-origin
plugins and local processes remain trusted, not sandboxed. User-directed HTTPS
networking may reach internal/private destinations. This is not a public-only
network policy or DNS-rebinding defense; TLS verification remains enabled.
No CSP widening, private key exposure to JavaScript, or native identity adapter
is included.
This broker integration does not expose private keys to JavaScript or widen CSP.
The separate [native identity adapter](identity.md) deliberately exports keys for
backup; same-origin plugin JavaScript is trusted and can invoke that IPC too.

## Verification

Expand Down
78 changes: 78 additions & 0 deletions docs/identity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Packaged human identity — first slice

Native macOS without the live development broker asks the user to **Use an existing
key** or **Create a new identity**. These are alternatives. Import accepts an nsec,
validates its checksum and secp256k1 scalar, and persists that exact key before
adopting its public identity. It never generates a replacement after an import,
read or write failure. Denied/unavailable/corrupt storage is not first run.

One native owner keeps the key in memory after successful restore. The new
create-only Keychain item is service `dev.local.buzz.foundation.identity` in release
builds, or `dev.local.buzz.foundation.identity.debug` with Rust debug assertions,
account `human`, in the default macOS file Keychain. Debug worktrees share the debug
item, not the release identity; ports and frontend dev mode do not isolate it. It uses the same security-framework
primitives as the existing agent adapter, but does not use or change agent
credentials or old Buzz's `buzz-desktop/secrets` blob. A competing item refuses
overwrite. There is no file/environment fallback, automatic legacy migration,
key replacement or delete command. Windows/Linux storage is explicitly unavailable
in this slice; those platforms skip onboarding and keep the unavailable shell.
No user key belongs in release configuration.

A shared credential blob can reduce repeated OS prompts by caching many credentials
after one read. For this one human key, one cached item retains that read-once benefit without
coupling human writes to agent credentials or old-app blob writers. This is not a
claim that per-secret storage is always superior or that prompts are eliminated.
Consent, app signing and update behavior require attended native verification.

## UI and sensitive data

Settings exposes Profile in Personal space as well as in a selected community.
Identity details remains available if the community profile cannot load. The full
npub and hex public key can be copied. The nsec is absent from the rendered field
until Reveal; Copy can fetch it without revealing it. Hide, leaving Profile,
window blur and document hiding retire pending reveals and clear the displayed
secret. Failed private-key actions report fixed errors. Copy deliberately leaves
the key on the OS clipboard; the UI warns about that.

The app UI passes a private string through IPC only during explicit import/export
interaction. JavaScript/IPC string memory is not guaranteed zeroized. Native
key bytes and temporary storage buffers use zeroizing owners, but this is not a
claim that every framework allocation is wiped. No secret is put in public
snapshots, plugin service registrations, localStorage, logs or relay events.
The main app and its same-origin plugins are trusted, not isolated security
principals; plugin JavaScript can invoke `identity_export` directly. Not registering
a plugin key service is an API ownership choice, not a sandbox. The main-webview
command permission is not proof of a human gesture.

## Deliberately not live-ready

A public native viewer hydrates the existing public-key-scoped local profile and
memberships. Native sessions do **not** fall through to the dev broker signer.
Packaged authenticated relay/HTTP/media transport is not implemented here. Identity
readiness is separate from `relayAvailable`: join and community profile editing
show unavailable states, and saved-community icon discovery makes no broker call.
Local profile editing and identity backup remain available. Remote profile
publishing and messaging are not established by this slice.

Development with `VITE_BUZZ_LIVE=1` continues to use its pinned legacy broker
identity, and does not offer native private-key controls. Creating/importing the
new native item does not update that old blob. Future reset/rotation would not
synchronize copies automatically; neither operation is in this scope.

## Try the UI without credentials

Use the existing environment-free fixture Vite config:

```sh
bin/pnpm exec vite --config tests/fixtures/agent-control.vite.mjs --port 1547
```

Open `/tests/fixtures/identity.html`. It uses mock IPC and an in-memory public
fixture key, not Keychain or a relay. Import accepts only the displayed fixture
key. Reset and simulated restart affect fixture state only. **Never enter a real
nsec.** Browser exercises prove UI behavior, not secure native persistence.

Before real-key use or a usable-release claim: independent custody review,
isolated native consent/denial/import/create/restart checks, human UI feedback,
and installed-app live read/send/receipt/restart acceptance are still required.
Do not launch/restart someone's desktop app or inspect their credentials to test.
31 changes: 18 additions & 13 deletions docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,21 @@ Settings has two groups:
groups**, **Templates & teams**, and future community-scoped personal or
permission-gated settings.
- **App** contains Appearance, Notifications, Shortcuts, Agents, and Plugins.
These preferences apply across communities on this device.
These preferences apply across communities on this device. In Personal space,
**Profile** is also in App so identity details and the local profile remain
accessible before joining. With a community selected, Profile stays under its
name; the identity details explicitly apply across all communities.

Selecting another community in the rail leaves Settings and opens that
community's view. Opening Settings there captures the new community context; the
contents do not change underneath an open form.

The current Profile implementation still edits a device-local seed and does not
publish to the named community. That is a transitional implementation, not the
final product contract. The community-settings batch must make Profile edit the
captured community profile, update the local seed only after an accepted publish,
and leave other existing community profiles unchanged.
Profile in Personal space edits the device-local default without publishing. With
a live community selected, it edits that captured community profile, confirms the
accepted publish, then updates the local default; other existing community profiles
remain unchanged. Without relay transport the community editor is unavailable,
but public identity details and supported native private-key backup remain
accessible. See [profile behavior](communities.md) and [native identity](identity.md).

## Appearance roadmap

Expand Down Expand Up @@ -93,8 +97,9 @@ when a product decision or complete Buzz 1.0 owner exists.

| Area | Current Buzz 1.0 decision |
| --- | --- |
| Profile details and public identity | Implemented as a device-local default; existing community profiles remain independent. |
| Identity backup, sign out, and delete data | Pending a security-reviewed identity and destructive-data lifecycle. |
| Profile details and public identity | Local default in Personal space; selected-community profile with live transport. Public identity applies across communities. |
| Identity backup | Native macOS nsec Reveal/Hide/Copy implemented; private export enters UI memory deliberately. Native persistence/consent acceptance remains pending; see [identity](identity.md). |
| Sign out and delete data | Pending a security-reviewed destructive-data lifecycle; not offered by identity backup. |
| Color mode and text size | Implemented as personal device preferences across communities. |
| Conversation density, link previews, and thread layout | Approved as future personal device preferences; modes not yet implemented. |
| Theme style, accent color, and native glass | Undecided; do not imply a user-selectable theme system from design tokens alone. |
Expand All @@ -105,7 +110,7 @@ when a product decision or complete Buzz 1.0 owner exists.
| Agent runtimes and inherited defaults | Separate future native-agent product decisions; individual configuration remains on the Agents page. |
| Voice, custom emoji, local archive, and channel templates | Pending dedicated product and implementation slices. |
| Compute, experiments, mobile pairing, and updates | Pending dedicated native/app capability owners. |
| Community profiles and administration | Planned for the Communities list-detail architecture below. |
| Community profiles and administration | Live community profile editing is implemented; permission-gated administration requires its own capability owner. |

Do not add empty destinations or functional-looking placeholders for pending rows.

Expand Down Expand Up @@ -136,10 +141,10 @@ authorization owners:

## Current implementation boundary

This change establishes the selected community and **App** groups, places the
existing Profile and contributed community cards under the community name, and
places agent conversation behavior under **Agents**. It does not yet change the
local-only Profile persistence contract or add permission-gated community
Settings has selected-community and **App** groups, with contributed community
cards under the community name and agent conversation behavior under **Agents**.
Profile is available in either context as described above. Native identity setup
and backup do not provide packaged relay transport or permission-gated community
administration.

Add new community entries only with real behavior and honest unavailable,
Expand Down
5 changes: 5 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ tauri-plugin-opener = "2"
tauri-plugin-deep-link = "2"
tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
uuid = { version = "1", features = ["v4"] }
bech32 = "0.11"
secp256k1 = { version = "0.31", default-features = false, features = ["std"] }
getrandom = "0.3"
zeroize = "1.9"

[dev-dependencies]
tempfile = "3"
Expand All @@ -42,6 +46,7 @@ portable-pty = "0.9"
libc = "0.2"

[target.'cfg(target_os = "macos")'.dependencies]
security-framework = "3.7"
mac-notification-sys = "=0.6.15"
block2 = "=0.6.2"
objc2 = "=0.6.4"
Expand Down
4 changes: 4 additions & 0 deletions src-tauri/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ fn main() {
}
tauri_build::try_build(
attributes.app_manifest(tauri_build::AppManifest::new().commands(&[
"identity_restore",
"identity_import",
"identity_create",
"identity_export",
"plugin_import_folder",
"plugin_import_git",
"plugin_import_install",
Expand Down
4 changes: 4 additions & 0 deletions src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"identifier": "main-window",
"description": "Allow the main application commands, title bar controls and external HTTP(S) links.",
"permissions": [
"allow-identity-restore",
"allow-identity-import",
"allow-identity-create",
"allow-identity-export",
"allow-plugin-import-folder",
"allow-plugin-import-git",
"allow-plugin-import-install",
Expand Down
4 changes: 4 additions & 0 deletions src-tauri/src/browser_permissions_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ fn native_command_permissions_allow_only_main_webview() {
.unwrap();

let application_commands = [
"identity_restore",
"identity_import",
"identity_create",
"identity_export",
"plugin_import_folder",
"plugin_import_git",
"plugin_import_install",
Expand Down
Loading
Loading