OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-wallet commits / 79373f1

Add two-channel deployment workflow (testing vs production)
**New Features:** - Testing channel: hyper-wallet-test (pear-seed:3) - Production channel: wdk-bare-wallet (pear-seed:0) - Deploy script: --testing and --production flags - Safety: Always test before production push **Changes:** - scripts/pear-deploy.sh: Support --testing and --production modes - .claude/tmux-registry.json: Updated window 3 for testing seed - .claude/DEPLOYMENT_WORKFLOW.md: Complete deployment guide - lib/wallet-storage.cjs: Multi-address contact support (WIP) **Workflow:** 1. Deploy to testing: ./scripts/pear-deploy.sh --testing "message" 2. Verify on tabertron + ashatron 3. Get approval 4. Deploy to production: ./scripts/pear-deploy.sh --production "message" Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
79373f1048d2c2458688b0b84ea548fae88460f8
by plankatron <fleeky@gmail.com> · 2026-01-27T21:32:36.000Z

Changed files (4) +562 -30

.claude/DEPLOYMENT_WORKFLOW.md +319 -0
……@@ -0,0 +1,319 @@
1# Hyper-Wallet Deployment Workflow
2
3**Status**: Active (as of 2026-01-27)
4**Purpose**: Two-channel deployment strategy for safe production releases
5
6---
7
8## Overview
9
10Hyper-wallet uses a **two-channel deployment system** to ensure stability:
11
121. **Testing Channel** (`hyper-wallet-test`) - Active development and verification
132. **Production Channel** (`wdk-bare-wallet`) - Stable public release
14
15## Channels
16
17### Testing Channel
18
19- **Name**: `hyper-wallet-test`
20- **Purpose**: Pre-production verification
21- **Tmux Window**: `pear-seed:3` (hw-test)
22- **Seeder Command**: `pear seed hyper-wallet-test .`
23- **Deploy Command**: `./scripts/pear-deploy.sh --testing "message"`
24- **Pear Key**: Generated on first stage
25
26**When to use:**
27- All new features
28- Bug fixes
29- Security updates
30- UI changes
31- ANY code changes
32
33### Production Channel
34
35- **Name**: `wdk-bare-wallet`
36- **Purpose**: Stable public release
37- **Tmux Window**: `pear-seed:0` (hyper-wallet)
38- **Seeder Command**: `pear seed pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso .`
39- **Deploy Command**: `./scripts/pear-deploy.sh --production "message"`
40- **Pear Key**: `pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso`
41
42**When to use:**
43- ONLY after testing verification complete
44- User-facing releases
45- Requires explicit approval
46
47---
48
49## Deployment Process
50
51### Step 1: Deploy to Testing
52
53```bash
54cd /storage/projects/code/hypercore-kb/hyper-fleeky/hyper-wallet
55
56# Create wallet backup FIRST
57cp -r ~/.hyper-wallet/ ~/.hyper-wallet.backup-$(date +%Y%m%d-%H%M%S)
58
59# Deploy to testing channel
60./scripts/pear-deploy.sh --testing "Fix contact list multi-address support"
61```
62
63**What happens:**
641. Git commit created with message + "Deployment: testing" tag
652. Pear stage creates new version
663. Pear release makes it default for testing channel
674. Testing seeder (pear-seed:3) automatically restarted
685. Testing pear:// key displayed
69
70### Step 2: Verify on Testing
71
72#### Local Testing (tabertron)
73
74```bash
75# Run testing version
76pear run hyper-wallet-test web 4004 --network
77
78# Test checklist:
79# - Web UI loads
80# - Password unlock works
81# - All features work as expected
82# - No console errors
83```
84
85#### Network Testing (ashatron)
86
87```bash
88# On ashatron.local:
89pear run hyper-wallet-test web 4004 --network
90
91# Access from browser: http://ashatron.local:4004
92
93# Test checklist:
94# - Network access works
95# - CORS properly configured
96# - Multi-device features work
97# - Sync works correctly
98```
99
100#### Multi-Device Testing (if needed)
101
102```bash
103# Test device pairing between tabertron and ashatron
104# Test transaction sync
105# Test contact sync
106```
107
108### Step 3: Get Approval
109
110**CRITICAL**: Do NOT deploy to production without explicit approval.
111
112Ask: **"Testing verification complete. Ready to push to production?"**
113
114### Step 4: Deploy to Production
115
116**ONLY after approval received:**
117
118```bash
119cd /storage/projects/code/hypercore-kb/hyper-fleeky/hyper-wallet
120
121# Deploy to production
122./scripts/pear-deploy.sh --production "Fix contact list multi-address support (verified on testing)"
123```
124
125**What happens:**
1261. Git commit created with message + "Deployment: production" tag
1272. Pear stage creates new version for production channel
1283. Pear release makes it default for wdk-bare-wallet
1294. Production seeder (pear-seed:0) automatically restarted
1305. Production pear:// key displayed
131
132### Step 5: Verify Production
133
134```bash
135# Check production seeder running
136tmux attach -t pear-seed
137# Window 0 should show: "pear seed pear://18x3enb..."
138
139# Test production access
140pear run pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso web 4004 --network
141```
142
143### Step 6: Update Documentation
144
1451. Update `.claude/tmux-registry.json`:
146 - Latest version number
147 - Last deployed date
148 - Features summary
149
1502. Update `CHANGELOG.md`:
151 - Version number
152 - Changes description
153 - Date
154
1553. Announce to users (if public release)
156
157---
158
159## Quick Reference
160
161### Deploy Commands
162
163```bash
164# Testing deployment
165./scripts/pear-deploy.sh --testing "your message"
166./scripts/pear-deploy.sh -t "your message"
167
168# Production deployment
169./scripts/pear-deploy.sh --production "your message"
170./scripts/pear-deploy.sh -p "your message"
171```
172
173### Check What's Running
174
175```bash
176# List all seeders
177tmux list-windows -t pear-seed
178
179# Attach to seeder session
180tmux attach -t pear-seed
181
182# Check specific window
183tmux capture-pane -t pear-seed:0 -p # Production
184tmux capture-pane -t pear-seed:3 -p # Testing
185```
186
187### Emergency Rollback
188
189**If production breaks:**
190
191```bash
192# Option 1: Redeploy previous version
193cd /storage/projects/code/hypercore-kb/hyper-fleeky/hyper-wallet
194git log --oneline # Find previous commit
195git checkout <previous-commit>
196./scripts/pear-deploy.sh --production "Rollback to stable version"
197
198# Option 2: Clear cache and force re-download
199rm -rf ~/.config/pear/app-storage/*
200pear run pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso --checkout=latest web 4004
201```
202
203---
204
205## Testing Checklist
206
207### Before Testing Deployment
208
209- [ ] All unit tests pass
210- [ ] Bare mode works: `bare hyper-wallet.cjs web 4004`
211- [ ] Pear local mode works: `pear run -d . web 4004`
212- [ ] Wallet backup created
213- [ ] CHANGELOG.md updated
214
215### Testing Verification
216
217- [ ] Web UI loads without errors
218- [ ] Password unlock works
219- [ ] Balance displays correctly
220- [ ] Send transaction works
221- [ ] Contact list works
222- [ ] Receive QR code displays
223- [ ] Transaction history loads
224- [ ] Settings work
225- [ ] No console errors
226- [ ] Network access works (test from ashatron)
227- [ ] Multi-device sync works (if applicable)
228
229### Before Production Deployment
230
231- [ ] Testing verification complete
232- [ ] Multi-device testing complete (if applicable)
233- [ ] No critical bugs found
234- [ ] User approval obtained
235- [ ] Ready to announce
236
237### After Production Deployment
238
239- [ ] Production seeder running (pear-seed:0)
240- [ ] Production pear:// key loads correctly
241- [ ] tmux-registry.json updated
242- [ ] CHANGELOG.md finalized
243- [ ] Users notified (if applicable)
244
245---
246
247## Troubleshooting
248
249### Testing Channel Not Working
250
251```bash
252# Recreate testing channel from scratch
253cd /storage/projects/code/hypercore-kb/hyper-fleeky/hyper-wallet
254pear stage hyper-wallet-test .
255pear release hyper-wallet-test
256
257# Start seeder manually in pear-seed:3
258tmux send-keys -t pear-seed:3 "pear seed hyper-wallet-test ." Enter
259```
260
261### Production Seeder Not Restarting
262
263```bash
264# Manually restart production seeder
265tmux send-keys -t pear-seed:0 C-c
266sleep 2
267tmux send-keys -t pear-seed:0 "pear seed pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso ." Enter
268```
269
270### Cache Issues
271
272```bash
273# Clear Pear cache (forces fresh download)
274rm -rf ~/.config/pear/app-storage/*
275
276# Run with --checkout=latest flag
277pear run hyper-wallet-test --checkout=latest web 4004
278```
279
280---
281
282## Safety Rules
283
284### NEVER
285
286- ❌ Deploy directly to production without testing
287- ❌ Skip testing verification steps
288- ❌ Deploy without user approval (when applicable)
289- ❌ Deploy without wallet backup
290- ❌ Push breaking changes without warning
291
292### ALWAYS
293
294- ✅ Deploy to testing FIRST
295- ✅ Verify on multiple devices (tabertron + ashatron)
296- ✅ Create wallet backup before deploying
297- ✅ Test all critical features
298- ✅ Get approval before production push
299- ✅ Update documentation after deployment
300
301---
302
303## Version History
304
305- **2026-01-27**: Two-channel system established
306 - Testing: `hyper-wallet-test` (pear-seed:3)
307 - Production: `wdk-bare-wallet` (pear-seed:0)
308 - Scripts updated: `scripts/pear-deploy.sh`
309 - Documentation: This file + `.claude/skills/hyper-wallet/SKILL.md`
310
311---
312
313## Related Documentation
314
315- `.claude/skills/hyper-wallet/SKILL.md` - Complete skill documentation
316- `.claude/tmux-registry.json` - Tmux session tracking
317- `.claude/rules/08-tmux-sessions.md` - Tmux session management
318- `scripts/pear-deploy.sh` - Deployment automation script
319- `CHANGELOG.md` - Version history
.claude/tmux-registry.json +11 -4
……@@ -5,9 +5,10 @@
55 "windows": {
66 "0": {
77 "name": "hyper-wallet",
8 "purpose": "Seeding pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso",
8 "purpose": "PRODUCTION: Seeding pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso (wdk-bare-wallet)",
99 "command": "pear seed pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso .",
1010 "persistent": true,
11 "channel": "wdk-bare-wallet",
1112 "latest_version": "11472",
1213 "last_deployed": "2026-01-27",
1314 "features": "Phase 2 COMPLETE: Auth + Encryption + Validation + Rate Limiting + CORS + Signed Invites + Password Unlock UI ✅"
……@@ -23,9 +24,15 @@
2324 "persistent": true
2425 },
2526 "3": {
26 "name": "hw-pear-",
27 "purpose": "Experimental hyper-wallet seeding",
28 "persistent": false
27 "name": "hw-test",
28 "purpose": "TESTING: Seeding hyper-wallet test channel (hyper-wallet-test)",
29 "command": "pear seed hyper-wallet-test .",
30 "persistent": true,
31 "channel": "hyper-wallet-test",
32 "latest_version": "TBD",
33 "last_deployed": "TBD",
34 "features": "Testing channel for pre-production verification",
35 "notes": "Deploy here first with --testing flag, verify, then promote to production (window 0)"
2936 }
3037 }
3138 },
lib/wallet-storage.cjs +139 -2
……@@ -1089,13 +1089,147 @@ async function deleteContact(id) {
10891089
10901090/**
10911091 * Find contact by address (helper for transaction display)
1092 * UPDATED: Supports both old (single address) and new (multiple addresses) format
10921093 * @param {string} address - Blockchain address
10931094 * @param {string} chain - Chain name
10941095 * @returns {Promise<Object|null>} - Contact or null
10951096 */
10961097async function findContactByAddress(address, chain) {
1097 const contacts = await getContacts(chain)
1098 return contacts.find(c => c.address.toLowerCase() === address.toLowerCase()) || null
1098 const contacts = await getContacts()
1099
1100 return contacts.find(c => {
1101 // Old format: contact.address
1102 if (c.address && c.address.toLowerCase() === address.toLowerCase() && c.chain === chain) {
1103 return true
1104 }
1105
1106 // New format: contact.addresses[]
1107 if (c.addresses && Array.isArray(c.addresses)) {
1108 return c.addresses.some(addr =>
1109 addr.chain === chain && addr.address.toLowerCase() === address.toLowerCase()
1110 )
1111 }
1112
1113 return false
1114 }) || null
1115}
1116
1117/**
1118 * Add an address to an existing contact
1119 * @param {string} contactId - Contact ID
1120 * @param {Object} addressInfo - Address details
1121 * @param {string} addressInfo.chain - Chain name (bitcoin, ethereum)
1122 * @param {string} addressInfo.address - Blockchain address
1123 * @param {string} addressInfo.network - Network (testnet, mainnet)
1124 * @returns {Promise<Object>} - Updated contact
1125 */
1126async function addAddressToContact(contactId, { chain, address, network }) {
1127 if (!autobase) {
1128 throw new Error('Storage not initialized')
1129 }
1130
1131 const existing = await getContactById(contactId)
1132 if (!existing) {
1133 throw new Error('Contact not found')
1134 }
1135
1136 // Migrate old format to new format if needed
1137 let addresses = []
1138 if (existing.addresses && Array.isArray(existing.addresses)) {
1139 addresses = existing.addresses
1140 } else if (existing.address && existing.chain) {
1141 // Convert old single-address format
1142 addresses = [{
1143 chain: existing.chain,
1144 address: existing.address,
1145 network: existing.network || network
1146 }]
1147 }
1148
1149 // Check if address already exists for this chain
1150 const existingAddr = addresses.find(a =>
1151 a.chain === chain && a.address.toLowerCase() === address.toLowerCase()
1152 )
1153 if (existingAddr) {
1154 throw new Error(`Address already exists for ${chain}`)
1155 }
1156
1157 // Add new address
1158 addresses.push({ chain, address, network })
1159
1160 // Update contact with new addresses array
1161 const updated = {
1162 ...existing,
1163 addresses,
1164 // Remove old format fields
1165 address: undefined,
1166 chain: undefined,
1167 modifiedAt: Date.now()
1168 }
1169
1170 const op = {
1171 type: 'put-contact',
1172 key: `contact:${contactId}`,
1173 value: updated
1174 }
1175
1176 await autobase.append(JSON.stringify(op))
1177
1178 // Wait for view to update
1179 await new Promise(resolve => setTimeout(resolve, 100))
1180
1181 return updated
1182}
1183
1184/**
1185 * Remove an address from a contact
1186 * @param {string} contactId - Contact ID
1187 * @param {string} chain - Chain name to remove
1188 * @returns {Promise<Object>} - Updated contact
1189 */
1190async function removeAddressFromContact(contactId, chain) {
1191 if (!autobase) {
1192 throw new Error('Storage not initialized')
1193 }
1194
1195 const existing = await getContactById(contactId)
1196 if (!existing) {
1197 throw new Error('Contact not found')
1198 }
1199
1200 if (!existing.addresses || !Array.isArray(existing.addresses)) {
1201 throw new Error('Contact has no addresses array')
1202 }
1203
1204 // Filter out the address for this chain
1205 const addresses = existing.addresses.filter(a => a.chain !== chain)
1206
1207 if (addresses.length === existing.addresses.length) {
1208 throw new Error(`No address found for chain: ${chain}`)
1209 }
1210
1211 if (addresses.length === 0) {
1212 throw new Error('Cannot remove last address. Delete contact instead.')
1213 }
1214
1215 const updated = {
1216 ...existing,
1217 addresses,
1218 modifiedAt: Date.now()
1219 }
1220
1221 const op = {
1222 type: 'put-contact',
1223 key: `contact:${contactId}`,
1224 value: updated
1225 }
1226
1227 await autobase.append(JSON.stringify(op))
1228
1229 // Wait for view to update
1230 await new Promise(resolve => setTimeout(resolve, 100))
1231
1232 return updated
10991233}
11001234
11011235/**
……@@ -1827,5 +1961,8 @@ module.exports = {
18271961 updateContact,
18281962 deleteContact,
18291963 findContactByAddress,
1964 // NEW: Multi-address contact support
1965 addAddressToContact,
1966 removeAddressFromContact,
18301967 closeStorage
18311968}
scripts/pear-deploy.sh +93 -24
……@@ -1,14 +1,55 @@
11#!/bin/bash
2# Pear Deployment Script
3# Usage: ./scripts/pear-deploy.sh "commit message"
2# Pear Deployment Script with Testing vs Production Channels
3# Usage:
4# ./scripts/pear-deploy.sh --testing "commit message"
5# ./scripts/pear-deploy.sh --production "commit message"
46
57set -e # Exit on error
68
7PEAR_KEY="pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso"
9# Deployment channel configuration
10TESTING_KEY="hyper-wallet-test"
11PRODUCTION_KEY="wdk-bare-wallet"
812APP_DIR="/storage/projects/code/hypercore-kb/hyper-fleeky/hyper-wallet"
913
14# Parse arguments
15MODE=""
16MESSAGE=""
17
18if [ "$1" == "--testing" ] || [ "$1" == "-t" ]; then
19 MODE="testing"
20 MESSAGE="$2"
21 PEAR_KEY="$TESTING_KEY"
22 TMUX_WINDOW="pear-seed:3" # hw-test window
23elif [ "$1" == "--production" ] || [ "$1" == "-p" ]; then
24 MODE="production"
25 MESSAGE="$2"
26 PEAR_KEY="$PRODUCTION_KEY"
27 TMUX_WINDOW="pear-seed:0" # hyper-wallet production window
28else
29 echo "❌ Invalid mode"
30 echo ""
31 echo "Usage:"
32 echo " ./scripts/pear-deploy.sh --testing \"commit message\""
33 echo " ./scripts/pear-deploy.sh --production \"commit message\""
34 echo ""
35 echo "Channels:"
36 echo " --testing (-t) → $TESTING_KEY (tmux: pear-seed:3)"
37 echo " --production (-p) → $PRODUCTION_KEY (tmux: pear-seed:0)"
38 echo ""
39 exit 1
40fi
41
42if [ -z "$MESSAGE" ]; then
43 echo "❌ Commit message required"
44 echo "Usage: ./scripts/pear-deploy.sh --$MODE \"your commit message\""
45 exit 1
46fi
47
1048echo "🔧 Pear Deployment Workflow"
1149echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
50echo " Mode: $MODE"
51echo " Channel: $PEAR_KEY"
52echo " Tmux: $TMUX_WINDOW"
1253echo ""
1354
1455# 1. Check we're in the right directory
……@@ -21,12 +62,6 @@ fi
2162if [ -n "$(git status --porcelain)" ]; then
2263 echo "📝 Step 1: Git Commit"
2364
24 if [ -z "$1" ]; then
25 echo "❌ Commit message required"
26 echo "Usage: ./scripts/pear-deploy.sh \"your commit message\""
27 exit 1
28 fi
29
3065 # Show what will be committed
3166 echo ""
3267 echo "Changes to commit:"
……@@ -38,19 +73,21 @@ if [ -n "$(git status --porcelain)" ]; then
3873
3974 # Commit with co-authorship
4075 git commit -m "$(cat <<EOF
41$1
76$MESSAGE
77
78Deployment: $MODE ($PEAR_KEY)
4279
4380Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
4481EOF
4582)"
4683
47 echo "✅ Committed: $1"
84 echo "✅ Committed: $MESSAGE"
4885else
4986 echo "✅ Step 1: No uncommitted changes"
5087fi
5188
5289echo ""
53echo "📦 Step 2: Pear Stage"
90echo "📦 Step 2: Pear Stage ($MODE)"
5491STAGE_OUTPUT=$(pear stage $PEAR_KEY . 2>&1)
5592echo "$STAGE_OUTPUT"
5693
……@@ -58,27 +95,59 @@ echo "$STAGE_OUTPUT"
5895LATEST=$(echo "$STAGE_OUTPUT" | grep "Latest:" | awk '{print $2}')
5996RELEASE=$(echo "$STAGE_OUTPUT" | grep "Release:" | awk '{print $2}')
6097
98# Get the actual pear:// key (only needed for testing on first run)
99PEAR_HASH=$(pear info $PEAR_KEY --json 2>/dev/null | grep -oP '"key":"pear://\K[^"]+' || echo "unknown")
100
61101echo ""
62echo "🚀 Step 3: Pear Release"
102echo "🚀 Step 3: Pear Release ($MODE)"
63103pear release $PEAR_KEY
64104
65105echo ""
66echo "🌱 Step 4: Update Seeder (tmux: pear-seed:0)"
67tmux send-keys -t pear-seed:0 C-c
68sleep 2
69tmux send-keys -t pear-seed:0 "pear seed $PEAR_KEY ." Enter
106echo "🌱 Step 4: Update Seeder (tmux: $TMUX_WINDOW)"
107
108# Check if tmux window exists
109if tmux list-windows -t pear-seed 2>/dev/null | grep -q "^${TMUX_WINDOW##*:}:"; then
110 tmux send-keys -t $TMUX_WINDOW C-c
111 sleep 2
112
113 if [ "$MODE" == "testing" ]; then
114 tmux send-keys -t $TMUX_WINDOW "pear seed $PEAR_KEY ." Enter
115 else
116 tmux send-keys -t $TMUX_WINDOW "pear seed pear://18x3enb54ttcr6uuhg5hh1cqzwi8ydnm658fmgyyjkzoqxd1ybso ." Enter
117 fi
118
119 echo "✅ Seeder updated in $TMUX_WINDOW"
120else
121 echo "⚠️ Warning: tmux window $TMUX_WINDOW not found"
122 echo " You may need to start the seeder manually"
123fi
70124
71125echo ""
72126echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
73127echo "✅ Deployment Complete!"
74128echo ""
75129echo "📊 Summary:"
76echo " Commit: $1"
130echo " Mode: $MODE"
131echo " Channel: $PEAR_KEY"
132if [ "$PEAR_HASH" != "unknown" ]; then
133 echo " Key: pear://$PEAR_HASH"
134fi
135echo " Commit: $MESSAGE"
77136echo " Version: $LATEST"
78echo " Seeding: Updated in tmux window pear-seed:0"
79echo ""
80echo "🧪 Next Steps:"
81echo " 1. Check tmux session 'pear-dev' for testing"
82echo " 2. Run: ./scripts/pear-test.sh"
83echo " 3. Or manually: tmux attach -t pear-dev"
137echo " Seeding: $TMUX_WINDOW"
84138echo ""
139
140if [ "$MODE" == "testing" ]; then
141 echo "🧪 Testing Deployment - Next Steps:"
142 echo " 1. Test on ashatron.local"
143 echo " 2. Test all Phase 2 security features"
144 echo " 3. Once verified, deploy to production:"
145 echo " ./scripts/pear-deploy.sh --production \"Phase 2 complete\""
146 echo ""
147else
148 echo "🚀 Production Deployment - Next Steps:"
149 echo " 1. Announce to users"
150 echo " 2. Monitor for issues"
151 echo " 3. Update documentation with new version"
152 echo ""
153fi