### --------- helpers ---------- function _need for c in $argv if not type -q $c echo (set_color red)"Error:"(set_color normal)" missing command: $c" >&2 return 1 end end end function _banner -a MSG set_color cyan; echo; echo "==> $MSG"; set_color normal end function _k8s_name -a S set s (string lower -- $S) set s (string replace -ar -- '[^a-z0-9-]' '-' $s) set s (string replace -r -- '^-+|(-)+$' '$1' $s) echo $s end function _require -a WHAT VAL if test -z "$VAL" echo (set_color red)"Error:"(set_color normal)" $WHAT cannot be empty." >&2 return 1 end end function _sc_list kubectl get storageclass -o json 2>/dev/null | jq -r ' .items[] | [ .metadata.name, .provisioner, (.reclaimPolicy // "Delete"), ( .metadata.annotations["storageclass.kubernetes.io/is-default-class"] == "true" or .metadata.annotations["storageclass.beta.kubernetes.io/is-default-class"] == "true") ] | @tsv' end function _choose_sc set lines (_sc_list) if test (count $lines) -eq 0 echo (set_color red)"Error:"(set_color normal)" No StorageClasses found." >&2 return 1 end echo "" >&2 echo "==> Available StorageClasses" >&2 echo " # | name | provisioner | reclaim | default" >&2 echo "----+----------------------+------------------------+---------+--------" >&2 set i 1 for l in $lines set name (echo $l | awk -F'\t' '{print $1}') set prov (echo $l | awk -F'\t' '{print $2}') set rec (echo $l | awk -F'\t' '{print $3}') set def (echo $l | awk -F'\t' '{print $4}') printf " %2d | %-20s | %-22s | %-7s | %s\n" $i $name $prov $rec $def >&2 set i (math $i + 1) end set prefer (printf "%s\n" $lines | awk -F'\t' '$4=="true"{print $1}' | head -n1) if test -z "$prefer" set prefer (printf "%s\n" $lines | awk -F'\t' '{print $1}' | head -n1) end read -P "Pick StorageClass by number or name (blank = $prefer): " choice if test -z "$choice" echo $prefer return 0 end if string match -qr '^[0-9]+$' -- $choice set idx (math $choice) if test $idx -lt 1 -o $idx -gt (count $lines) echo (set_color red)"Error:"(set_color normal)" Choice out of range." >&2 return 1 end printf "%s\n" $lines[$idx] | awk -F'\t' '{print $1}' else printf "%s\n" $lines | awk -F'\t' -v want="$choice" '$1==want{print $1}' end end ### --------- main workflow ---------- function suiwallet_bootstrap --description "Create a Sui wallet (PVC+Deployment) and generate a new address" _need kubectl jq awk; or return 1 _banner "Sui wallet bootstrap" read -P "Namespace [crypto]: " ns if test -z "$ns"; set ns crypto; end set ns (_k8s_name $ns) echo "Ensuring namespace '$ns' exists…" if not kubectl get ns $ns >/dev/null 2>&1 kubectl create ns $ns; or return 1 end kubectl get storageclass 2>/dev/null echo "TIP: Prefer a Retain StorageClass so deleting the pod won't delete keys." set sc (_choose_sc); or return 1 read -P "Wallet name (e.g. 'brad' → wallet-sui-brad): " wallet_raw _require "Wallet name" $wallet_raw; or return 1 set base (_k8s_name $wallet_raw) set app "wallet-sui-$base" set pvc $app read -P "Network [mainnet|testnet|devnet] [mainnet]: " net if test -z "$net"; set net mainnet; end switch $net case mainnet set rpc_url https://fullnode.mainnet.sui.io:443 case testnet set rpc_url https://fullnode.testnet.sui.io:443 case devnet set rpc_url https://fullnode.devnet.sui.io:443 case '*' echo (set_color red)"Error:"(set_color normal)" network must be mainnet|testnet|devnet"; return 1 end read -P "Container image [mysten/sui-tools:latest]: " image if test -z "$image"; set image mysten/sui-tools:latest; end read -P "Address alias to create [main]: " alias if test -z "$alias"; set alias main; end read -P "Mnemonic word length [24]: " wl if test -z "$wl"; set wl 24; end read -P "Store mnemonic as a Kubernetes Secret? (NOT recommended) (y/N): " store_mn set store_mn (string lower -- $store_mn) _banner "Summary" echo " Namespace: $ns" echo " App name: $app" echo " StorageClass: $sc" echo " PVC: $pvc" echo " Image: $image" echo " Network: $net" echo " RPC URL: $rpc_url" echo " New alias: $alias (word length: $wl)" read -P "Proceed? [y/N]: " ok if not string match -qi 'y*' -- $ok echo "Aborted."; return 1 end _banner "Applying PVC" kubectl -n $ns get pvc $pvc >/dev/null 2>&1; or begin printf "%s\n" \ "apiVersion: v1 kind: PersistentVolumeClaim metadata: name: $pvc namespace: $ns spec: accessModes: [\"ReadWriteOnce\"] storageClassName: $sc resources: requests: storage: 1Gi" | kubectl apply -f -; or return 1 end _banner "Applying Deployment" printf "%s\n" \ "apiVersion: apps/v1 kind: Deployment metadata: name: $app namespace: $ns labels: { app: $app } spec: replicas: 1 selector: { matchLabels: { app: $app } } template: metadata: labels: { app: $app } spec: containers: - name: sui-tools image: $image imagePullPolicy: IfNotPresent command: [\"bash\",\"-lc\",\"sleep infinity\"] volumeMounts: - name: data mountPath: /root/.sui resources: requests: { cpu: 50m, memory: 128Mi } limits: { cpu: 1, memory: 512Mi } volumes: - name: data persistentVolumeClaim: { claimName: $pvc }" | kubectl apply -f -; or return 1 _banner "Waiting for rollout" kubectl -n $ns rollout status deploy/$app --timeout=120s; or return 1 _banner "Initializing Sui client env ($net) and creating a new address" # new-env: writes client.yaml; new-address: writes to sui.keystore and prints the phrase set OUT (mktemp) kubectl -n $ns exec deploy/$app -- bash -lc \ (printf 'mkdir -p /root/.sui/sui_config ; sui client new-env --alias %s --rpc %s >/dev/null 2>&1 || true ; sui client new-address ed25519 --word-length %s --alias %s' \ $net $rpc_url $wl $alias) | tee $OUT set ADDR (kubectl -n $ns exec deploy/$app -- bash -lc \ (printf 'sui client addresses | awk "/%s/{print \$NF}" | tail -n1' $alias)) echo echo (set_color yellow)"IMPORTANT — Secret Recovery Phrase (write down & store offline):"(set_color normal) grep -E 'Secret Recovery Phrase|Mnemon' $OUT -A2 | sed -e 's/^\s\+//' echo echo "Address alias '$alias' appears to be: $ADDR" rm -f $OUT if test "$store_mn" = "y" -o "$store_mn" = "yes" set MN (kubectl -n $ns exec deploy/$app -- bash -lc \ (printf 'sui client addresses >/dev/null 2>&1 ; sui client new-address ed25519 --word-length %s --alias %s --json 2>/dev/null | jq -r .secretRecoveryPhrase || true' $wl "${alias}-dup")) if test -n "$MN" -a "$MN" != "null" kubectl -n $ns create secret generic {$app}-mnemonic \ --from-literal=mnemonic="$MN" --dry-run=client -o yaml | kubectl -n $ns apply -f - echo (set_color yellow)"Stored mnemonic in Secret $ns/{$app}-mnemonic. Treat your cluster as sensitive."(set_color normal) # clean the duplicate alias we created to harvest JSON kubectl -n $ns exec deploy/$app -- bash -lc "sui client remove-address --alias ${alias}-dup >/dev/null 2>&1 || true" >/dev/null 2>&1 else echo (set_color red)"Warning:"(set_color normal)" could not capture mnemonic via --json, not storing." end end echo echo "Done. Files inside the pod:" kubectl -n $ns exec deploy/$app -- bash -lc 'ls -l /root/.sui/sui_config || true' echo echo "Useful commands:" echo " suiwallet_addresses $ns $app" echo " suiwallet_export_keystore $ns $app ./sui.keystore.$base" echo " kubectl -n $ns exec -it deploy/$app -- bash # interactive CLI" end ### --------- utilities ---------- function suiwallet_addresses -a NS APP if test -z "$NS"; set NS crypto; end if test -z "$APP"; set APP wallet-sui-main; end kubectl -n $NS exec deploy/$APP -- bash -lc 'sui client addresses || true' end function suiwallet_export_keystore -a NS APP DEST if test -z "$NS"; set NS crypto; end if test -z "$APP"; set APP wallet-sui-main; end if test -z "$DEST"; set DEST ./sui.keystore; end echo "Copying keystore to $DEST (keep it safe!)" kubectl -n $NS cp deploy/$APP:/root/.sui/sui_config/sui.keystore $DEST end function suiwallet_status -a NS APP if test -z "$NS"; set NS crypto; end if test -z "$APP"; set APP wallet-sui-main; end kubectl -n $NS get deploy,po,pvc -l app=$APP -o wide end function suiwallet_stop -a NS APP if test -z "$NS"; set NS crypto; end if test -z "$APP"; set APP wallet-sui-main; end kubectl -n $NS delete deploy $APP --ignore-not-found echo "Stopped $NS/$APP (PVC kept)." end function suiwallet_purge -a NS APP PVC if test -z "$NS"; set NS crypto; end if test -z "$APP"; set APP wallet-sui-main; end if test -z "$PVC"; set PVC $APP; end echo "Type to confirm: PURGE $NS $PVC" read CONFIRM if test "$CONFIRM" != "PURGE $NS $PVC" echo "Aborted."; return 1 end kubectl -n $NS delete deploy $APP --ignore-not-found kubectl -n $NS delete pvc $PVC --ignore-not-found --wait=true end