Skip to content

add encrypted Nostr session sharing#8922

Open
callebtc wants to merge 5 commits intoaaif-goose:mainfrom
callebtc:nostr-export
Open

add encrypted Nostr session sharing#8922
callebtc wants to merge 5 commits intoaaif-goose:mainfrom
callebtc:nostr-export

Conversation

@callebtc
Copy link
Copy Markdown

@callebtc callebtc commented Apr 29, 2026

Overview

Category: new-feature
User Impact: Users can share and import Goose conversations through encrypted Nostr links instead of manually exchanging exported JSON files.
Problem: Session export already supports JSON files but leaves users to manage transport themselves. There was also no direct way for a receiving Goose client to fetch and restore a shared conversation from a link.
Solution: Add an isolated Nostr sharing layer that encrypts the session JSON with NIP-44 v2, publishes it as kind 30278 to configurable relays, and encodes the event reference plus decryption key in a Goose deep link. The CLI, server API, and desktop session UI now reuse that path for exporting, importing, and handling Nostr session deep links.

Changes

File changes

Cargo.lock
Adds the Nostr SDK, NIP-44 crypto, relay-pool, and supporting dependency graph needed to publish and fetch encrypted Nostr events.

crates/goose/Cargo.toml
Adds nostr, nostr-sdk, and an optional rustls dependency wired into the existing rustls-tls feature so the live Nostr client can install a Rustls crypto provider.

crates/goose/src/session/mod.rs
Exports the new isolated nostr_share module from the session domain.

crates/goose/src/session/nostr_share.rs
Implements the reusable core Nostr share/import logic: default/configured relays, NIP-44 v2 encryption/decryption, kind 30278 event creation, nevent encoding, Goose deep-link creation, live publish/fetch clients, and unit tests.

crates/goose-cli/src/cli.rs
Adds CLI flags for goose session export --format json --nostr --relay ... and a new goose session import command that accepts either JSON files or Nostr share links.

crates/goose-cli/src/commands/session.rs
Wires CLI export/import to the shared Nostr module while preserving the existing JSON/YAML/Markdown export behavior.

crates/goose-server/src/routes/session.rs
Adds desktop-facing endpoints for sharing a session to Nostr and importing a Nostr share link back through the normal session import path.

crates/goose-server/src/openapi.rs
Registers the new Nostr session endpoints and schemas in the OpenAPI spec.

ui/desktop/openapi.json
Regenerates the desktop OpenAPI schema with the new Nostr share/import endpoints.

ui/desktop/src/api/index.ts
Regenerates the desktop API exports so UI code can call the new Nostr endpoints.

ui/desktop/src/api/sdk.gen.ts
Regenerates typed API client functions for importSessionNostr and shareSessionNostr.

ui/desktop/src/api/types.gen.ts
Regenerates request/response types for Nostr session import and share responses.

ui/desktop/src/components/sessions/SessionListView.tsx
Adds desktop UI controls to create encrypted Nostr share links from session cards and import a pasted Goose Nostr share link from Chat History.

ui/desktop/src/sessionLinks.ts
Routes goose://sessions/nostr?... deep links through the Nostr import endpoint before returning users to the session list.

Reproduction Steps

  1. Start Goose Desktop and create or use an existing chat session.
  2. Open Chat History and hover a session card.
  3. Click the Nostr share icon to publish the encrypted session event and show a copyable goose://sessions/nostr?... link.
  4. Use Chat History's Import Link button and paste that link.
  5. Confirm the session is fetched from the relays, decrypted, imported, and appears in the session list.
  6. From the CLI, run goose session export --session-id <id> --format json --nostr and verify it prints a Goose Nostr share link.
  7. From another Goose client, run goose session import '<goose://sessions/nostr?...>' and verify it imports the session.

Validation

  • cargo fmt
  • cargo test -p goose nostr_share --lib
  • cargo test -p goose-cli parses_session_export_nostr_relays --lib
  • cargo test -p goose-cli parses_session_import_nostr_link --lib
  • cargo check -p goose-server
  • pnpm run generate-api
  • pnpm run typecheck

@callebtc
Copy link
Copy Markdown
Author

CLI:
image

image

GUI:
image

image

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6c4c3eaa8c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

body: { deeplink: url },
throwOnError: true,
});
setView('sessions');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve target view when handling Nostr deep links

Calling setView('sessions') here assumes the caller honors the view argument, but the desktop deep-link path in App.tsx currently passes a callback that ignores _view and always navigates to /shared-session (openSharedSessionFromDeepLink(link, (_view, options) => navigate('/shared-session', ...))). As a result, opening a goose://sessions/nostr?... link from the OS imports successfully but still lands on the shared-session page instead of session history, which is a broken post-import flow for external deep links.

Useful? React with 👍 / 👎.

Signed-off-by: callebtc <93376500+callebtc@users.noreply.github.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d7ef2cf3a6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +24 to +27
await importSessionNostr({
body: { deeplink: url },
throwOnError: true,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle Nostr deep-link errors in the sessions flow

importSessionNostr is called inside the shared try block, so any Nostr import failure (invalid link, relay fetch/decrypt error, server-side 400/500) falls through to the generic catch path that always navigates to the shared-session error view. That view is specific to share-token links and shows an irrelevant shareToken for goose://sessions/nostr URLs, which breaks the intended import flow for failed Nostr links. Split Nostr handling/error routing from legacy shared-session handling so Nostr failures stay in the sessions import UX.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant