apps/readest-app/scripts/nightly-verify-harness/README.md
Exercises the in-app nightly check (Tier 2 detection) and the signature gate
(Tier 4) on a desktop pnpm tauri dev build, without waiting on CI.
Throwaway-signed fixtures only (the signing key was discarded).
check(); error / up-to-date states render.resolveNightlyUpdate — harness scenarios in src/__tests__/helpers/updater.test.ts runs the real
resolver against these manifest builders (pnpm test src/__tests__/helpers/updater.test.ts).READEST_UPDATER_PUBKEY, so the
throwaway artifact correctly fails real-key verification if you click
"Download & Install". Accept-valid is covered by the Rust test
pnpm test:rust → nightly_update::tests::verify_accepts_valid_signature.pnpm verify:nightly # or: node scripts/nightly-verify-harness/serve.mjs
src/services/constants.ts temporarily change:
export const READEST_NIGHTLY_UPDATER_FILE = 'http://127.0.0.1:8788/nightly/latest.json';
// and
export const READEST_UPDATER_FILE = 'http://127.0.0.1:8788/releases/latest.json';
http://*:*, so localhost works.)pnpm tauri dev
GET /nightly/latest.json and /releases/latest.json
(both fetched, then filtered/compared).READEST_UPDATER_FILE at
http://127.0.0.1:8788/releases/latest-surpass.json (stable = base, patch +1).
The offered version should switch to the stable <base+1> — a higher-base
stable beats the nightly. Switch back and the nightly wins again.git checkout src/services/constants.ts
In the dev window devtools console (right-click → Inspect):
const path = '<ABS>/apps/readest-app/scripts/nightly-verify-harness/artifacts/test.bin';
const pubKey = 'dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEZFQTAxMjIzNUEwRkE0OUIKUldTYnBBOWFJeEtnL2x4Q3dKR3dSWVJCY3dLNXdCR1l4d1YyVkhaZUppOVVNVm1kOGprbU85bTMK';
const goodSig = 'dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVTYnBBOWFJeEtnL3RvRC83dEJEUXZONVFZM1hranhKTUZxQzllR2lGWnNjckZMbCtOa3RXMi80aFdDYUNDUkdOa0NqUjJUQkZDL2dqaUVTeURlNzI0cW1BcUlZY2ZsOGcwPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzgxNDE0MzExCWZpbGU6bnYuYmluCkQzajlpbVZPOXVDYXdna2JBVWZ0TTE4K1d1cWdEYWVYQzVraGh4U1ZuOGNSTDZaOU5zV093OEVDajBvV0JydVV5VGY2K0tkb0hBbGJHYWprK0NsNUN3PT0K';
const { invoke } = window.__TAURI_INTERNALS__;
await invoke('verify_update_signature', { path, signature: goodSig, pubKey }); // → true
await invoke('verify_update_signature', { path, signature: 'AAAAgarbage', pubKey }); // → false
Replace <ABS>. (pubKey is the throwaway key that signed test.bin, not the
production key.) Tampering test.bin and re-running the good-sig call also → false.
<base>-2099010100 so it's always newer than installed.serve.mjs reads the base version from package.json each request, so it stays
correct as the app version changes.