{"_id":"@agntn/keys","name":"@agntn/keys","dist-tags":{"latest":"0.2.0"},"versions":{"0.2.0":{"name":"@agntn/keys","version":"0.2.0","description":"Cryptographic key generation, derivation, addresses, and message signing across blockchains","keywords":["addresses","aptos","bitcoin","blockchain","cardano","crypto","cryptocurrency","cryptography","keys","solana","sui","tron","typescript","wallet"],"homepage":"https://github.com/agntn/keys#readme","bugs":{"url":"https://github.com/agntn/keys/issues"},"license":"MIT","author":"oritwoen","repository":{"type":"git","url":"git+https://github.com/agntn/keys.git"},"bin":{"keys":"./dist/cli.mjs"},"type":"module","sideEffects":false,"main":"./dist/index.mjs","module":"./dist/index.mjs","types":"./dist/index.d.mts","exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./blockchains/*":{"types":"./dist/blockchains/*.d.mts","default":"./dist/blockchains/*.mjs"},"./bip32":{"types":"./dist/utils/bip32/index.d.mts","default":"./dist/utils/bip32/index.mjs"},"./bip39":{"types":"./dist/utils/bip39/index.d.mts","default":"./dist/utils/bip39/index.mjs"},"./slip10":{"types":"./dist/utils/slip10/index.d.mts","default":"./dist/utils/slip10/index.mjs"},"./mcp":{"types":"./dist/mcp.d.mts","default":"./dist/mcp.mjs"}},"publishConfig":{"access":"public"},"dependencies":{"@modelcontextprotocol/sdk":"1.30.0","@noble/curves":"2.4.0","@noble/hashes":"2.4.0","@scure/base":"2.4.0","@scure/bip32":"2.4.0","@scure/bip39":"2.4.0","citty":"0.2.2","consola":"3.4.2","micro-key-producer":"0.10.2","typebox":"1.3.23"},"devDependencies":{"@agntn/ox":"0.1.0","@earendil-works/pi-coding-agent":"0.84.4","@earendil-works/pi-tui":"0.84.4","@solana/web3.js":"^1.98.4","@types/node":"26.4.0","@vitest/coverage-v8":"4.1.11","changelogen":"0.6.2","ethers":"^6.17.0","obuild":"0.4.38","oxfmt":"0.65.0","oxlint":"1.80.0","oxlint-tsgolint":"^7.0.2001","tsx":"^4.23.13","typescript":"7.0.2","vitest":"4.1.11","ws":"^8.21.3"},"engines":{"node":">=24"},"pi":{"extensions":["./packages/pi/extensions/*.ts"]},"scripts":{"build":"obuild","dev":"vitest dev","release":"changelogen --release && git push --follow-tags","fmt":"pnpm build && oxlint . --fix && oxfmt --threads=8 .","lint":"pnpm build && oxlint . && oxfmt --threads=8 --check .","lint:fix":"pnpm build && oxlint . --fix && oxfmt --threads=8 .","test":"pnpm lint && pnpm test:types && pnpm build && pnpm test:ext && vitest run --coverage --maxWorkers=8 && node test/eval-mcp.mjs","test:types":"tsc --noEmit --skipLibCheck && tsc -p tsconfig.type-tests.json --noEmit --skipLibCheck","test:ext":"tsc -p tsconfig.extensions.json --noEmit","test:mcp":"pnpm build && node test/eval-mcp.mjs","playground":"tsx","playground:bip32":"tsx playground/bip32-demo.ts","playground:slip10":"tsx playground/slip10-demo.ts","playground:bip39":"tsx playground/bip39-demo.ts","playground:bip44":"tsx playground/bip44-demo.ts","playground:signing":"tsx playground/signing-demo.ts"},"_nodeVersion":"26.8.1","_id":"@agntn/keys@0.2.0","dist":{"integrity":"sha512-Wo1PzYGjLC4km8s4Ci6GVApk2WEojjJ4hcBn4zogivuP14EWaHcNSuBjcBkiJ5ESsnQp7jZj+ToK8rir751VPQ==","shasum":"a9e0af18819e22203300fa27235caa5acfcae390","tarball":"https://registry.npmjs.org/@agntn/keys/-/keys-0.2.0.tgz","fileCount":66,"unpackedSize":235683,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDzozwvOlfsDuk6TJUzfntzLpjTHqD9/e5lhtv17PZd0wIgJf+Ax0Vtd7+MkUDn3shp4Gc0b99V7P588f48J+ZTYoI="}]},"_npmUser":{"name":"oritwoen","email":"dominik.opyd+1@gmail.com"},"directories":{},"maintainers":[{"name":"oritwoen","email":"dominik.opyd+1@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/keys_0.2.0_1788978222301_0.17818632380991906"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-09T18:23:42.018Z","0.2.0":"2026-09-09T18:23:42.434Z","modified":"2026-09-09T18:23:42.690Z"},"maintainers":[{"name":"oritwoen","email":"dominik.opyd+1@gmail.com"}],"description":"Cryptographic key generation, derivation, addresses, and message signing across blockchains","homepage":"https://github.com/agntn/keys#readme","keywords":["addresses","aptos","bitcoin","blockchain","cardano","crypto","cryptocurrency","cryptography","keys","solana","sui","tron","typescript","wallet"],"repository":{"type":"git","url":"git+https://github.com/agntn/keys.git"},"author":"oritwoen","bugs":{"url":"https://github.com/agntn/keys/issues"},"license":"MIT","readme":"# @agntn/keys\n\n[![npm version](https://img.shields.io/npm/v/%40agntn%2Fkeys?style=flat&colorA=130f40&colorB=474787)](https://npmjs.com/package/@agntn/keys)\n[![npm downloads](https://img.shields.io/npm/dm/%40agntn%2Fkeys?style=flat&colorA=130f40&colorB=474787)](https://npm.chart.dev/@agntn/keys)\n[![license](https://img.shields.io/github/license/agntn/keys?style=flat&colorA=130f40&colorB=474787)](https://github.com/agntn/keys/blob/main/LICENSE.md)\n[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/agntn/keys)\n\nTyped key generation, address derivation, and message signing across ten blockchains and two curves.\n\n> [!WARNING]\n> **@agntn/keys is experimental.** The package name, public API, provider model, and tool surfaces may change before the first stable release. Pin exact versions if you build on it now.\n\n## Features\n\n- 🔑 **Key generation** - cryptographically secure private keys via Web Crypto API\n- 📫 **Address generation** - all major formats per chain (legacy, segwit, taproot, base58, hex)\n- ✅ **Address validation** - verify validity and checksums for every supported format\n- 💼 **Wallet construction** - generate a new wallet, or derive one from a private key or from a BIP39 mnemonic and derivation path\n- ✍️ **Message signing** - sign and verify with secp256k1 or ed25519\n- 🛤️ **BIP44 paths** - derivation path utilities for all supported chains\n- 🧩 **BIP39 puzzles** - validate phrases, narrow one missing word, and map words or indices across all 10 official lists\n- 🔌 **Lazy loading** - blockchain implementations load on demand for smaller bundles\n- 🤖 **MCP server** - the same 13 key, mnemonic, address, and signing tools over stdio\n- 📐 **Fully typed** - TypeScript definitions for every interface\n\n## Install\n\n```bash\npnpm add @agntn/keys\n```\n\n## Usage\n\nConcrete blockchain classes are lazy-loaded. The double-call pattern `blockchains.chain(options)()` first passes config, then imports and constructs the class.\n\n### Generate a wallet\n\n```ts\nimport { useBlockchain, blockchains } from \"@agntn/keys\";\n\nconst ethereum = await blockchains.ethereum()();\nconst chain = useBlockchain(ethereum);\n\nconst wallet = chain.generateWallet();\nconsole.log(wallet.keys.private); // hex private key\nconsole.log(wallet.keys.public); // hex public key\nconsole.log(wallet.address); // 0x... checksum address\n```\n\n### Bitcoin address types\n\n```ts\nimport { useBlockchain, blockchains } from \"@agntn/keys\";\n\nconst btc = useBlockchain(await blockchains.bitcoin()());\n\nconst privateKey = btc.generateKeyPrivate();\nconst publicKey = btc.getKeyPublic(privateKey);\n\nbtc.getAddress(publicKey); // legacy (1...)\nbtc.getAddress(publicKey, \"segwit\"); // native segwit (bc1q...)\nbtc.getAddress(publicKey, \"taproot\"); // taproot (bc1p...)\nbtc.getAddress(publicKey, \"p2sh\"); // pay-to-script-hash (3...)\nbtc.getAddress(publicKey, \"p2wsh\"); // witness script hash\n\n// testnet\nconst testnet = useBlockchain(await blockchains.bitcoin({ network: \"testnet\" })());\ntestnet.getAddress(publicKey, \"segwit\"); // tb1q...\n```\n\n### Litecoin\n\n```ts\nimport { blockchains } from \"@agntn/keys\";\n\nconst mnemonic =\n  \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\";\nconst ltc = await blockchains.litecoin()();\nconst wallet = ltc.deriveHDWallet(mnemonic, \"m/84'/2'/0'/0/0\");\nconst testnet = await blockchains.litecoin({ network: \"testnet\" })();\n```\n\nLitecoin uses the same five address types as Bitcoin, with `L`/`M`/`ltc1` on mainnet and `m` or `n`/`Q`/`tltc1` on testnet. Old P2SH prefixes (`3` and `2`) are accepted, not generated. Those old addresses overlap with Bitcoin, so validation alone cannot identify the chain. MWEB and regtest are outside this implementation.\n\nMessage signing uses the Litecoin Core message digest and returns a compact signature of 64 bytes as hex, not Core's recoverable base64 format. Verify against the public key with `verifyMessage`.\n\n### Decred\n\n```ts\nimport { blockchains } from \"@agntn/keys\";\n\nconst dcr = await blockchains.decred()();\nconst wallet = dcr.generateWallet();\nconst testnet = await blockchains.decred({ network: \"testnet\" })();\n```\n\nDecred supports ECDSA P2PKH addresses (`legacy`), with `Ds` on mainnet and `Ts` on testnet3. Both compressed and uncompressed public keys work. Other address formats and signature schemes are outside this implementation.\n\nMessage signing uses the Decred message digest and returns 64 bytes of compact r/s as hex, not the recoverable base64 format used by dcrd. Use `verifyMessage` with the public key. `deriveHDWallet` throws: Decred's HD derivation strips leading zeros, so ordinary BIP32 is not a safe substitute. BIP44 path generation uses coin type 42, not the historical type 20.\n\n### Sign and verify messages\n\n```ts\nimport { useBlockchain, blockchains } from \"@agntn/keys\";\n\nconst chain = useBlockchain(await blockchains.solana()());\n\nconst { keys } = chain.generateKeys();\nconst signature = chain.signMessage(\"hello\", keys.private);\nconst valid = chain.verifyMessage(\"hello\", signature, keys.public); // true\n```\n\n### EVM chains share addresses\n\n```ts\nimport { useBlockchain, blockchains } from \"@agntn/keys\";\n\nconst eth = useBlockchain(await blockchains.ethereum()());\nconst base = useBlockchain(await blockchains.base()());\n\nconst privateKey = eth.generateKeyPrivate();\nconst pubKey = eth.getKeyPublic(privateKey);\n\neth.getAddress(pubKey) === base.getAddress(pubKey); // true\n```\n\n### Derive HD keys\n\n```ts\nimport { mnemonicToSeed } from \"@agntn/keys/bip39\";\nimport { getMasterKeyFromSeed, deriveHDKey } from \"@agntn/keys/bip32\";\n\nconst seed = mnemonicToSeed(\n  \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\",\n);\nconst master = getMasterKeyFromSeed(seed);\nconst account = deriveHDKey(master, \"m/84'/0'/0'/0/0\");\n```\n\nUse `@agntn/keys/slip10` instead of `@agntn/keys/bip32` for ed25519 derivation.\n\n### Derive a wallet from a mnemonic\n\n```ts\nimport { useBlockchain, blockchains } from \"@agntn/keys\";\n\nconst mnemonic =\n  \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\";\n\nconst btc = useBlockchain(await blockchains.bitcoin()());\nbtc.deriveHDWallet(mnemonic, \"m/84'/0'/0'/0/0\").address; // bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu\n\nconst sol = useBlockchain(await blockchains.solana()());\nsol.deriveHDWallet(mnemonic, \"m/44'/501'/0'/0'\", { passphrase: \"TREZOR\" }).address;\n```\n\nsecp256k1 chains walk BIP32 and ed25519 chains walk SLIP-10, which accepts hardened segments only. Bitcoin and Litecoin read the address type off the purpose level (44, 49, 84, 86) unless one is passed. Decred throws because its HD derivation differs from standard BIP32. Cardano throws, because CIP-1852 starts from the entropy rather than the BIP39 seed.\n\n### Puzzle phrases with an invalid checksum\n\nA bad checksum does not always mean a wrong puzzle answer. The [claimed Bitcoin Movie Enigma solution](https://github.com/floflo777/open-crypto-puzzles/issues/24) has one. Repairing its last word derives a different wallet.\n\n`deriveHDWallet` rejects invalid checksums by default. Set `allowInvalidChecksum: true` explicitly to derive from the supplied words:\n\n```ts\nconst puzzleMnemonic =\n  \"path mad alien apology escape spare miss goddess leopard crime visit clock start first blade guard close barrel term screen matrix toy ghost shine\";\nconst puzzleWallet = btc.deriveHDWallet(puzzleMnemonic, \"m/84'/0'/0'/0/0\", {\n  allowInvalidChecksum: true,\n});\nconsole.log(puzzleWallet.address);\nconsole.log(puzzleWallet.warnings);\n```\n\nThis public, burned example produces `bc1q94ecsn0qk8lap2gefrycnms3ruepy889z969a6` and a checksum warning. The override still requires English BIP39 words and a count of 12, 15, 18, 21 or 24. It does not bypass path or chain restrictions. Whitespace collapsing and BIP39 NFKD normalization still apply. This is not arbitrary text hashing.\n\nMCP and Pi expose the same `allowInvalidChecksum` boolean on `keys_derive_hd_wallet`, defaulting to `false`. Warnings appear in tool text and Pi details. `keys_inspect_mnemonic` and the library's `inspectBIP39Mnemonic` export from `@agntn/keys/bip39` report `wordCountValid`, `wordlistValid` and `checksumValid` separately. A `null` checksum verdict means the shape or dictionary check failed. Entropy is returned by the inspection tool only for a fully valid mnemonic.\n\n### Recover one missing BIP39 word\n\n```ts\nimport { getMnemonicWordCandidates } from \"@agntn/keys/bip39\";\n\nconst candidates = getMnemonicWordCandidates(\n  \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon ?\",\n);\n```\n\nThe result only satisfies the BIP39 checksum. It does not prove that a candidate belongs to the wallet or puzzle target. Use this filter only when canonical BIP39 generation is established. It excludes the actual last word from the Movie Enigma solution above.\n\n### Map localized BIP39 words and indices\n\n```ts\nimport { lookupBIP39Indices, lookupBIP39Words } from \"@agntn/keys/bip39\";\n\nconst wordMatches = await lookupBIP39Words([\"orologio\", \"civetta\"], \"italian\");\nconst indexMatches = await lookupBIP39Indices([1, 1179, 2048], \"italian\", 1);\n```\n\nLanguage keys cover the 10 official BIP39 lists. Word lookup is case-insensitive and normalizes Unicode to NFKD. Index lookup uses base 0 by default and accepts base 1 explicitly.\n\n## MCP server\n\nThe package includes a stdio MCP server with the same 13 operations used by the Pi extension. After installing the package, configure an MCP client to run `keys mcp`. A checkout can run the built entry directly:\n\n```json\n{\n  \"mcpServers\": {\n    \"keys\": {\n      \"command\": \"node\",\n      \"args\": [\"/absolute/path/to/keys/dist/cli.mjs\", \"mcp\"]\n    }\n  }\n}\n```\n\nHosts that own their transport can import `createMcpServer` from `@agntn/keys/mcp`.\n\nThe server handles private keys, mnemonics, entropy, messages, and signatures as plaintext MCP arguments or results. They enter client transcripts. Use only public puzzle material or disposable test keys, never a wallet that controls real funds.\n\n## Supported Blockchains\n\n| Chain        | Curve              | Address Formats                      | Testnet |\n| ------------ | ------------------ | ------------------------------------ | ------- |\n| **Bitcoin**  | secp256k1          | legacy, p2sh, segwit, p2wsh, taproot | ✅      |\n| **Litecoin** | secp256k1          | legacy, p2sh, segwit, p2wsh, taproot | ✅      |\n| **Decred**   | secp256k1          | legacy ECDSA P2PKH                   | ✅      |\n| **Ethereum** | secp256k1          | EIP-55 checksum                      | -       |\n| **Base**     | secp256k1          | EVM-compatible                       | -       |\n| **Solana**   | ed25519            | base58                               | -       |\n| **Aptos**    | ed25519            | 0x-prefixed hex                      | -       |\n| **Cardano**  | ed25519            | payment, stake, enterprise           | ✅      |\n| **SUI**      | ed25519, secp256k1 | 0x-prefixed hex (blake2b)            | -       |\n| **TRON**     | secp256k1          | base58check                          | ✅      |\n\nAll chains support key generation, address derivation, address validation, and message signing.\n\n## Security\n\nBuilt on audited cryptographic packages from [@paulmillr](https://github.com/paulmillr):\n\n- [@noble/curves](https://github.com/paulmillr/noble-curves) - elliptic curve implementations (secp256k1, ed25519)\n- [@noble/hashes](https://github.com/paulmillr/noble-hashes) - SHA-256, Keccak, BLAKE-256, BLAKE2b, SHA3\n- [@scure/base](https://github.com/paulmillr/scure-base) - base58, bech32, hex encoding\n- [@scure/bip32](https://github.com/paulmillr/scure-bip32) - HD wallet key derivation\n- [micro-key-producer](https://github.com/paulmillr/micro-key-producer) - SLIP-0010 for ed25519\n\n> [!CAUTION]\n> **Never use this with real funds or with any wallet that has ever been used.** Generated and signed material is handled as plaintext; treat every key it touches as burned the moment it is produced. Generate fresh throwaway keys for testing only and assume anything passing through `@agntn/keys` is compromised. Keys that control real funds belong on a hardware wallet, never in a process, log, or agent transcript.\n\n## License\n\n[MIT](./LICENSE.md)\n","readmeFilename":"","_rev":"1-f2a3ceef0e233c99fe13b9612ce0592f"}