OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-wallet commits / ff714fb

Phase 2: Fix unlock prompt wording + readBody bug
- Fixed ReferenceError: readBody → parseBody in /api/unlock - Added GET /api/wallet/encryption-status endpoint - First-time password setup shows clear warning about encryption - Distinct prompts for 'SET PASSWORD' vs 'UNLOCK' scenarios - Success message emphasizes password must be remembered - Prevents confusion about password purpose Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
ff714fb3f6cf824d4fbc4c9167c58b3b22b83c29
by plankatron <fleeky@gmail.com> · 2026-01-27T20:38:14.000Z

Changed files (3) +85 -6

.claude/tmux-registry.json +2 -2
……@@ -8,9 +8,9 @@
88 "purpose": "Seeding pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso",
99 "command": "pear seed pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso .",
1010 "persistent": true,
11 "latest_version": "11436",
11 "latest_version": "11442",
1212 "last_deployed": "2026-01-27",
13 "features": "Phase 2 COMPLETE: Auth + Encryption + Validation + Rate Limiting + CORS + Signed Invites ✅"
13 "features": "Phase 2 COMPLETE: Auth + Encryption + Validation + Rate Limiting + CORS + Signed Invites + Password Unlock UI ✅"
1414 },
1515 "1": {
1616 "name": "wirecore",
ui/app.js +57 -3
……@@ -2180,13 +2180,62 @@ function exportTransactions() {
21802180 showSuccess(`Exported ${allTransactions.length} transactions`)
21812181}
21822182
2183// Check if wallet needs password setup (plaintext mnemonics)
2184async function checkEncryptionStatus() {
2185 try {
2186 const res = await fetch(`${API_BASE}/wallet/encryption-status`)
2187 if (res.ok) {
2188 const data = await res.json()
2189 return data.encrypted
2190 }
2191 } catch (err) {
2192 console.error('Failed to check encryption status:', err)
2193 }
2194 return null // Unknown status
2195}
2196
21832197// Prompt for password to unlock wallet
21842198async function promptForPassword() {
21852199 let attempts = 0
21862200 const maxAttempts = 3
21872201
2202 // Check if this is first-time password setup or regular unlock
2203 const isEncrypted = await checkEncryptionStatus()
2204 const isFirstTimeSetup = isEncrypted === false
2205
21882206 while (attempts < maxAttempts) {
2189 const password = prompt(`🔐 Unlock Wallet\n\nEnter your password to access the wallet:\n\n(Attempt ${attempts + 1}/${maxAttempts})`)
2207 let promptMessage
2208
2209 if (isFirstTimeSetup && attempts === 0) {
2210 // First-time password setup - clear warning
2211 promptMessage = `⚠️ SET WALLET PASSWORD (First Time Setup)
2212
2213This will ENCRYPT your wallet mnemonics with a password.
2214
2215IMPORTANT:
2216• You MUST remember this password
2217• Required every time you access the wallet
2218• Cannot be recovered if forgotten
2219• Minimum 8 characters
2220
2221Enter a strong password:`
2222 } else if (isFirstTimeSetup) {
2223 // First-time setup, retry attempt
2224 promptMessage = `⚠️ SET WALLET PASSWORD
2225
2226Enter a strong password (min 8 characters):
2227
2228(Attempt ${attempts + 1}/${maxAttempts})`
2229 } else {
2230 // Regular unlock
2231 promptMessage = `🔐 Unlock Wallet
2232
2233Enter your wallet password:
2234
2235(Attempt ${attempts + 1}/${maxAttempts})`
2236 }
2237
2238 const password = prompt(promptMessage)
21902239
21912240 // User cancelled
21922241 if (password === null) {
……@@ -2214,7 +2263,11 @@ async function promptForPassword() {
22142263 if (!res.ok) {
22152264 const error = await res.json()
22162265 if (res.status === 401) {
2217 alert('❌ Invalid password. Please try again.')
2266 if (isFirstTimeSetup) {
2267 alert('❌ Failed to set password. Please try again.')
2268 } else {
2269 alert('❌ Invalid password. Please try again.')
2270 }
22182271 attempts++
22192272 continue
22202273 }
……@@ -2228,7 +2281,8 @@ async function promptForPassword() {
22282281
22292282 // Show success message
22302283 if (data.migrated) {
2231 showSuccess('✅ Wallet unlocked and mnemonics encrypted!')
2284 alert('✅ SUCCESS!\n\nYour wallet mnemonics have been encrypted.\n\nREMEMBER YOUR PASSWORD - you will need it every time you access the wallet!')
2285 showSuccess('✅ Wallet encrypted and unlocked!')
22322286 } else {
22332287 showSuccess('✅ Wallet unlocked successfully')
22342288 }
wallet-server.cjs +26 -1
……@@ -1372,10 +1372,35 @@ const routes = {
13721372 }
13731373 }),
13741374
1375 // GET /api/wallet/encryption-status - Check if mnemonics are encrypted or plaintext
1376 'GET /api/wallet/encryption-status': async (req, res) => {
1377 try {
1378 // Check if wallet files exist
1379 if (!fs.existsSync(MNEMONIC_FILE)) {
1380 return sendJSON(res, { encrypted: null, initialized: false })
1381 }
1382
1383 // Read identity mnemonic file
1384 const identityContent = fs.readFileSync(MNEMONIC_FILE, 'utf8').trim()
1385
1386 // Check if plaintext (24 words separated by spaces)
1387 const isPlaintext = identityContent.includes(' ') && identityContent.split(' ').length === 24
1388
1389 sendJSON(res, {
1390 encrypted: !isPlaintext,
1391 initialized: true,
1392 needsMigration: isPlaintext
1393 })
1394 } catch (err) {
1395 console.error('Encryption status check error:', err)
1396 sendError(res, err.message)
1397 }
1398 },
1399
13751400 // POST /api/unlock - Unlock existing wallet with password (caches password in memory)
13761401 'POST /api/unlock': async (req, res) => {
13771402 try {
1378 const body = await readBody(req)
1403 const body = await parseBody(req)
13791404 const { password } = body
13801405
13811406 // Validate password provided