All checks were successful
check / check (push) Successful in 11s
DEBUG mode must behave identically to normal mode except for the red banner and hardcoded mnemonic. No other DEBUG branches without explicit owner approval. Policy documented in README.
340 lines
16 KiB
Markdown
340 lines
16 KiB
Markdown
# AutistMask
|
|
|
|
AutistMask is a GPL-licensed JavaScript browser extension by
|
|
[@sneak](https://sneak.berlin) that provides a minimal Ethereum wallet for
|
|
Chrome and Firefox. It manages HD wallets derived from BIP-39 seed phrases and
|
|
supports sending and receiving ETH and ERC-20 tokens, as well as web3 site
|
|
connection and authentication via the EIP-1193 provider API.
|
|
|
|
## Getting Started
|
|
|
|
```bash
|
|
git clone https://git.eeqj.de/sneak/autistmask.git
|
|
cd autistmask
|
|
make install
|
|
make build
|
|
```
|
|
|
|
Load the extension:
|
|
|
|
- **Chrome**: Navigate to `chrome://extensions/`, enable "Developer mode", click
|
|
"Load unpacked", and select the `dist/chrome/` directory.
|
|
- **Firefox**: Navigate to `about:debugging#/runtime/this-firefox`, click "Load
|
|
Temporary Add-on", and select `dist/firefox/manifest.json`.
|
|
|
|
## Rationale
|
|
|
|
MetaMask has become bloated with swap UIs, portfolio dashboards, analytics,
|
|
tracking, and advertisements. It is no longer a simple wallet. Most alternatives
|
|
(Rabby, Rainbow, etc.) only support Chromium browsers, leaving Firefox users
|
|
without a usable option.
|
|
|
|
AutistMask exists to provide the absolute minimum viable Ethereum wallet
|
|
experience: manage seed phrases, derive HD addresses, send and receive ETH and
|
|
ERC-20 tokens, and connect to web3 sites. Nothing else. No swaps (that's what
|
|
the web is for), no analytics, no tracking, no ads, no portfolio views, no NFT
|
|
galleries. Just a wallet.
|
|
|
|
## Design
|
|
|
|
AutistMask is a browser extension targeting both Chrome (Manifest V3) and
|
|
Firefox (Manifest V2/V3 as supported). The codebase is shared between both
|
|
targets with platform-specific manifest files and a build step that produces
|
|
separate output directories.
|
|
|
|
### Architecture
|
|
|
|
```
|
|
src/
|
|
background/ — service worker / background script
|
|
index.js — extension lifecycle, message routing
|
|
wallet.js — wallet management (create, import, derive via ethers.js)
|
|
provider.js — EIP-1193 JSON-RPC provider implementation
|
|
popup/ — popup UI (the main wallet interface)
|
|
index.html
|
|
index.js
|
|
styles/ — CSS (Tailwind)
|
|
content/ — content script injected into web pages
|
|
index.js — injects the provider into page context
|
|
inpage.js — the window.ethereum provider object
|
|
shared/ — shared utilities
|
|
vault.js — encrypted storage via libsodium
|
|
constants.js — chain IDs, default RPC endpoints, ERC-20 ABI
|
|
manifest/
|
|
chrome.json — Manifest V3 for Chrome
|
|
firefox.json — Manifest V2/V3 for Firefox
|
|
```
|
|
|
|
### UI Design Philosophy
|
|
|
|
The UI follows a "Universal Paperclips" aesthetic — a deliberately spartan,
|
|
almost brutalist approach. The guiding principle is that an unskilled,
|
|
non-technical person should be able to figure out how to use it without any
|
|
crypto knowledge.
|
|
|
|
#### Visual Style
|
|
|
|
- **Monochrome**: Black text on white background. No brand colors, no gradients,
|
|
no color-coding. Color may be introduced later for specific semantic purposes
|
|
(e.g. error states) but the baseline is monochrome.
|
|
- **Text-first**: Every piece of information is presented as text. Balances are
|
|
numbers. Addresses are hex strings. Status is a sentence. No progress spinners
|
|
with animations — a text status line is sufficient.
|
|
- **Monospace font**: All text is rendered in the system monospace font.
|
|
Ethereum addresses, transaction hashes, and balances are inherently
|
|
fixed-width data. Rather than mixing proportional and monospace fonts, we use
|
|
monospace everywhere for visual consistency and alignment.
|
|
- **No images**: Zero image assets in the entire extension. No logos, no
|
|
illustrations, no token icons. Token identity is conveyed by symbol text (ETH,
|
|
USDC, etc.).
|
|
- **Tailwind CSS**: Utility-first CSS via Tailwind. No custom CSS classes for
|
|
styling. Tailwind is configured with a minimal monochrome palette. This keeps
|
|
the styling co-located with the markup and eliminates CSS file management.
|
|
- **Vanilla JS**: No framework (React, Vue, Svelte, etc.). The popup UI is small
|
|
enough that vanilla JS with simple view switching is sufficient. A framework
|
|
would add bundle size, build complexity, and attack surface for no benefit at
|
|
this scale.
|
|
- **360x600 popup**: Standard browser extension popup dimensions. The UI is
|
|
designed for this fixed viewport — no responsive breakpoints needed.
|
|
|
|
#### Language & Labeling
|
|
|
|
All user-facing text avoids crypto jargon wherever possible:
|
|
|
|
- "Recovery phrase" instead of "seed phrase", "mnemonic", or "BIP-39 mnemonic"
|
|
- "Address" instead of "account", "derived key", or "HD child"
|
|
- "Password" instead of "encryption key" or "vault passphrase"
|
|
- "Private key" instead of "secret key" or "signing key"
|
|
- Buttons use plain verbs: "Send", "Receive", "Copy address", "Add", "Back",
|
|
"Cancel", "Lock", "Unlock", "Allow", "Deny"
|
|
- No bracket notation like `[locked]` or `[setup]` — just plain titles
|
|
- Helpful inline descriptions where needed (e.g. "This password locks the wallet
|
|
on this device. It is not the same as your recovery phrase.")
|
|
- Error messages are full sentences ("Please enter your password." not "password
|
|
required")
|
|
|
|
#### Data Model
|
|
|
|
The core hierarchy is **Wallets → Addresses**:
|
|
|
|
- A **wallet** is either:
|
|
- An **HD wallet** (recovery phrase): generates multiple addresses from a
|
|
single 12/24 word recovery phrase using BIP-39/BIP-44 derivation. The user
|
|
can add more addresses with a "+" button.
|
|
- A **key wallet** (private key): a single address imported directly from a
|
|
private key. No "+" button since there is only one address.
|
|
- An **address** holds ETH and any user-added ERC-20 tokens.
|
|
- The user can have multiple wallets, each with multiple addresses (HD) or a
|
|
single address (key).
|
|
|
|
#### Navigation
|
|
|
|
The main view shows all addresses grouped by wallet, with ETH balances inline.
|
|
The user taps an address to see its detail view (full address, balance, tokens,
|
|
send/receive). Navigation is flat — every view has a "Back" or "Cancel" button
|
|
that returns to the previous context. No deep nesting, no tabs, no hamburger
|
|
menus.
|
|
|
|
### UI Views
|
|
|
|
The popup has the following views, switched via simple show/hide:
|
|
|
|
1. **Lock**: Password input + Unlock button. Shown when the wallet is locked or
|
|
on first open after browser restart.
|
|
2. **Welcome**: Shown on first use. Two options: "Add wallet" (recovery phrase
|
|
based) and "Import private key". Password is set during the first wallet
|
|
addition.
|
|
3. **Add wallet**: A unified view for both creating and importing recovery
|
|
phrase wallets. The recovery phrase text area starts empty. A clickable die
|
|
button `[die]` generates a random 12-word phrase and fills it in. If the user
|
|
already has a phrase, they paste it directly. When the die is clicked, a
|
|
warning box appears reminding the user to write the phrase down. Password
|
|
fields are shown only on first use.
|
|
4. **Import private key**: Paste a private key. This creates a wallet with a
|
|
single address. Password fields shown only on first use.
|
|
5. **Main**: All wallets listed, each showing its addresses with truncated
|
|
address and ETH balance. "+" next to recovery phrase wallets to add another
|
|
address. "+ Add wallet" and "+ Import private key" buttons at the bottom.
|
|
Settings and Lock buttons in the header. Future: a sub-heading showing total
|
|
portfolio value in USD (and eventually other currencies).
|
|
6. **Address detail**: Full address (click to copy), ETH balance, USD value
|
|
(future), Send/Receive buttons, token list with "+ Add" button.
|
|
7. **Send**: Token selector, recipient address, amount. Cancel returns to
|
|
address detail.
|
|
8. **Receive**: Full address displayed with "Copy address" button.
|
|
9. **Add token**: Enter contract address. The extension looks up the token
|
|
name/symbol automatically.
|
|
10. **Settings**: Network (RPC endpoint URL) with explanatory text.
|
|
11. **Approval**: When a website requests wallet access or a signature, shows
|
|
the site origin, request details, and Allow/Deny buttons.
|
|
|
|
### External Services
|
|
|
|
AutistMask is not a fully self-contained offline tool. It necessarily
|
|
communicates with external services to function as a wallet:
|
|
|
|
- **Ethereum JSON-RPC endpoint**: The extension needs an Ethereum node to query
|
|
balances (`eth_getBalance`), read ERC-20 token contracts (`eth_call`),
|
|
estimate gas (`eth_estimateGas`), fetch nonces (`eth_getTransactionCount`),
|
|
broadcast transactions (`eth_sendRawTransaction`), and check transaction
|
|
receipts. The default endpoint is a public RPC (configurable by the user to
|
|
any endpoint they prefer, including a local node). This is the only external
|
|
service the extension talks to.
|
|
|
|
What the extension does NOT do:
|
|
|
|
- No analytics or telemetry services
|
|
- No token list APIs (user adds tokens manually by contract address)
|
|
- No price feed APIs (balances are shown in token units, not fiat)
|
|
- No phishing/blocklist APIs
|
|
- No Infura/Alchemy dependency (any JSON-RPC endpoint works)
|
|
- No backend servers operated by the developer
|
|
|
|
The user's RPC endpoint is the single point of external communication. Users who
|
|
want maximum privacy can point it at their own Ethereum node.
|
|
|
|
### Dependencies
|
|
|
|
AutistMask uses two runtime libraries. All cryptographic operations are
|
|
delegated to these libraries — see the Crypto Policy section below.
|
|
|
|
| Package | Version | License | Purpose |
|
|
| ------------------------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `ethers` | 6.16.0 | MIT | All Ethereum operations: BIP-39 mnemonic generation/validation, BIP-32/BIP-44 HD key derivation (`m/44'/60'/0'/0/n`), secp256k1 signing, transaction construction, ERC-20 contract interaction, JSON-RPC communication, address derivation (keccak256). |
|
|
| `libsodium-wrappers-sumo` | 0.8.2 | ISC | Password-based encryption of secrets at rest: Argon2id key derivation (`crypto_pwhash`), authenticated encryption (`crypto_secretbox` / XSalsa20-Poly1305). |
|
|
|
|
Dev dependencies (not shipped in extension):
|
|
|
|
| Package | Version | License | Purpose |
|
|
| ------------------ | ------- | ------- | ------------------------- |
|
|
| `esbuild` | 0.27.3 | MIT | JS bundler (inlines deps) |
|
|
| `tailwindcss` | 4.2.1 | MIT | CSS compilation |
|
|
| `@tailwindcss/cli` | 4.2.1 | MIT | Tailwind CLI |
|
|
| `jest` | 30.2.0 | MIT | Test runner |
|
|
| `prettier` | 3.8.1 | MIT | Code formatter |
|
|
|
|
### Crypto Policy
|
|
|
|
**No raw crypto primitives in application code.** If the strings `aes`, `sha`,
|
|
`pbkdf`, `hmac`, `encrypt`, `decrypt`, `hash`, `cipher`, `digest`, `sign`
|
|
(case-insensitive) appear in our own source code (outside of `node_modules/`),
|
|
it is almost certainly a bug. All cryptographic operations must go through
|
|
`ethers` or `libsodium-wrappers-sumo`. This policy exists because:
|
|
|
|
- Rolling your own crypto is the single most common source of security
|
|
vulnerabilities in wallet software.
|
|
- Both libraries are widely audited and battle-tested.
|
|
- Keeping crypto out of application code makes security review tractable:
|
|
reviewers only need to verify that we call the libraries correctly, not that
|
|
we implemented crypto correctly.
|
|
|
|
Exceptions require explicit authorization in a code comment referencing this
|
|
policy.
|
|
|
|
### DEBUG Mode Policy
|
|
|
|
The `DEBUG` constant in the popup JS enables a red "DEBUG / INSECURE" banner and
|
|
a hardcoded test mnemonic. **DEBUG mode must behave as close to normal mode as
|
|
possible.** No `if (DEBUG)` branches that skip functionality, bypass security
|
|
flows, or alter program behavior beyond the banner and the hardcoded mnemonic.
|
|
Adding new DEBUG-conditional branches requires explicit approval from the
|
|
project owner.
|
|
|
|
### Key Decisions
|
|
|
|
- **No framework**: The popup UI is vanilla JS and HTML. The extension is small
|
|
enough that a framework adds unnecessary complexity and attack surface.
|
|
- **Encrypted storage**: Recovery phrases and private keys are encrypted at rest
|
|
in the extension's local storage using libsodium. The encryption scheme:
|
|
- The user's password is run through Argon2id (`crypto_pwhash`) to derive a
|
|
256-bit encryption key. Argon2id is memory-hard, making GPU/ASIC brute
|
|
force attacks expensive.
|
|
- The derived key encrypts the secret material using XSalsa20-Poly1305
|
|
(`crypto_secretbox`), which provides authenticated encryption (the
|
|
ciphertext cannot be tampered with without detection).
|
|
- Stored blob: `{ salt, nonce, ciphertext }` (the auth tag is part of the
|
|
`crypto_secretbox` output).
|
|
- **The password is NOT used in address derivation.** It exists solely to
|
|
protect the recovery phrase / private key on disk. Anyone with the
|
|
recovery phrase can restore the wallet on any device without this
|
|
password. This matches MetaMask's behavior.
|
|
- **BIP-39 / BIP-44 via ethers.js**: Mnemonic generation, validation, and HD key
|
|
derivation (`m/44'/60'/0'/0/n`) are handled entirely by ethers.js. The BIP-39
|
|
passphrase is always empty (matching MetaMask and most wallet software). The
|
|
user's password is completely separate and has no effect on which addresses
|
|
are generated.
|
|
- **ethers.js for everything Ethereum**: Transaction construction, signing, gas
|
|
estimation, RPC communication, ERC-20 contract calls, and address derivation
|
|
are all handled by ethers.js. This means zero hand-rolled Ethereum logic.
|
|
- **EIP-1193 provider**: The content script injects a `window.ethereum` object
|
|
that implements the EIP-1193 provider interface, enabling web3 site
|
|
connectivity.
|
|
- **Minimal RPC**: The extension communicates with Ethereum nodes via JSON-RPC
|
|
through ethers.js's `JsonRpcProvider`. The default endpoint is configurable.
|
|
No Infura dependency — users can point it at any Ethereum JSON-RPC endpoint.
|
|
|
|
### Supported Functionality
|
|
|
|
- Create new HD wallet (generates 12-word recovery phrase)
|
|
- Import HD wallet from existing 12 or 24 word recovery phrase
|
|
- Import single-address wallet from private key
|
|
- Add multiple addresses within an HD wallet
|
|
- Manage multiple wallets simultaneously
|
|
- View ETH balance per address
|
|
- View ERC-20 token balances (user adds token by contract address)
|
|
- Send ETH to an address
|
|
- Send ERC-20 tokens to an address
|
|
- Receive ETH/tokens (display address, copy to clipboard)
|
|
- Connect to web3 sites (EIP-1193 `eth_requestAccounts`)
|
|
- Sign transactions requested by connected sites
|
|
- Sign messages (`personal_sign`, `eth_sign`)
|
|
- Lock/unlock with password
|
|
- Configurable RPC endpoint
|
|
- Future: USD value display (and other fiat currencies)
|
|
|
|
### Non-Goals
|
|
|
|
- Token swaps (use a DEX in the browser)
|
|
- NFT display or management
|
|
- Multi-chain support (Ethereum mainnet only, for now)
|
|
- Analytics, telemetry, or tracking of any kind
|
|
- Advertisements or promotions
|
|
- Phishing detection
|
|
- Hardware wallet support (maybe later)
|
|
- Token list auto-discovery (user adds tokens manually)
|
|
- Fiat on/off ramps
|
|
- Browser notifications
|
|
- Transaction history (use Etherscan)
|
|
|
|
## TODO
|
|
|
|
- [x] Project scaffolding (Makefile, Dockerfile, CI, manifests)
|
|
- [x] Set up Tailwind CSS build pipeline
|
|
- [x] Build popup UI views (all 13 views with stub state)
|
|
- [ ] Implement BIP-39 mnemonic generation and validation
|
|
- [ ] Implement BIP-32/BIP-44 HD key derivation for Ethereum
|
|
- [ ] Implement private key import and address derivation
|
|
- [ ] Implement encrypted storage for wallet data
|
|
- [ ] Implement background wallet manager
|
|
- [ ] Implement EIP-1193 provider and content script injection
|
|
- [ ] Implement ETH balance lookup via RPC
|
|
- [ ] Implement ETH send (transaction construction + signing)
|
|
- [ ] Implement ERC-20 token management (add by contract, view balance, send)
|
|
- [ ] Implement site connection approval flow
|
|
- [ ] Implement transaction signing approval flow
|
|
- [ ] Implement message signing (`personal_sign`, `eth_sign`)
|
|
- [ ] Add USD value display (price feed TBD)
|
|
- [ ] Add multi-currency fiat display support
|
|
- [ ] Test on Chrome and Firefox
|
|
- [ ] Write tests for crypto operations
|
|
- [ ] Write tests for transaction construction
|
|
- [ ] Security audit of key management
|
|
|
|
## License
|
|
|
|
GPL-3.0. See [LICENSE](LICENSE).
|
|
|
|
## Author
|
|
|
|
[@sneak](https://sneak.berlin)
|