From 4c6c3012d7ecf1577a8b215d80c03a78dd02b8f9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 01:53:18 +0000 Subject: [PATCH 01/18] Initial plan From 3d93734bdcb66cb488acf29494f3ebd9ff05242b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 01:55:17 +0000 Subject: [PATCH 02/18] Fix mnemonic persistence: display once without saving to file Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/pup.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spv_enclave/pup.nix b/spv_enclave/pup.nix index 808ae7b..1ff6b2e 100644 --- a/spv_enclave/pup.nix +++ b/spv_enclave/pup.nix @@ -23,11 +23,11 @@ let fi # Generate a mnemonic with the libdogecoin key management enclave - if [ ! -f "${storageDirectory}/present" ]; then + if [ ! -f "${storageDirectory}/wallet.db" ]; then # YubiKey (TOTP) path { sleep 1; printf '\n'; sleep 1; printf 'y\n'; } | \ SHELL=/run/current-system/sw/bin/bash \ - ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1 | tee "${storageDirectory}/present" + ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1 # Give the TEE a moment sleep 1 From 87e274e1048416eccec99e47557b3a09e5e10abf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 02:09:10 +0000 Subject: [PATCH 03/18] Implement one-time mnemonic display in output.log with clear warnings Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/pup.nix | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/spv_enclave/pup.nix b/spv_enclave/pup.nix index 1ff6b2e..105b404 100644 --- a/spv_enclave/pup.nix +++ b/spv_enclave/pup.nix @@ -24,10 +24,25 @@ let # Generate a mnemonic with the libdogecoin key management enclave if [ ! -f "${storageDirectory}/wallet.db" ]; then - # YubiKey (TOTP) path + # Create output.log and display one-time mnemonic warning + echo "============================================" > "${storageDirectory}/output.log" + echo "⚠️ ONE-TIME MNEMONIC DISPLAY ⚠️" >> "${storageDirectory}/output.log" + echo "============================================" >> "${storageDirectory}/output.log" + echo "IMPORTANT: Save this mnemonic phrase now!" >> "${storageDirectory}/output.log" + echo "This is your ONLY opportunity to see it." >> "${storageDirectory}/output.log" + echo "It will NOT be saved or shown again." >> "${storageDirectory}/output.log" + echo "============================================" >> "${storageDirectory}/output.log" + echo "" >> "${storageDirectory}/output.log" + + # YubiKey (TOTP) path - capture mnemonic to output.log for one-time display { sleep 1; printf '\n'; sleep 1; printf 'y\n'; } | \ SHELL=/run/current-system/sw/bin/bash \ - ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1 + ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1 | tee -a "${storageDirectory}/output.log" + + echo "" >> "${storageDirectory}/output.log" + echo "============================================" >> "${storageDirectory}/output.log" + echo "Mnemonic displayed above. Starting wallet initialization..." >> "${storageDirectory}/output.log" + echo "============================================" >> "${storageDirectory}/output.log" # Give the TEE a moment sleep 1 From 205432f4ed044dd490a9c490c012dc8611379817 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 02:09:34 +0000 Subject: [PATCH 04/18] Document one-time mnemonic display feature in README Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spv_enclave/README.md b/spv_enclave/README.md index 4a6c476..6f3bffa 100644 --- a/spv_enclave/README.md +++ b/spv_enclave/README.md @@ -9,3 +9,14 @@ This pup will install [Libdogecoin SPV](https://github.com/dogecoinfoundation/libdogecoin) as a pup on your node. It will generate a new wallet and start block sync from the last checkpoint. + +## ⚠️ Important: One-Time Mnemonic Display + +**On first initialization**, this pup will display your wallet's mnemonic phrase **ONCE** through the logger output. + +- ✅ The mnemonic is displayed clearly with warning messages +- ✅ This is your **ONLY** opportunity to see and save the mnemonic +- ✅ The mnemonic is **NOT** saved to disk for security reasons +- ✅ If you miss it, you will need to recreate the wallet + +**Save your mnemonic phrase immediately when you see it!** From df9c651d74384ea8d075469b21db375c109fb8f3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 01:37:01 +0000 Subject: [PATCH 05/18] Add graphical mnemonic display as a metric in manifest Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/README.md | 6 ++- spv_enclave/manifest.json | 6 +++ spv_enclave/monitor/monitor.go | 67 ++++++++++++++++++++++++++++++++++ spv_enclave/pup.nix | 4 +- 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/spv_enclave/README.md b/spv_enclave/README.md index 6f3bffa..9dc3031 100644 --- a/spv_enclave/README.md +++ b/spv_enclave/README.md @@ -12,11 +12,15 @@ It will generate a new wallet and start block sync from the last checkpoint. ## ⚠️ Important: One-Time Mnemonic Display -**On first initialization**, this pup will display your wallet's mnemonic phrase **ONCE** through the logger output. +**On first initialization**, this pup will display your wallet's mnemonic phrase **ONCE** in two ways: + +1. **Graphical Display (Metrics)**: The mnemonic appears as a metric in your Dogebox dashboard +2. **Logger Output**: The mnemonic is also displayed in the logger output - ✅ The mnemonic is displayed clearly with warning messages - ✅ This is your **ONLY** opportunity to see and save the mnemonic - ✅ The mnemonic is **NOT** saved to disk for security reasons +- ✅ After being displayed once, the metric will show "[Mnemonic was displayed and should have been saved]" - ✅ If you miss it, you will need to recreate the wallet **Save your mnemonic phrase immediately when you see it!** diff --git a/spv_enclave/manifest.json b/spv_enclave/manifest.json index e883dc5..df1e571 100644 --- a/spv_enclave/manifest.json +++ b/spv_enclave/manifest.json @@ -79,6 +79,12 @@ ], "dependencies": null, "metrics": [ + { + "name": "mnemonic", + "label": "⚠️ Wallet Mnemonic (ONE-TIME DISPLAY)", + "type": "string", + "history": 1 + }, { "name": "chaintip", "label": "Chain Tip", diff --git a/spv_enclave/monitor/monitor.go b/spv_enclave/monitor/monitor.go index 0329efe..ea22323 100644 --- a/spv_enclave/monitor/monitor.go +++ b/spv_enclave/monitor/monitor.go @@ -12,7 +12,10 @@ import ( "time" ) +var storageDirectory = "/storage" + type Metrics struct { + Mnemonic string `json:"mnemonic"` Chaintip string `json:"chaintip"` Balance string `json:"balance"` Addresses string `json:"addresses"` @@ -58,9 +61,47 @@ func fetchEndpoint(endpoint string) (string, error) { return string(body), nil } +// readMnemonic reads the mnemonic from the temporary display file +// Returns the mnemonic on first read, then deletes the file and returns a message +func readMnemonic() string { + mnemonicFile := storageDirectory + "/.mnemonic_display" + viewedFile := storageDirectory + "/.mnemonic_viewed" + + // Check if already viewed + if _, err := os.Stat(viewedFile); err == nil { + return "[Mnemonic was displayed and should have been saved]" + } + + // Check if mnemonic file exists + if _, err := os.Stat(mnemonicFile); os.IsNotExist(err) { + return "[Waiting for wallet initialization...]" + } + + // Read the mnemonic + content, err := os.ReadFile(mnemonicFile) + if err != nil { + log.Printf("Error reading mnemonic file: %v", err) + return "[Error reading mnemonic]" + } + + mnemonic := strings.TrimSpace(string(content)) + + // If mnemonic is empty or too short, don't mark as viewed yet + if len(mnemonic) < 10 { + return "[Generating mnemonic...]" + } + + // Mark as viewed and delete the display file + // This happens after the metric is successfully submitted + return mnemonic +} + func collectMetrics() (Metrics, error) { var metrics Metrics + // Read mnemonic for one-time display + metrics.Mnemonic = readMnemonic() + // Fetch chain tip chaintipStr, err := fetchEndpoint("/getChaintip") if err != nil { @@ -164,6 +205,7 @@ func submitMetrics(metrics Metrics) { } jsonData := map[string]interface{}{ + "mnemonic": map[string]interface{}{"value": metrics.Mnemonic}, "chaintip": map[string]interface{}{"value": metrics.Chaintip}, "balance": map[string]interface{}{"value": metrics.Balance}, "addresses": map[string]interface{}{"value": metrics.Addresses}, @@ -201,6 +243,31 @@ func submitMetrics(metrics Metrics) { body, _ := io.ReadAll(resp.Body) log.Printf("Unexpected status code when submitting metrics: %d", resp.StatusCode) log.Printf("Response body: %s", string(body)) + return + } + + // After successful submission, mark mnemonic as viewed if it was just displayed + markMnemonicAsViewed(metrics.Mnemonic) +} + +// markMnemonicAsViewed marks the mnemonic as viewed and deletes the display file +func markMnemonicAsViewed(mnemonic string) { + // Only mark as viewed if we actually sent a real mnemonic (not a status message) + if !strings.HasPrefix(mnemonic, "[") { + mnemonicFile := storageDirectory + "/.mnemonic_display" + viewedFile := storageDirectory + "/.mnemonic_viewed" + + // Create the viewed marker file + if err := os.WriteFile(viewedFile, []byte("viewed"), 0600); err != nil { + log.Printf("Error creating viewed marker: %v", err) + } + + // Delete the mnemonic display file + if err := os.Remove(mnemonicFile); err != nil { + log.Printf("Error removing mnemonic display file: %v", err) + } else { + log.Println("Mnemonic displayed successfully - file removed for security") + } } } diff --git a/spv_enclave/pup.nix b/spv_enclave/pup.nix index 105b404..3eb7b8e 100644 --- a/spv_enclave/pup.nix +++ b/spv_enclave/pup.nix @@ -34,10 +34,10 @@ let echo "============================================" >> "${storageDirectory}/output.log" echo "" >> "${storageDirectory}/output.log" - # YubiKey (TOTP) path - capture mnemonic to output.log for one-time display + # YubiKey (TOTP) path - capture mnemonic to output.log and temporary metric file { sleep 1; printf '\n'; sleep 1; printf 'y\n'; } | \ SHELL=/run/current-system/sw/bin/bash \ - ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1 | tee -a "${storageDirectory}/output.log" + ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1 | tee -a "${storageDirectory}/output.log" | tee "${storageDirectory}/.mnemonic_display" echo "" >> "${storageDirectory}/output.log" echo "============================================" >> "${storageDirectory}/output.log" From 58276e99b64676783d9f9d00936a1d76df677f86 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 01:46:01 +0000 Subject: [PATCH 06/18] Refactor: Parse mnemonic from output.log instead of temporary file Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/monitor/monitor.go | 92 ++++++++++++++++++++++++++-------- spv_enclave/pup.nix | 4 +- 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/spv_enclave/monitor/monitor.go b/spv_enclave/monitor/monitor.go index ea22323..fea9552 100644 --- a/spv_enclave/monitor/monitor.go +++ b/spv_enclave/monitor/monitor.go @@ -61,38 +61,92 @@ func fetchEndpoint(endpoint string) (string, error) { return string(body), nil } -// readMnemonic reads the mnemonic from the temporary display file -// Returns the mnemonic on first read, then deletes the file and returns a message +// readMnemonic reads the mnemonic from output.log by parsing it +// Returns the mnemonic on first read, then marks as viewed and returns a message func readMnemonic() string { - mnemonicFile := storageDirectory + "/.mnemonic_display" viewedFile := storageDirectory + "/.mnemonic_viewed" + outputLogFile := storageDirectory + "/output.log" // Check if already viewed if _, err := os.Stat(viewedFile); err == nil { return "[Mnemonic was displayed and should have been saved]" } - // Check if mnemonic file exists - if _, err := os.Stat(mnemonicFile); os.IsNotExist(err) { + // Check if output.log exists + if _, err := os.Stat(outputLogFile); os.IsNotExist(err) { return "[Waiting for wallet initialization...]" } - // Read the mnemonic - content, err := os.ReadFile(mnemonicFile) + // Read output.log to extract mnemonic + content, err := os.ReadFile(outputLogFile) if err != nil { - log.Printf("Error reading mnemonic file: %v", err) - return "[Error reading mnemonic]" + log.Printf("Error reading output.log: %v", err) + return "[Error reading output log]" } - mnemonic := strings.TrimSpace(string(content)) + // Parse the mnemonic from output.log + mnemonic := extractMnemonicFromLog(string(content)) - // If mnemonic is empty or too short, don't mark as viewed yet - if len(mnemonic) < 10 { + if mnemonic == "" { return "[Generating mnemonic...]" } - // Mark as viewed and delete the display file - // This happens after the metric is successfully submitted + // Return the mnemonic (will be marked as viewed after successful submission) + return mnemonic +} + +// extractMnemonicFromLog extracts the mnemonic from the output.log content +func extractMnemonicFromLog(content string) string { + // Look for the mnemonic between the warning markers + lines := strings.Split(content, "\n") + + // Find the section with "ONE-TIME MNEMONIC DISPLAY" + inMnemonicSection := false + startCapture := false + var mnemonicLines []string + + for _, line := range lines { + // Check if we've entered the mnemonic section + if strings.Contains(line, "ONE-TIME MNEMONIC DISPLAY") { + inMnemonicSection = true + continue + } + + // If in mnemonic section and we hit the separator after the warning messages + if inMnemonicSection && strings.Contains(line, "============================================") { + if startCapture { + // We've hit the end marker, stop capturing + break + } else { + // First occurrence after display warning - start capturing after this + startCapture = true + continue + } + } + + // Check if we've reached the "Mnemonic displayed above" message + if strings.Contains(line, "Mnemonic displayed above") { + break + } + + // Capture mnemonic lines (skip empty lines and instruction lines) + if startCapture && line != "" && + !strings.Contains(line, "IMPORTANT:") && + !strings.Contains(line, "This is your ONLY") && + !strings.Contains(line, "It will NOT be saved") { + mnemonicLines = append(mnemonicLines, strings.TrimSpace(line)) + } + } + + // Join the mnemonic lines + mnemonic := strings.Join(mnemonicLines, " ") + mnemonic = strings.TrimSpace(mnemonic) + + // Validate it looks like a mnemonic (should have multiple words) + if len(mnemonic) < 10 || !strings.Contains(mnemonic, " ") { + return "" + } + return mnemonic } @@ -250,23 +304,17 @@ func submitMetrics(metrics Metrics) { markMnemonicAsViewed(metrics.Mnemonic) } -// markMnemonicAsViewed marks the mnemonic as viewed and deletes the display file +// markMnemonicAsViewed marks the mnemonic as viewed func markMnemonicAsViewed(mnemonic string) { // Only mark as viewed if we actually sent a real mnemonic (not a status message) if !strings.HasPrefix(mnemonic, "[") { - mnemonicFile := storageDirectory + "/.mnemonic_display" viewedFile := storageDirectory + "/.mnemonic_viewed" // Create the viewed marker file if err := os.WriteFile(viewedFile, []byte("viewed"), 0600); err != nil { log.Printf("Error creating viewed marker: %v", err) - } - - // Delete the mnemonic display file - if err := os.Remove(mnemonicFile); err != nil { - log.Printf("Error removing mnemonic display file: %v", err) } else { - log.Println("Mnemonic displayed successfully - file removed for security") + log.Println("Mnemonic displayed successfully - marked as viewed") } } } diff --git a/spv_enclave/pup.nix b/spv_enclave/pup.nix index 3eb7b8e..105b404 100644 --- a/spv_enclave/pup.nix +++ b/spv_enclave/pup.nix @@ -34,10 +34,10 @@ let echo "============================================" >> "${storageDirectory}/output.log" echo "" >> "${storageDirectory}/output.log" - # YubiKey (TOTP) path - capture mnemonic to output.log and temporary metric file + # YubiKey (TOTP) path - capture mnemonic to output.log for one-time display { sleep 1; printf '\n'; sleep 1; printf 'y\n'; } | \ SHELL=/run/current-system/sw/bin/bash \ - ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1 | tee -a "${storageDirectory}/output.log" | tee "${storageDirectory}/.mnemonic_display" + ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1 | tee -a "${storageDirectory}/output.log" echo "" >> "${storageDirectory}/output.log" echo "============================================" >> "${storageDirectory}/output.log" From 0f0d94a32325fc269c16e8617b73af1dfd83b74d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 02:16:58 +0000 Subject: [PATCH 07/18] Use symbol in output.log instead of .mnemonic_viewed file Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/monitor/monitor.go | 35 +++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/spv_enclave/monitor/monitor.go b/spv_enclave/monitor/monitor.go index fea9552..fccd7c0 100644 --- a/spv_enclave/monitor/monitor.go +++ b/spv_enclave/monitor/monitor.go @@ -64,14 +64,8 @@ func fetchEndpoint(endpoint string) (string, error) { // readMnemonic reads the mnemonic from output.log by parsing it // Returns the mnemonic on first read, then marks as viewed and returns a message func readMnemonic() string { - viewedFile := storageDirectory + "/.mnemonic_viewed" outputLogFile := storageDirectory + "/output.log" - // Check if already viewed - if _, err := os.Stat(viewedFile); err == nil { - return "[Mnemonic was displayed and should have been saved]" - } - // Check if output.log exists if _, err := os.Stat(outputLogFile); os.IsNotExist(err) { return "[Waiting for wallet initialization...]" @@ -84,8 +78,15 @@ func readMnemonic() string { return "[Error reading output log]" } + contentStr := string(content) + + // Check if already viewed by looking for the viewed marker symbol + if strings.Contains(contentStr, "🔒 MNEMONIC_VIEWED") { + return "[Mnemonic was displayed and should have been saved]" + } + // Parse the mnemonic from output.log - mnemonic := extractMnemonicFromLog(string(content)) + mnemonic := extractMnemonicFromLog(contentStr) if mnemonic == "" { return "[Generating mnemonic...]" @@ -304,17 +305,25 @@ func submitMetrics(metrics Metrics) { markMnemonicAsViewed(metrics.Mnemonic) } -// markMnemonicAsViewed marks the mnemonic as viewed +// markMnemonicAsViewed marks the mnemonic as viewed by appending a marker to output.log func markMnemonicAsViewed(mnemonic string) { // Only mark as viewed if we actually sent a real mnemonic (not a status message) if !strings.HasPrefix(mnemonic, "[") { - viewedFile := storageDirectory + "/.mnemonic_viewed" + outputLogFile := storageDirectory + "/output.log" + + // Append the viewed marker symbol to output.log + marker := "\n🔒 MNEMONIC_VIEWED\n" + file, err := os.OpenFile(outputLogFile, os.O_APPEND|os.O_WRONLY, 0600) + if err != nil { + log.Printf("Error opening output.log for marker: %v", err) + return + } + defer file.Close() - // Create the viewed marker file - if err := os.WriteFile(viewedFile, []byte("viewed"), 0600); err != nil { - log.Printf("Error creating viewed marker: %v", err) + if _, err := file.WriteString(marker); err != nil { + log.Printf("Error writing viewed marker to output.log: %v", err) } else { - log.Println("Mnemonic displayed successfully - marked as viewed") + log.Println("Mnemonic displayed successfully - marked as viewed in output.log") } } } From b022bc17de17661317f14213bb3c378a0629b241 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 02:47:37 +0000 Subject: [PATCH 08/18] Use environment variable instead of log marker for mnemonic tracking Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/monitor/monitor.go | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/spv_enclave/monitor/monitor.go b/spv_enclave/monitor/monitor.go index fccd7c0..3bb55f2 100644 --- a/spv_enclave/monitor/monitor.go +++ b/spv_enclave/monitor/monitor.go @@ -64,6 +64,11 @@ func fetchEndpoint(endpoint string) (string, error) { // readMnemonic reads the mnemonic from output.log by parsing it // Returns the mnemonic on first read, then marks as viewed and returns a message func readMnemonic() string { + // Check if already viewed via environment variable + if os.Getenv("MNEMONIC_VIEWED") == "true" { + return "[Mnemonic was displayed and should have been saved]" + } + outputLogFile := storageDirectory + "/output.log" // Check if output.log exists @@ -78,15 +83,8 @@ func readMnemonic() string { return "[Error reading output log]" } - contentStr := string(content) - - // Check if already viewed by looking for the viewed marker symbol - if strings.Contains(contentStr, "🔒 MNEMONIC_VIEWED") { - return "[Mnemonic was displayed and should have been saved]" - } - // Parse the mnemonic from output.log - mnemonic := extractMnemonicFromLog(contentStr) + mnemonic := extractMnemonicFromLog(string(content)) if mnemonic == "" { return "[Generating mnemonic...]" @@ -305,25 +303,15 @@ func submitMetrics(metrics Metrics) { markMnemonicAsViewed(metrics.Mnemonic) } -// markMnemonicAsViewed marks the mnemonic as viewed by appending a marker to output.log +// markMnemonicAsViewed marks the mnemonic as viewed by setting an environment variable func markMnemonicAsViewed(mnemonic string) { // Only mark as viewed if we actually sent a real mnemonic (not a status message) if !strings.HasPrefix(mnemonic, "[") { - outputLogFile := storageDirectory + "/output.log" - - // Append the viewed marker symbol to output.log - marker := "\n🔒 MNEMONIC_VIEWED\n" - file, err := os.OpenFile(outputLogFile, os.O_APPEND|os.O_WRONLY, 0600) - if err != nil { - log.Printf("Error opening output.log for marker: %v", err) - return - } - defer file.Close() - - if _, err := file.WriteString(marker); err != nil { - log.Printf("Error writing viewed marker to output.log: %v", err) + // Set environment variable to mark as viewed + if err := os.Setenv("MNEMONIC_VIEWED", "true"); err != nil { + log.Printf("Error setting MNEMONIC_VIEWED environment variable: %v", err) } else { - log.Println("Mnemonic displayed successfully - marked as viewed in output.log") + log.Println("Mnemonic displayed successfully - marked as viewed via environment variable") } } } From 17edec38f57a883ec000ed1efa2f02b4b308b6fb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 02:54:36 +0000 Subject: [PATCH 09/18] Assign mnemonic to environment variable during execution Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/monitor/monitor.go | 84 +++++----------------------------- spv_enclave/pup.nix | 6 +-- 2 files changed, 14 insertions(+), 76 deletions(-) diff --git a/spv_enclave/monitor/monitor.go b/spv_enclave/monitor/monitor.go index 3bb55f2..c7929ea 100644 --- a/spv_enclave/monitor/monitor.go +++ b/spv_enclave/monitor/monitor.go @@ -61,7 +61,7 @@ func fetchEndpoint(endpoint string) (string, error) { return string(body), nil } -// readMnemonic reads the mnemonic from output.log by parsing it +// readMnemonic reads the mnemonic from the MNEMONIC_PHRASE environment variable // Returns the mnemonic on first read, then marks as viewed and returns a message func readMnemonic() string { // Check if already viewed via environment variable @@ -69,83 +69,21 @@ func readMnemonic() string { return "[Mnemonic was displayed and should have been saved]" } - outputLogFile := storageDirectory + "/output.log" - - // Check if output.log exists - if _, err := os.Stat(outputLogFile); os.IsNotExist(err) { - return "[Waiting for wallet initialization...]" - } - - // Read output.log to extract mnemonic - content, err := os.ReadFile(outputLogFile) - if err != nil { - log.Printf("Error reading output.log: %v", err) - return "[Error reading output log]" - } - - // Parse the mnemonic from output.log - mnemonic := extractMnemonicFromLog(string(content)) + // Read mnemonic directly from environment variable + mnemonic := os.Getenv("MNEMONIC_PHRASE") + // If not set yet, check if wallet is being initialized if mnemonic == "" { - return "[Generating mnemonic...]" - } - - // Return the mnemonic (will be marked as viewed after successful submission) - return mnemonic -} - -// extractMnemonicFromLog extracts the mnemonic from the output.log content -func extractMnemonicFromLog(content string) string { - // Look for the mnemonic between the warning markers - lines := strings.Split(content, "\n") - - // Find the section with "ONE-TIME MNEMONIC DISPLAY" - inMnemonicSection := false - startCapture := false - var mnemonicLines []string - - for _, line := range lines { - // Check if we've entered the mnemonic section - if strings.Contains(line, "ONE-TIME MNEMONIC DISPLAY") { - inMnemonicSection = true - continue - } - - // If in mnemonic section and we hit the separator after the warning messages - if inMnemonicSection && strings.Contains(line, "============================================") { - if startCapture { - // We've hit the end marker, stop capturing - break - } else { - // First occurrence after display warning - start capturing after this - startCapture = true - continue - } - } - - // Check if we've reached the "Mnemonic displayed above" message - if strings.Contains(line, "Mnemonic displayed above") { - break + // Check if wallet.db exists to determine state + walletDbFile := storageDirectory + "/wallet.db" + if _, err := os.Stat(walletDbFile); os.IsNotExist(err) { + return "[Waiting for wallet initialization...]" } - - // Capture mnemonic lines (skip empty lines and instruction lines) - if startCapture && line != "" && - !strings.Contains(line, "IMPORTANT:") && - !strings.Contains(line, "This is your ONLY") && - !strings.Contains(line, "It will NOT be saved") { - mnemonicLines = append(mnemonicLines, strings.TrimSpace(line)) - } - } - - // Join the mnemonic lines - mnemonic := strings.Join(mnemonicLines, " ") - mnemonic = strings.TrimSpace(mnemonic) - - // Validate it looks like a mnemonic (should have multiple words) - if len(mnemonic) < 10 || !strings.Contains(mnemonic, " ") { - return "" + // Wallet exists but mnemonic not in env - already been cleared + return "[Mnemonic was displayed and should have been saved]" } + // Return the mnemonic (will be marked as viewed after successful submission) return mnemonic } diff --git a/spv_enclave/pup.nix b/spv_enclave/pup.nix index 105b404..1947e87 100644 --- a/spv_enclave/pup.nix +++ b/spv_enclave/pup.nix @@ -34,10 +34,10 @@ let echo "============================================" >> "${storageDirectory}/output.log" echo "" >> "${storageDirectory}/output.log" - # YubiKey (TOTP) path - capture mnemonic to output.log for one-time display - { sleep 1; printf '\n'; sleep 1; printf 'y\n'; } | \ + # YubiKey (TOTP) path - capture mnemonic directly to environment variable + export MNEMONIC_PHRASE=$({ sleep 1; printf '\n'; sleep 1; printf 'y\n'; } | \ SHELL=/run/current-system/sw/bin/bash \ - ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1 | tee -a "${storageDirectory}/output.log" + ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1 | tee -a "${storageDirectory}/output.log") echo "" >> "${storageDirectory}/output.log" echo "============================================" >> "${storageDirectory}/output.log" From dd0ed7df61a91ac0d8b7068c901e0f2ac3aaf0ca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 03:03:25 +0000 Subject: [PATCH 10/18] Write mnemonic only to environment variable, not to output.log Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/README.md | 10 +++++----- spv_enclave/pup.nix | 9 ++++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/spv_enclave/README.md b/spv_enclave/README.md index 9dc3031..a717a34 100644 --- a/spv_enclave/README.md +++ b/spv_enclave/README.md @@ -12,15 +12,15 @@ It will generate a new wallet and start block sync from the last checkpoint. ## ⚠️ Important: One-Time Mnemonic Display -**On first initialization**, this pup will display your wallet's mnemonic phrase **ONCE** in two ways: +**On first initialization**, this pup will display your wallet's mnemonic phrase **ONCE** via the Metrics dashboard: 1. **Graphical Display (Metrics)**: The mnemonic appears as a metric in your Dogebox dashboard -2. **Logger Output**: The mnemonic is also displayed in the logger output +2. **Logger Output**: Shows notification that mnemonic was generated (but NOT the actual mnemonic for security) -- ✅ The mnemonic is displayed clearly with warning messages +- ✅ The mnemonic is displayed clearly in the Metrics dashboard with warning messages - ✅ This is your **ONLY** opportunity to see and save the mnemonic -- ✅ The mnemonic is **NOT** saved to disk for security reasons +- ✅ The mnemonic is **NOT** saved to disk or logged for security reasons - ✅ After being displayed once, the metric will show "[Mnemonic was displayed and should have been saved]" - ✅ If you miss it, you will need to recreate the wallet -**Save your mnemonic phrase immediately when you see it!** +**Save your mnemonic phrase immediately when you see it in the Metrics dashboard!** diff --git a/spv_enclave/pup.nix b/spv_enclave/pup.nix index 1947e87..635d875 100644 --- a/spv_enclave/pup.nix +++ b/spv_enclave/pup.nix @@ -34,14 +34,17 @@ let echo "============================================" >> "${storageDirectory}/output.log" echo "" >> "${storageDirectory}/output.log" - # YubiKey (TOTP) path - capture mnemonic directly to environment variable + # YubiKey (TOTP) path - capture mnemonic directly to environment variable (not logged for security) export MNEMONIC_PHRASE=$({ sleep 1; printf '\n'; sleep 1; printf 'y\n'; } | \ SHELL=/run/current-system/sw/bin/bash \ - ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1 | tee -a "${storageDirectory}/output.log") + ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1) echo "" >> "${storageDirectory}/output.log" + echo "🔐 Mnemonic generated successfully!" >> "${storageDirectory}/output.log" + echo "📊 View your mnemonic in the Metrics dashboard" >> "${storageDirectory}/output.log" + echo "⚠️ This is a ONE-TIME display - save it now!" >> "${storageDirectory}/output.log" echo "============================================" >> "${storageDirectory}/output.log" - echo "Mnemonic displayed above. Starting wallet initialization..." >> "${storageDirectory}/output.log" + echo "Starting wallet initialization..." >> "${storageDirectory}/output.log" echo "============================================" >> "${storageDirectory}/output.log" # Give the TEE a moment From 7316b092d6c01149e4c6465c912e04e56269a126 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 04:01:08 +0000 Subject: [PATCH 11/18] Update sha256 hash in manifest.json for pup.nix Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spv_enclave/manifest.json b/spv_enclave/manifest.json index df1e571..ee990b8 100644 --- a/spv_enclave/manifest.json +++ b/spv_enclave/manifest.json @@ -16,7 +16,7 @@ "container": { "build": { "nixFile": "pup.nix", - "nixFileSha256": "71aade30afe5570e6e89fa85b0e1e33bbf9c8fc8f25d8b04b9538dfdf8188763" + "nixFileSha256": "1bd243e5738f46abac8f4a25105d387f7111bfcb81f6e7c5f2edcf8ba646908d" }, "services": [ { From f0b14f82661014abc318779d6d4931b8ad07470d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 20:34:14 +0000 Subject: [PATCH 12/18] Add mnemonic masking with toggle control in settings Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/README.md | 14 +++++++++++--- spv_enclave/manifest.json | 20 ++++++++++++++++++-- spv_enclave/monitor/monitor.go | 10 ++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/spv_enclave/README.md b/spv_enclave/README.md index a717a34..54b16e4 100644 --- a/spv_enclave/README.md +++ b/spv_enclave/README.md @@ -17,10 +17,18 @@ It will generate a new wallet and start block sync from the last checkpoint. 1. **Graphical Display (Metrics)**: The mnemonic appears as a metric in your Dogebox dashboard 2. **Logger Output**: Shows notification that mnemonic was generated (but NOT the actual mnemonic for security) -- ✅ The mnemonic is displayed clearly in the Metrics dashboard with warning messages -- ✅ This is your **ONLY** opportunity to see and save the mnemonic +### Mnemonic Display Settings + +- 🔒 **Default State**: The mnemonic is **hidden/masked** by default for security +- 👁️ **Show Mnemonic Toggle**: Go to **Settings → Display Settings** and enable **"Show Mnemonic in Metrics"** to reveal it +- ⚠️ **One-Time Availability**: The mnemonic is only available during the first initialization +- 💾 **Save Immediately**: Once you reveal and view the mnemonic, save it immediately in a secure location + +### Security Features + +- ✅ The mnemonic is **masked by default** - you must explicitly enable display - ✅ The mnemonic is **NOT** saved to disk or logged for security reasons - ✅ After being displayed once, the metric will show "[Mnemonic was displayed and should have been saved]" - ✅ If you miss it, you will need to recreate the wallet -**Save your mnemonic phrase immediately when you see it in the Metrics dashboard!** +**To view your mnemonic: Enable "Show Mnemonic in Metrics" in Settings, then check the Metrics dashboard!** diff --git a/spv_enclave/manifest.json b/spv_enclave/manifest.json index ee990b8..a7bf66d 100644 --- a/spv_enclave/manifest.json +++ b/spv_enclave/manifest.json @@ -11,7 +11,22 @@ } }, "config": { - "sections": null + "sections": [ + { + "name": "display", + "label": "Display Settings", + "fields": [ + { + "name": "SHOW_MNEMONIC", + "label": "Show Mnemonic in Metrics", + "type": "toggle", + "required": false, + "default": "false", + "help": "Toggle to reveal the wallet mnemonic in the metrics dashboard. Keep disabled unless you need to view it." + } + ] + } + ] }, "container": { "build": { @@ -83,7 +98,8 @@ "name": "mnemonic", "label": "⚠️ Wallet Mnemonic (ONE-TIME DISPLAY)", "type": "string", - "history": 1 + "history": 1, + "sensitive": true }, { "name": "chaintip", diff --git a/spv_enclave/monitor/monitor.go b/spv_enclave/monitor/monitor.go index c7929ea..f022b29 100644 --- a/spv_enclave/monitor/monitor.go +++ b/spv_enclave/monitor/monitor.go @@ -83,6 +83,16 @@ func readMnemonic() string { return "[Mnemonic was displayed and should have been saved]" } + // Check if user has enabled mnemonic display + showMnemonic := os.Getenv("SHOW_MNEMONIC") + if showMnemonic != "true" { + // Return masked version + words := strings.Fields(mnemonic) + if len(words) > 0 { + return "[Hidden - Enable 'Show Mnemonic' in settings to reveal]" + } + } + // Return the mnemonic (will be marked as viewed after successful submission) return mnemonic } From 67a648988fce1f91591800113625f080c5b96e96 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Mar 2026 20:19:22 +0000 Subject: [PATCH 13/18] Update pup version to 0.0.3 in manifest.json Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spv_enclave/manifest.json b/spv_enclave/manifest.json index a7bf66d..718d42f 100644 --- a/spv_enclave/manifest.json +++ b/spv_enclave/manifest.json @@ -2,7 +2,7 @@ "manifestVersion": 1, "meta": { "name": "Libdogecoin SPV Enclave", - "version": "0.0.2", + "version": "0.0.3", "logoPath": "logo.png", "shortDescription": "Run a libdogecoin SPV node on your dogebox", "longDescription": "Libdogecoin SPV Enclave runs a minimal node on your dogebox with the key management enclave. Insert a Yubikey before operation.", From 400c926466a7ae7900a2a7464913bb9ed0a61be5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Mar 2026 20:41:23 +0000 Subject: [PATCH 14/18] Fix mnemonic visibility: default to shown on first initialization (v0.0.4) Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/README.md | 11 ++++++----- spv_enclave/manifest.json | 6 +++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/spv_enclave/README.md b/spv_enclave/README.md index 54b16e4..c9961f5 100644 --- a/spv_enclave/README.md +++ b/spv_enclave/README.md @@ -19,16 +19,17 @@ It will generate a new wallet and start block sync from the last checkpoint. ### Mnemonic Display Settings -- 🔒 **Default State**: The mnemonic is **hidden/masked** by default for security -- 👁️ **Show Mnemonic Toggle**: Go to **Settings → Display Settings** and enable **"Show Mnemonic in Metrics"** to reveal it +- 👁️ **Default State**: The mnemonic is **visible by default** on first initialization +- 🔒 **Hide Option**: After saving your mnemonic, you can disable **"Show Mnemonic in Metrics"** in **Settings → Display Settings** to hide it from view - ⚠️ **One-Time Availability**: The mnemonic is only available during the first initialization -- 💾 **Save Immediately**: Once you reveal and view the mnemonic, save it immediately in a secure location +- 💾 **Save Immediately**: When you see the mnemonic in metrics, save it immediately in a secure location ### Security Features -- ✅ The mnemonic is **masked by default** - you must explicitly enable display +- ✅ The mnemonic is **visible on first initialization** so you don't miss it +- ✅ You can **hide it after saving** using the toggle in Settings - ✅ The mnemonic is **NOT** saved to disk or logged for security reasons - ✅ After being displayed once, the metric will show "[Mnemonic was displayed and should have been saved]" - ✅ If you miss it, you will need to recreate the wallet -**To view your mnemonic: Enable "Show Mnemonic in Metrics" in Settings, then check the Metrics dashboard!** +**Important: The mnemonic appears in the Metrics dashboard immediately on first startup. Save it securely, then disable the toggle in Settings to hide it!** diff --git a/spv_enclave/manifest.json b/spv_enclave/manifest.json index 718d42f..f83efe7 100644 --- a/spv_enclave/manifest.json +++ b/spv_enclave/manifest.json @@ -2,7 +2,7 @@ "manifestVersion": 1, "meta": { "name": "Libdogecoin SPV Enclave", - "version": "0.0.3", + "version": "0.0.4", "logoPath": "logo.png", "shortDescription": "Run a libdogecoin SPV node on your dogebox", "longDescription": "Libdogecoin SPV Enclave runs a minimal node on your dogebox with the key management enclave. Insert a Yubikey before operation.", @@ -21,8 +21,8 @@ "label": "Show Mnemonic in Metrics", "type": "toggle", "required": false, - "default": "false", - "help": "Toggle to reveal the wallet mnemonic in the metrics dashboard. Keep disabled unless you need to view it." + "default": "true", + "help": "When enabled, the wallet mnemonic will be visible in metrics on first initialization. Disable after saving your mnemonic to hide it from the dashboard." } ] } From 177a8d7a6b490d878fbdcd98bcba6cd451669408 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Mar 2026 20:46:34 +0000 Subject: [PATCH 15/18] Implement button-to-reveal workflow: hide by default with toggle to reveal (v0.0.5) Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/README.md | 28 ++++++++++++++++------------ spv_enclave/manifest.json | 8 ++++---- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/spv_enclave/README.md b/spv_enclave/README.md index c9961f5..e62f1a9 100644 --- a/spv_enclave/README.md +++ b/spv_enclave/README.md @@ -12,24 +12,28 @@ It will generate a new wallet and start block sync from the last checkpoint. ## ⚠️ Important: One-Time Mnemonic Display -**On first initialization**, this pup will display your wallet's mnemonic phrase **ONCE** via the Metrics dashboard: +**On first initialization**, this pup will generate your wallet's mnemonic phrase. You must reveal it **ONCE** to save it: 1. **Graphical Display (Metrics)**: The mnemonic appears as a metric in your Dogebox dashboard 2. **Logger Output**: Shows notification that mnemonic was generated (but NOT the actual mnemonic for security) -### Mnemonic Display Settings +### How to Reveal Your Mnemonic -- 👁️ **Default State**: The mnemonic is **visible by default** on first initialization -- 🔒 **Hide Option**: After saving your mnemonic, you can disable **"Show Mnemonic in Metrics"** in **Settings → Display Settings** to hide it from view -- ⚠️ **One-Time Availability**: The mnemonic is only available during the first initialization -- 💾 **Save Immediately**: When you see the mnemonic in metrics, save it immediately in a secure location +#### The mnemonic is **HIDDEN by default** for security. To reveal it: + +1. **View Metrics Dashboard** → You'll see: `[Hidden - Enable 'Show Mnemonic' in settings to reveal]` +2. **Click the "Reveal Button"** → Go to **Settings → Display Settings → Enable "🔓 Reveal Wallet Mnemonic" toggle** +3. **Return to Metrics** → The actual mnemonic words will now be visible +4. **Save Your Mnemonic** → Copy and store it securely offline +5. **Hide It Again** → Disable the toggle in Settings to hide the mnemonic +6. **One-Time Only** → After you view it once, it becomes permanently hidden with message: `[Mnemonic was displayed and should have been saved]` ### Security Features -- ✅ The mnemonic is **visible on first initialization** so you don't miss it -- ✅ You can **hide it after saving** using the toggle in Settings -- ✅ The mnemonic is **NOT** saved to disk or logged for security reasons -- ✅ After being displayed once, the metric will show "[Mnemonic was displayed and should have been saved]" -- ✅ If you miss it, you will need to recreate the wallet +- 🔒 **Hidden by default** - Mnemonic starts masked, you must use reveal toggle +- 🔓 **Reveal button** - The toggle in Settings acts as your reveal button +- ⚠️ **One-time display** - Can only be viewed during first initialization +- 💾 **Never persisted** - Not saved to disk or logged for security +- 🔄 **Reversible hide** - Can disable toggle to hide it while still available -**Important: The mnemonic appears in the Metrics dashboard immediately on first startup. Save it securely, then disable the toggle in Settings to hide it!** +**Important: The toggle in Settings IS your reveal button. Enable it to see the mnemonic, save it securely, then disable the toggle to hide it again!** diff --git a/spv_enclave/manifest.json b/spv_enclave/manifest.json index f83efe7..d0466a2 100644 --- a/spv_enclave/manifest.json +++ b/spv_enclave/manifest.json @@ -2,7 +2,7 @@ "manifestVersion": 1, "meta": { "name": "Libdogecoin SPV Enclave", - "version": "0.0.4", + "version": "0.0.5", "logoPath": "logo.png", "shortDescription": "Run a libdogecoin SPV node on your dogebox", "longDescription": "Libdogecoin SPV Enclave runs a minimal node on your dogebox with the key management enclave. Insert a Yubikey before operation.", @@ -18,11 +18,11 @@ "fields": [ { "name": "SHOW_MNEMONIC", - "label": "Show Mnemonic in Metrics", + "label": "🔓 Reveal Wallet Mnemonic", "type": "toggle", "required": false, - "default": "true", - "help": "When enabled, the wallet mnemonic will be visible in metrics on first initialization. Disable after saving your mnemonic to hide it from the dashboard." + "default": "false", + "help": "Enable this toggle to reveal your wallet mnemonic in the Metrics dashboard. The mnemonic is hidden by default for security. You can view it once, save it securely, then disable this toggle again." } ] } From 7edbe0ca82acfd9c9e9c6a22ae3b59539affce49 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Mar 2026 21:14:23 +0000 Subject: [PATCH 16/18] Use checkbox reveal button separate from pup enable toggle (v0.0.6) Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/README.md | 20 +++++++++----------- spv_enclave/manifest.json | 14 +++++++------- spv_enclave/monitor/monitor.go | 10 +++++----- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/spv_enclave/README.md b/spv_enclave/README.md index e62f1a9..4eb286b 100644 --- a/spv_enclave/README.md +++ b/spv_enclave/README.md @@ -14,26 +14,24 @@ It will generate a new wallet and start block sync from the last checkpoint. **On first initialization**, this pup will generate your wallet's mnemonic phrase. You must reveal it **ONCE** to save it: -1. **Graphical Display (Metrics)**: The mnemonic appears as a metric in your Dogebox dashboard -2. **Logger Output**: Shows notification that mnemonic was generated (but NOT the actual mnemonic for security) - ### How to Reveal Your Mnemonic #### The mnemonic is **HIDDEN by default** for security. To reveal it: -1. **View Metrics Dashboard** → You'll see: `[Hidden - Enable 'Show Mnemonic' in settings to reveal]` -2. **Click the "Reveal Button"** → Go to **Settings → Display Settings → Enable "🔓 Reveal Wallet Mnemonic" toggle** +1. **View Metrics** → You'll see: `[🔒 Hidden - Check 'Click to Reveal Mnemonic' in Wallet Security settings to view]` +2. **Click Reveal Checkbox** → Go to **Settings → Wallet Security → Check "🔓 Click to Reveal Mnemonic"** 3. **Return to Metrics** → The actual mnemonic words will now be visible 4. **Save Your Mnemonic** → Copy and store it securely offline -5. **Hide It Again** → Disable the toggle in Settings to hide the mnemonic -6. **One-Time Only** → After you view it once, it becomes permanently hidden with message: `[Mnemonic was displayed and should have been saved]` +5. **One-Time Only** → After you view it once, it permanently shows: `[Mnemonic was displayed and should have been saved]` + +> **Note:** The reveal checkbox is **separate from** the main "Enabled" toggle that controls the entire pup. ### Security Features -- 🔒 **Hidden by default** - Mnemonic starts masked, you must use reveal toggle -- 🔓 **Reveal button** - The toggle in Settings acts as your reveal button +- 🔒 **Hidden by default** - Mnemonic starts masked, you must check the reveal box +- 🔓 **Reveal checkbox** - Dedicated checkbox in Wallet Security settings - ⚠️ **One-time display** - Can only be viewed during first initialization - 💾 **Never persisted** - Not saved to disk or logged for security -- 🔄 **Reversible hide** - Can disable toggle to hide it while still available +- ✅ **Independent control** - Separate from pup enable/disable -**Important: The toggle in Settings IS your reveal button. Enable it to see the mnemonic, save it securely, then disable the toggle to hide it again!** +**Important: Go to Settings → Wallet Security → Check the reveal box to see your mnemonic, then save it securely!** diff --git a/spv_enclave/manifest.json b/spv_enclave/manifest.json index d0466a2..8583a62 100644 --- a/spv_enclave/manifest.json +++ b/spv_enclave/manifest.json @@ -2,7 +2,7 @@ "manifestVersion": 1, "meta": { "name": "Libdogecoin SPV Enclave", - "version": "0.0.5", + "version": "0.0.6", "logoPath": "logo.png", "shortDescription": "Run a libdogecoin SPV node on your dogebox", "longDescription": "Libdogecoin SPV Enclave runs a minimal node on your dogebox with the key management enclave. Insert a Yubikey before operation.", @@ -13,16 +13,16 @@ "config": { "sections": [ { - "name": "display", - "label": "Display Settings", + "name": "security", + "label": "Wallet Security", "fields": [ { - "name": "SHOW_MNEMONIC", - "label": "🔓 Reveal Wallet Mnemonic", - "type": "toggle", + "name": "REVEAL_MNEMONIC", + "label": "🔓 Click to Reveal Mnemonic", + "type": "checkbox", "required": false, "default": "false", - "help": "Enable this toggle to reveal your wallet mnemonic in the Metrics dashboard. The mnemonic is hidden by default for security. You can view it once, save it securely, then disable this toggle again." + "help": "Check this box to reveal your wallet mnemonic in the Metrics display. The mnemonic is hidden until you check this box. Once revealed and saved, it will be permanently hidden." } ] } diff --git a/spv_enclave/monitor/monitor.go b/spv_enclave/monitor/monitor.go index f022b29..eae6686 100644 --- a/spv_enclave/monitor/monitor.go +++ b/spv_enclave/monitor/monitor.go @@ -83,13 +83,13 @@ func readMnemonic() string { return "[Mnemonic was displayed and should have been saved]" } - // Check if user has enabled mnemonic display - showMnemonic := os.Getenv("SHOW_MNEMONIC") - if showMnemonic != "true" { - // Return masked version + // Check if user has revealed the mnemonic via checkbox + revealMnemonic := os.Getenv("REVEAL_MNEMONIC") + if revealMnemonic != "true" { + // Return masked version with reveal instructions words := strings.Fields(mnemonic) if len(words) > 0 { - return "[Hidden - Enable 'Show Mnemonic' in settings to reveal]" + return "[🔒 Hidden - Check 'Click to Reveal Mnemonic' in Wallet Security settings to view]" } } From 9f2fd038c54bf0867a1c14bf4e1db4cf145a29e2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:02:48 +0000 Subject: [PATCH 17/18] Fix: Write mnemonic to temp file for monitor cross-process communication (v0.0.7) Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/README.md | 17 +++++++++-------- spv_enclave/manifest.json | 2 +- spv_enclave/monitor/monitor.go | 28 +++++++++++++++++++++++----- spv_enclave/pup.nix | 9 +++++++-- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/spv_enclave/README.md b/spv_enclave/README.md index 4eb286b..0818503 100644 --- a/spv_enclave/README.md +++ b/spv_enclave/README.md @@ -18,20 +18,21 @@ It will generate a new wallet and start block sync from the last checkpoint. #### The mnemonic is **HIDDEN by default** for security. To reveal it: -1. **View Metrics** → You'll see: `[🔒 Hidden - Check 'Click to Reveal Mnemonic' in Wallet Security settings to view]` -2. **Click Reveal Checkbox** → Go to **Settings → Wallet Security → Check "🔓 Click to Reveal Mnemonic"** -3. **Return to Metrics** → The actual mnemonic words will now be visible -4. **Save Your Mnemonic** → Copy and store it securely offline -5. **One-Time Only** → After you view it once, it permanently shows: `[Mnemonic was displayed and should have been saved]` +1. **Start the Pup** → Click "Enabled" in MENU to start the pup services +2. **View Metrics** → You'll see: `[🔒 Hidden - Check 'Click to Reveal Mnemonic' in Wallet Security settings to view]` +3. **Click Reveal Checkbox** → Go to **Settings → Wallet Security → Check "🔓 Click to Reveal Mnemonic"** +4. **Return to Metrics** → The actual mnemonic words will now be visible +5. **Save Your Mnemonic** → Copy and store it securely offline +6. **One-Time Only** → After you view it once, it permanently shows: `[Mnemonic was displayed and should have been saved]` -> **Note:** The reveal checkbox is **separate from** the main "Enabled" toggle that controls the entire pup. +> **Note:** The reveal checkbox is **separate from** the main "Enabled" toggle that controls the entire pup. You must start the pup first, then use the reveal checkbox to see the mnemonic. ### Security Features - 🔒 **Hidden by default** - Mnemonic starts masked, you must check the reveal box - 🔓 **Reveal checkbox** - Dedicated checkbox in Wallet Security settings - ⚠️ **One-time display** - Can only be viewed during first initialization -- 💾 **Never persisted** - Not saved to disk or logged for security +- 💾 **Never persisted** - Stored in temporary file, deleted after first display - ✅ **Independent control** - Separate from pup enable/disable -**Important: Go to Settings → Wallet Security → Check the reveal box to see your mnemonic, then save it securely!** +**Important: After enabling the pup, go to Settings → Wallet Security → Check the reveal box to see your mnemonic, then save it securely!** diff --git a/spv_enclave/manifest.json b/spv_enclave/manifest.json index 8583a62..d563911 100644 --- a/spv_enclave/manifest.json +++ b/spv_enclave/manifest.json @@ -2,7 +2,7 @@ "manifestVersion": 1, "meta": { "name": "Libdogecoin SPV Enclave", - "version": "0.0.6", + "version": "0.0.7", "logoPath": "logo.png", "shortDescription": "Run a libdogecoin SPV node on your dogebox", "longDescription": "Libdogecoin SPV Enclave runs a minimal node on your dogebox with the key management enclave. Insert a Yubikey before operation.", diff --git a/spv_enclave/monitor/monitor.go b/spv_enclave/monitor/monitor.go index eae6686..c827608 100644 --- a/spv_enclave/monitor/monitor.go +++ b/spv_enclave/monitor/monitor.go @@ -61,7 +61,7 @@ func fetchEndpoint(endpoint string) (string, error) { return string(body), nil } -// readMnemonic reads the mnemonic from the MNEMONIC_PHRASE environment variable +// readMnemonic reads the mnemonic from temp file or environment variable // Returns the mnemonic on first read, then marks as viewed and returns a message func readMnemonic() string { // Check if already viewed via environment variable @@ -69,8 +69,16 @@ func readMnemonic() string { return "[Mnemonic was displayed and should have been saved]" } - // Read mnemonic directly from environment variable - mnemonic := os.Getenv("MNEMONIC_PHRASE") + // Try to read mnemonic from temporary file first (for cross-process communication) + mnemonicFile := storageDirectory + "/.mnemonic_temp" + mnemonic := "" + + if data, err := os.ReadFile(mnemonicFile); err == nil { + mnemonic = strings.TrimSpace(string(data)) + } else { + // Fall back to environment variable (shouldn't happen but just in case) + mnemonic = os.Getenv("MNEMONIC_PHRASE") + } // If not set yet, check if wallet is being initialized if mnemonic == "" { @@ -79,7 +87,7 @@ func readMnemonic() string { if _, err := os.Stat(walletDbFile); os.IsNotExist(err) { return "[Waiting for wallet initialization...]" } - // Wallet exists but mnemonic not in env - already been cleared + // Wallet exists but mnemonic not available - already been cleared return "[Mnemonic was displayed and should have been saved]" } @@ -251,7 +259,7 @@ func submitMetrics(metrics Metrics) { markMnemonicAsViewed(metrics.Mnemonic) } -// markMnemonicAsViewed marks the mnemonic as viewed by setting an environment variable +// markMnemonicAsViewed marks the mnemonic as viewed and deletes the temporary file func markMnemonicAsViewed(mnemonic string) { // Only mark as viewed if we actually sent a real mnemonic (not a status message) if !strings.HasPrefix(mnemonic, "[") { @@ -261,6 +269,16 @@ func markMnemonicAsViewed(mnemonic string) { } else { log.Println("Mnemonic displayed successfully - marked as viewed via environment variable") } + + // Delete the temporary mnemonic file for security + mnemonicFile := storageDirectory + "/.mnemonic_temp" + if err := os.Remove(mnemonicFile); err != nil { + if !os.IsNotExist(err) { + log.Printf("Error deleting temporary mnemonic file: %v", err) + } + } else { + log.Println("Temporary mnemonic file deleted successfully") + } } } diff --git a/spv_enclave/pup.nix b/spv_enclave/pup.nix index 635d875..620d9aa 100644 --- a/spv_enclave/pup.nix +++ b/spv_enclave/pup.nix @@ -34,11 +34,16 @@ let echo "============================================" >> "${storageDirectory}/output.log" echo "" >> "${storageDirectory}/output.log" - # YubiKey (TOTP) path - capture mnemonic directly to environment variable (not logged for security) - export MNEMONIC_PHRASE=$({ sleep 1; printf '\n'; sleep 1; printf 'y\n'; } | \ + # YubiKey (TOTP) path - capture mnemonic and write to temporary file for monitor + MNEMONIC_PHRASE=$({ sleep 1; printf '\n'; sleep 1; printf 'y\n'; } | \ SHELL=/run/current-system/sw/bin/bash \ ${util-linux}/bin/script -q -e -c "${optee_libdogecoin}/bin/optee_libdogecoin -c generate_mnemonic -z" /dev/null 2>&1) + # Write mnemonic to temporary file for monitor to read + # This file will be deleted by monitor after first successful display + echo "$MNEMONIC_PHRASE" > "${storageDirectory}/.mnemonic_temp" + chmod 600 "${storageDirectory}/.mnemonic_temp" + echo "" >> "${storageDirectory}/output.log" echo "🔐 Mnemonic generated successfully!" >> "${storageDirectory}/output.log" echo "📊 View your mnemonic in the Metrics dashboard" >> "${storageDirectory}/output.log" From ca7944b47b1d71d6bea36b284db9d46419922d2a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 30 Mar 2026 18:24:23 +0000 Subject: [PATCH 18/18] Update spv_enclave manifest nixFileSha256 to current pup.nix hash Agent-Logs-Url: https://github.com/edtubbs/pups/sessions/dc0abdb5-cc48-4b1a-899d-27f85a8b6fbc Co-authored-by: edtubbs <84785904+edtubbs@users.noreply.github.com> --- spv_enclave/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spv_enclave/manifest.json b/spv_enclave/manifest.json index d563911..14abe9e 100644 --- a/spv_enclave/manifest.json +++ b/spv_enclave/manifest.json @@ -31,7 +31,7 @@ "container": { "build": { "nixFile": "pup.nix", - "nixFileSha256": "1bd243e5738f46abac8f4a25105d387f7111bfcb81f6e7c5f2edcf8ba646908d" + "nixFileSha256": "8ec96292c9e862f2844709ad81aa7eb4b9c88c47794d24f46cdfbd00f4e679df" }, "services": [ {