Cinder Wallet
An open-source, self-custodial Solana wallet extension, small enough to read end to end. The popup never holds a keypair. Not audited.
- Role
- Author and maintainer
- When
- Aug 2025–now
- Client
- Personal open source (MIT)
- Stack
- TypeScript, React, Chrome Manifest V3, Wallet Standard, Solana web3.js, Redux, and 4 more
- Status
- Open source
Context
I wanted a Solana wallet small and plain enough to read end to end, with its security boundaries in the places you'd look for them, and honest about what it can't do.
Cinder is a Manifest V3 Chrome extension, open source under MIT.
What I built
- The encrypted keyring lives in the extension's service worker. The popup and the approval window only ever send requests to it, so the popup never holds a Keypair.
- The vault is encrypted with AES-256-GCM, with the key derived by PBKDF2-SHA256 at OWASP's recommended iteration count. The encrypted blob records its own version and key-derivation settings, so an older vault is re-encrypted on the next unlock, and one this build can't read is refused plainly rather than reported as a wrong password.
- Sites connect through Wallet Standard (connect, sign transaction, sign and send, sign message), not a pretend copy of another wallet's global object. Connections are per site and can be revoked in Settings.
- Legacy and v0 transactions are both supported, with address lookup tables resolved in the preview. The approval screen simulates the transaction and shows the balance change, before and after, plus warnings for authority changes and unknown programs. Approve stays disabled while the preview is unsettled.
- With no endpoint configured, Cinder still finds your tokens, but every amount you can act on is read from the chain. When a read isn't available, the popup says so instead of showing a zero.
Key decisions
Putting the keyring behind a message boundary makes every other part of the extension less interesting to an attacker. It costs a message round trip for everything, and a protocol file that has to describe every request and response.
On the free mainnet path, compressed NFTs can't be listed, because no free endpoint serves the indexer (DAS) that can find them. The collectibles tab says so, rather than letting a partial grid pass for a whole collection.
Result
Version 0.5.0 is public, with its tests, coverage gate and design records in the repo. It's the one project on this site you can check line by line.
My part
Not audited. Do not put mainnet funds on it that you cannot lose. I built it with AI coding agents (Claude and Cursor) under my direction, and every change was reviewed and tested. It's a portfolio project.
Numbers
| Figure | What it measures |
|---|---|
| 134 | commits in the public repo (GitHub) |
| 94% | line-coverage threshold the test suite must pass (README) |
| 557 | tests across Vitest and Playwright, as of my 2026 résumé (GitHub) |
| ~24K | lines of TypeScript and React, as of my 2026 résumé (GitHub) |
Links
Stack: TypeScript, React, Chrome Manifest V3, Wallet Standard, Solana web3.js, Redux, React Query, Vite, Vitest, Playwright
