CCWork
CC
Email me

Work

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
The public Cinder Wallet repository on GitHub: MIT license, the 0.5.0 release, topics and the file tree; the account name blurred
The public Cinder Wallet repository, Sept 2026. Account name blurred.

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

FigureWhat it measures
134commits in the public repo (GitHub)
94%line-coverage threshold the test suite must pass (README)
557tests across Vitest and Playwright, as of my 2026 résumé (GitHub)
~24Klines of TypeScript and React, as of my 2026 résumé (GitHub)
Questions