A second, independent check on every transaction before you sign.
Ethereum is built to be verifiable, and verification is cheap. Yet most users still sign transactions without independently checking what they approve. Onay makes that second check seamless: it verifies the transaction locally, explains what it does, and lets you compare against your wallet before signing.
Onay is a local desktop app with a thin browser extension that sits between the dapp and the wallet. It intercepts the signing request, verifies chain state through an embedded Helios light client, recompiles and verifies the source code locally, simulates the transaction, renders its intent in readable form with ERC-7730 clear signing, and finally displays the ERC-8213 calldata digest for an equivalence check with the wallet. Onay is view only: it holds no keys and does not replace your wallet.
The plan of record is PLAN.md.
| Folder | What it is |
|---|---|
app/ |
The desktop app (Tauri). The web user interface is in app/src/, the Rust side in app/src-tauri/. Also holds the relay (app/relay/) and the code that relay and app share (app/ipc/). |
extension/ |
The Chrome extension (Manifest V3). No runtime dependencies. |
-
Node.js 22 or newer and pnpm. The exact pnpm version is pinned in
package.json; pnpm switches to it automatically. -
Rust through rustup. The compiler version is pinned in
app/rust-toolchain.toml; rustup installs it on first use. -
Google Chrome or Chromium, installed as a normal package. Snap and Flatpak browsers cannot start the relay.
-
Linux only: the Tauri system libraries.
sudo apt install libwebkit2gtk-4.1-dev libdbus-1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev -
macOS only: the Xcode Command Line Tools (
xcode-select --install).
Then install the JavaScript dependencies once, from the repo root:
pnpm install
pnpm --filter app tauri dev
This builds the relay, starts the web user interface with hot reload, compiles the app, and opens its window. At startup the app does two things:
- It writes the native messaging host manifest for each installed browser, for example
~/.config/google-chrome/NativeMessagingHosts/dev.sourcify.onay.json. The manifest points to the relay inapp/target/debug/. - It opens a Unix socket at
$XDG_RUNTIME_DIR/onay/onay.sock(Linux) or$TMPDIR/onay/onay.sock(macOS).
The window lists both under "Browser link".
pnpm --filter extension build
- Open
chrome://extensionsand turn on "Developer mode". - Click "Load unpacked" and select
extension/dist. - Check that the ID is
jhopbgoicoiceialjejgojebmeijklbn. The app accepts only this ID. Thekeyfield inextension/manifest.jsonkeeps it the same on every machine.
After a code change, build again and click the reload icon on the extension card.
Click the Onay icon in the Chrome toolbar and press "Ping the app". The popup shows "The app answered", and the app window shows the ping and the pong under "Messages". If you quit the app and ping again, the popup says that the app is not running.
Run these before you push. Continuous integration (CI) runs the same commands.
| Command | Where | What it does |
|---|---|---|
pnpm -r build |
repo root | Type-checks and builds the mock, the app's web user interface, and the extension. |
pnpm -r lint |
repo root | Runs oxlint. |
cargo test |
app/ |
Runs the Rust tests, including one that drives the real relay binary. |
cargo clippy --all-targets -- -D warnings |
app/ |
Rust lints. |
cargo fmt --all --check |
app/ |
Rust formatting. |
cargo deny check |
app/ |
Advisories, licenses, and sources of the Rust dependencies. Install once with cargo install --locked cargo-deny. |
Nothing is published yet. These commands produce the same artifacts a release will ship. Signing, notarization, and the release pipeline are tracked in issue 15.
pnpm --filter app bundle
This builds the relay in release mode, copies it to where Tauri expects an extra binary, and runs tauri build with the settings in app/src-tauri/tauri.bundle.conf.json. The packages land in app/target/release/bundle/:
- Linux: a
.debindeb/and an.rpminrpm/. - macOS:
Onay.appinmacos/and a.dmgindmg/.
The package contains two binaries side by side: onay (the app) and onay-relay. To install and run on Debian or Ubuntu:
sudo apt install ./app/target/release/bundle/deb/Onay_0.0.0_amd64.deb
onay
Start the app once after installing. The first start writes the host manifest, now pointing to the installed relay (/usr/bin/onay-relay).
pnpm --filter extension package
This writes extension/onay-extension.zip for the Chrome Web Store. It is the same code as the development build, without the key field in the manifest, because the store rejects it.
To keep the extension ID that the app accepts, the first upload to the store must include the private key as key.pem in the root of the zip. That key is extension/key.pem, which is not in the repo. After the first upload the store holds the key, and later uploads do not need it.
There is one host manifest per browser, and each start of the app overwrites it. The app you started last is the one the extension reaches. To switch back to the development build, start it again with pnpm --filter app tauri dev.
web page -> extension -> Chrome -> relay -> app
(starts it) (Unix socket)
- The extension asks Chrome to connect to the host named
dev.sourcify.onay. - Chrome reads the host manifest, checks that the extension ID is allowed, and starts the relay.
- The relay connects to the app's socket and copies bytes in both directions. It does not read the messages.
- No network port is opened at any point.
The mock/ folder holds a clickable mock of the first release with fake data. It is live at sourcifyeth.github.io/onay. To run it locally: pnpm --filter mock dev.