A Manifest V3 Chrome extension that verifies ORIGINPROOF .oproof sidecar files offline. Drop a file and its .oproof into the popup, get a green/red answer in milliseconds. Zero network requests, zero permissions, zero dependencies — all verification runs locally via the Web Crypto API.
This extension is verify-only. To sign files, use the ORIGINPROOF CLI.
- Chrome 120 or later (Ed25519 support in
SubtleCrypto)
- Open
chrome://extensions - Toggle Developer mode on (top right)
- Click Load unpacked
- Select the
originproof-extension/directory - The shield icon appears in your toolbar — click it to open the verifier
# 1. Build the CLI (if not already built)
cd ../originproof && npm install && npm run build
# 2. Sign any file
npx originproof sign README.md
# 3. In Chrome, click the extension icon, drop README.md and README.md.oproof
# Expected: green "VERIFIED" with signer metadataoriginproof-extension/
├── manifest.json MV3 config, zero permissions
├── popup/
│ ├── popup.html Two drop zones + verify button + result + footer
│ ├── popup.css Dark theme with indigo accent
│ ├── popup.js DOM, drag-drop, BOM handling, result rendering
│ └── verify.js Pure verification logic (Web Crypto port)
├── icons/ Shield icon at 16/48/128
├── docs/
│ └── popup-verified.png Screenshot captured by tests/e2e/screenshot.mjs
└── tests/
├── verify.test.mjs 10 logic scenarios via node:test
├── popup.test.mjs 10 DOM-layer scenarios via jsdom
├── generate-fixture.mjs Pretest hook — signs sample.txt in-process
├── fixtures/sample.txt Stable committed sample for signing
└── e2e/
├── server.mjs Static server for HTTP-served popup
├── popup.e2e.mjs 5 end-to-end scenarios in real Chromium
└── screenshot.mjs Playwright script that captures the README PNG
| Status | Meaning | Color |
|---|---|---|
VERIFIED |
File is authentic and unmodified | green |
HASH_MISMATCH |
File has been modified since signing | red |
INVALID_SIGNATURE |
Signature doesn't match the embedded public key | red |
MALFORMED_PROOF |
Proof is missing, corrupt, or has an unsupported version | yellow |
UNSUPPORTED_BROWSER |
Chrome version too old — upgrade to 120+ | yellow |
FILE_TOO_LARGE |
File exceeds 250 MB limit | yellow |
FILE_EMPTY |
File buffer has zero length | yellow |
There are two test tiers:
npm testRuns 20 tests via node:test:
- verify.js (10) — pure logic loaded via
new Functionwrapper: happy path, tamper detection, corrupted signature, garbage JSON, missing fields, extra-field resilience, version mismatch, uppercase hex, empty file, UTF-8 BOM stripping. - popup.js (10) — DOM layer via jsdom: initial render, button enablement, file-drop input, dataTransfer drops, drag-over visual feedback, XSS escaping in error messages, full verify click flow, BOM stripping at the I/O boundary, FILE_EMPTY flow, and version footer population.
The pretest hook signs tests/fixtures/sample.txt in-process using Node's built-in crypto. It caches by content hash — if sample.txt hasn't changed since the last run, signing is skipped. No CLI dependency.
npm run test:e2eRuns 5 scenarios in headless Chromium 147 against an HTTP-served popup: version footer populates, VERIFIED round-trip, HASH_MISMATCH on tampered file, INVALID_SIGNATURE on flipped signature byte, MALFORMED_PROOF on garbage JSON. Catches any Chrome-vs-Node behavioral drift that jsdom + Node's crypto.subtle would miss.
First run downloads ~92 MB of Chromium Headless Shell via npx playwright install chromium.
npm run screenshotDrives the real popup through a verification flow in Chromium and writes docs/popup-verified.png. Every pixel is produced by real verification code — no DOM injection. Rerun after visual changes.
- No signing. Use the CLI.
- No page scanning. The extension does not auto-detect
.oprooffiles on websites. - No network requests. Fully offline.
- No background activity. No service worker, no persistent state.
See the main ORIGINPROOF README for the full JSON schema.
