diff --git a/loop/plan.md b/loop/plan.md index 374ef812..7ad06ddc 100644 --- a/loop/plan.md +++ b/loop/plan.md @@ -474,7 +474,7 @@ - [x] **IDENT-01** — Auto-generate Nostr keypair during identity creation. In `core/archipelago/src/identity_manager.rs` `create()` method, after generating the Ed25519 keypair, immediately call `create_nostr_key()` on the same identity so every identity gets both Ed25519 (DID) and secp256k1 (Nostr) keys from creation. Update the `IdentityInfo` struct returned by `identity.create` and `identity.list` RPC to always include `nostr_pubkey` (hex) and `nostr_npub` (bech32) fields when present. **Acceptance**: Call `identity.create`, then `identity.get` — response includes both `did` and `nostr_npub`. Deploy and verify. -- [ ] **IDENT-02** — Update onboarding to show DID + npub. In `neode-ui/src/views/OnboardingDid.vue`, after fetching the node DID, also fetch `node.nostr-pubkey` (already exists as RPC endpoint). Display both: "Your DID: did:key:z..." and "Your Nostr ID: npub1..." with copy buttons for each. Add a brief explanation: DID for Web5/federation, npub for Nostr apps. Store `nostr_npub` in localStorage alongside `neode_did`. **Acceptance**: Fresh onboarding flow shows both DID and npub on the identity screen. Deploy and verify at http://192.168.1.228. +- [x] **IDENT-02** — Update onboarding to show DID + npub. In `neode-ui/src/views/OnboardingDid.vue`, after fetching the node DID, also fetch `node.nostr-pubkey` (already exists as RPC endpoint). Display both: "Your DID: did:key:z..." and "Your Nostr ID: npub1..." with copy buttons for each. Add a brief explanation: DID for Web5/federation, npub for Nostr apps. Store `nostr_npub` in localStorage alongside `neode_did`. **Acceptance**: Fresh onboarding flow shows both DID and npub on the identity screen. Deploy and verify at http://192.168.1.228. - [ ] **IDENT-03** — Wire real signature verification in onboarding. In `neode-ui/src/views/OnboardingVerify.vue`, replace `generateMockSignature()` with a real call to `rpcClient.signChallenge(challenge)`. Generate a random challenge string, send it to the backend, display the real Ed25519 signature. Add a "Verify" button that calls `identity.verify` with the DID, challenge, and signature to prove the node controls its keys. Show green checkmark on success. **Acceptance**: Onboarding verify step shows real cryptographic signature and verification succeeds. Deploy and verify. diff --git a/neode-ui/src/views/OnboardingDid.vue b/neode-ui/src/views/OnboardingDid.vue index b7edca4e..7d0de3bc 100644 --- a/neode-ui/src/views/OnboardingDid.vue +++ b/neode-ui/src/views/OnboardingDid.vue @@ -69,9 +69,30 @@ -

- This is your sovereign digital identity. It proves you are you, without any company in the middle. -

+

For Web5, federation, and verifiable credentials

+ + + +
+

Your Nostr ID

+
+

+ {{ nostrNpub }} +

+ +
+

For Nostr social apps and NIP-07 signing

@@ -104,10 +125,12 @@ import { rpcClient } from '@/api/rpc-client' const router = useRouter() const generatedDid = ref('') +const nostrNpub = ref('') const isGenerating = ref(false) const waitingForServer = ref(false) const autoAdvancing = ref(false) const didCopied = ref(false) +const npubCopied = ref(false) const elapsedSeconds = ref(0) const elapsedDisplay = ref('0:00') let retryTimer: ReturnType | null = null @@ -147,6 +170,15 @@ async function fetchDid() { storeDidState(did, pubkey) isGenerating.value = false waitingForServer.value = false + + // Fetch Nostr npub in parallel (non-blocking) + rpcClient.getNostrPubkey().then(({ nostr_npub }) => { + if (nostr_npub) { + nostrNpub.value = nostr_npub + localStorage.setItem('neode_nostr_npub', nostr_npub) + } + }).catch(() => { /* Nostr key may not exist yet */ }) + autoAdvanceAfterDelay() } catch { isGenerating.value = false @@ -167,6 +199,8 @@ function autoAdvanceAfterDelay() { onMounted(() => { const cached = localStorage.getItem('neode_did') + const cachedNpub = localStorage.getItem('neode_nostr_npub') + if (cachedNpub) nostrNpub.value = cachedNpub if (cached && !cached.includes('...')) { generatedDid.value = cached } else { @@ -194,6 +228,13 @@ function copyDid() { didCopied.value = true setTimeout(() => { didCopied.value = false }, 2000) } + +function copyNpub() { + if (!nostrNpub.value) return + navigator.clipboard.writeText(nostrNpub.value).catch(() => {}) + npubCopied.value = true + setTimeout(() => { npubCopied.value = false }, 2000) +}