Skip to content

Repository files navigation

Kivuli Extension

A private wallet for every site you connect, right in your browser. Kivuli derives a distinct, unlinkable account for each dApp origin and receives money at addresses only you can connect to yourself, built on the Opaque protocol stealth-address rails. Base mainnet by default, with Ethereum Sepolia available behind developer options for free end-to-end testing.

Beta. Distributed as a GitHub-release zip, loaded unpacked. Unaudited. On Base it holds real money; read the security model below and never keep more in a beta wallet than you are testing with.

What it does

  • A wallet per site. Connecting to a dApp derives a fresh wallet for that origin alone, exposed over EIP-6963. Sites never see your other money or each other, and because each wallet is funded from its own stealth note, no shared funding address links them on-chain either. That second half is what makes the unlinkability real rather than cosmetic; it is also the part no shipping wallet does today.
  • Private receiving. Receive hands out plain 0x… addresses from a seed-derived ladder, so any ordinary wallet can pay you, plus a meta-address for stealth-aware senders. Each payment lands at a fresh one-time address that only you can find, and sweeps where you want it.
  • Honest approvals. Transactions are decoded into what actually changes for you; token allowances get editable spending caps; unlimited approvals (including the Permit2 sentinel) are refused; suspicious sites are interrupted before they can ask.
  • A full surface, not just a popup. The same wallet runs in the popup, Chrome's side panel, and an expanded tab. USDC on Base is tracked out of the box via the curated token registry.

Security model, in brief

  • One secret. Everything (the meta-address, every receive address, every per-site wallet) derives from a 12-word phrase. The backup ceremony verifies the words before the wallet activates, because on this distribution channel the phrase is the recovery path.
  • Keys stay in the service worker. The master secret exists only in service-worker memory while unlocked. The vault is encrypted under your passcode; the scan cache and the origin-to-address map are encrypted at rest under keys derived from the unlocked wallet, so a locked wallet serves nothing.
  • Isolation per origin. Each dApp origin gets its own account, and spending from it requires that origin's connection.
  • A small, unprobeable surface. Install-time host permissions cover only the built-in RPC endpoints (custom ones are granted individually), and the build strips web_accessible_resources, so pages cannot probe for Kivuli's presence.
  • Not audited yet. There has been no external audit. Vulnerability reports go through the private path in SECURITY.md.

Install (users)

  1. Download kivuli-extension-v<version>.zip from the latest GitHub release.
  2. Verify it if you like: each release ships SHA256SUMS.txt and a GitHub build-provenance attestation (gh attestation verify <zip> --repo kivuliapp/extension).
  3. Unzip into a folder you will keep, then chrome://extensions, Developer mode, Load unpacked.

Unpacked installs never auto-update, and removing the extension deletes the wallet vault. Before updating anything, read docs/release.md: it is short, and the update procedure in it is what keeps your vault alive.

Build from source

Node 20 (.nvmrc).

npm ci
npm run build      # typecheck + production build + hardening → dist/
npm test           # full vitest suite

Load dist/ as an unpacked extension via chrome://extensions (Developer mode). Other useful scripts:

npm run dev        # popup UI on mock data (no extension APIs needed)
npm run storybook  # component workbench
npm run demo:dev   # the test bench (see below) at :5173
npm run demo:build # bench → demo-dist/, deployable as a static site
npm run release    # dist/ packaged as the release zip, with verification

Testing it for real

Stealth receiving needs a counterparty, so a fresh install has nothing to show until somebody pays you. demo/ is that somebody: a standalone page that pays a meta-address from a funded wallet and doubles as the dApp for provider testing. It has two modes, picked at the top of the page:

  • Ethereum Sepolia (default): free, full battery, including deploying a mintable ERC-20 and the adversarial signature payloads.
  • Base mainnet: real money, clearly labeled. Nothing is deployed; token buttons run against canonical Base USDC with cent-scale amounts.

The loop: load dist/ unpacked, copy your meta-address from Receive, fund a separate wallet, open the bench, pay yourself, watch the note land in Kivuli, sweep it. Then connect the bench to Kivuli and run the smoke tests: the address it reports should be unique to that origin.

docs/runtime-qa.md is the full first-load checklist (free, Sepolia); docs/runtime-qa-base.md is the budgeted real-money pass on Base. Host the bench (npm run demo:build, any static host) before handing the extension to testers.

v1 scope

This build is deliberately narrow. Scope is decided in exactly two places: ENABLED_CHAINS in src/lib/config.ts and capabilities() in src/ui/services/bridge.ts. The UI reads both rather than guessing:

  • EVM only, one network at a time. Base mainnet by default; Ethereum Sepolia behind Settings, Advanced, Developer options. dApp-driven network switching is refused; switching is a user decision.
  • Local vault, no accounts server. Onboarding is a passcode plus a 12-word recovery phrase (capabilities.localVault). The Kivuli login model (@handles, pay links, nothing to write down) lands with the server, behind kivuliLogin.
  • ETH moves, tokens show. Receive, sweep, and send work for native ETH. ERC-20 payments (USDC included) are detected and shown, but cannot be moved yet; they wait on the gasless rail. No Solana surface, no ENS, no custom networks.

Architecture

  • src/background/: the MV3 service worker. Approvals queue, request decoding, origin tracking, RPC proxy, scanning and funding rails, keyring.
  • src/content/ + src/inpage/: the EIP-6963 provider bridge, re-bundled as self-contained IIFEs by scripts/finalize-dist.mjs so no web-accessible resources ship.
  • src/ui/: popup, side panel, and approval UI over a WalletService seam (services/bridge.ts is the real one; services/mock.ts powers dev and Storybook).
  • demo/: the test bench, its own vite root.

The docs go deeper: docs/release.md (release process and the pinned extension ID), docs/runtime-qa.md and docs/runtime-qa-base.md (runbooks), docs/service-delta.md (the service seam's design record), and docs/ux-research.md (design brief).

Contributing

See CONTRIBUTING.md for setup, test expectations, commit style, and the recommended branch protection. Bugs and feature requests go through the issue forms; vulnerabilities go through SECURITY.md, never a public issue.

License

GPL-3.0-or-later.

About

Kivuli browser extension: private money for Ethereum and Solana, with a private wallet for every site you connect

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages