Skip to content

keygen/vanity silently overwrites ~/.tinyplace/config.json wallet key with no backup, stranding funds #200

Description

@Giri-Aayush

Summary

The keygen/vanity flow overwrites the existing wallet key in ~/.tinyplace/config.json with no guard, no backup, and no --force. A user (or agent) who already has a funded wallet and runs a vanity/keygen command silently loses their funded key, stranding any on-chain funds. Reported by a Discord user (Amrit) whose funded wallet was orphaned this way.

Problem

runKeygen (sdk/typescript/src/cli/keygen.ts:232) grinds a new wallet and calls persistSecretKey (keygen.ts:243), which writes { ...existing, secretKey: NEW } over config.json (keygen.ts:272). There is no existence check, no backup of the prior secretKey, and no --force gate. It even captures the previous wallet id (previousWallet: ctx.signer?.agentId, keygen.ts:252) but throws the old secret away. If the vanity prefix is not found in time, it saves a random wallet anyway (keygen.ts:248).

This is the only unguarded write path. For contrast:

  • makeContext only generates a key when none exists (if (!seed && managed), sdk/typescript/src/cli/context.ts:34).
  • The openclaw vault createWallet throws unless --force (sdk/plugin-openclaw/src/wallet.ts:123).

So keygen is the outlier. Because config.json IS the wallet (the docs warn "losing it loses the wallet"), this overwrite is a silent fund-loss footgun.

Steps to reproduce:

  1. Have a funded wallet (a config.json with a secretKey that holds funds).
  2. Run a vanity/keygen command (e.g. tinyplace keygen --vanity <prefix>), or have an agent do it.
  3. config.json.secretKey is replaced by the new wallet; the old funded key is gone with no backup; funds are stranded.

Real report: Discord user Amrit. His funded wallet 4gBCa7E... was orphaned this way; he could no longer sign from it, and the app then generated a new empty wallet that could not pay the USDC registration fee.

Suggested fix

Make keygen's persistSecretKey match the openclaw vault safety:

  • Refuse to overwrite an existing secretKey without an explicit --force.
  • Back up the old key first (e.g. config.json.bak, or print the old seed) before writing.
  • Warn before grinding, not only after.

Acceptance criteria

  • Running keygen/vanity when a secretKey already exists does not destroy it without --force.
  • When overwriting is intended (--force), the prior key is backed up first.
  • Test covering "existing funded key + keygen" preserves or backs up the old key.

Related

  • Reported via a TinyHumans Discord support ticket (Amrit).
  • Code: sdk/typescript/src/cli/keygen.ts:232-272; safe contrasts at sdk/typescript/src/cli/context.ts:34 and sdk/plugin-openclaw/src/wallet.ts:123.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions