# KoreMD v1.1.10 — reproducible-build diagnostic

**Repo:** https://github.com/PYU224/koremd (F-Droid: `com.pyu.koremd`, rbtlog "DOES NOT VERIFY")
**Official APK tested:** `93a94503b5f5005c5b3d4d30cde4769038b9eb1033f093afcf0dda4aee9d1741-com.pyu.koremd-v1.1.10-upstream.apk` (5,846,373 bytes, 495 entries)
**Date:** 2026-10-04
**Verdict: NOT reproducible — but the build is locally deterministic; the residue is toolchain-version drift, not tampering.**

## What I did

1. Cloned `v1.1.10` (commit `dbce8ca`) and built the web bundle (`npm ci` + `vite build`) **twice in two independent working trees with different absolute source paths** — a standard determinism control.
2. Compared the two rebuilds file-by-file (SHA-256 of all 25 `dist/` files): **identical — 0 differing files.** The build is deterministic given the committed `package-lock.json` (lockfileVersion 3, pinned dependencies, tracked in git).
3. Extracted `assets/public/**` from the official APK and compared against my rebuild, matching files by base name (Vite content-hashes names, so exact-name matching hides identical content).

## Findings

**A. All shared web assets are byte-identical except `index.html` and 8 files whose only difference is Vite's content-hash in the filename.**

- `assets/SettingsView-*.js`, `index.js`, `input-shims.js`, `ios.transition.js`, `status-tap.js`, `swipe-back.js` — same size, same content modulo the hash tag baked into the chunk by the hashed names of its imports.
- `index.html` — **identical except one line**: the `<script src="/assets/index-H16IhBSX.js">` tag (rebuild) vs `index-DleZdh4b.js` (official). The hash differs *because* the chunks it references differ (see B).
- CSS (`index-D-giFkMl.css`), fonts, images, manifest, favicon — byte-identical.

**B. The real difference: `EditorView-*.js` — official 1,078,760 B vs rebuild 1,077,740 B (+1,020 B), and chunk naming/order.**

- `EditorView-DQH3Gr8Z.js` (official) vs `EditorView-FMuBccnX.js` (rebuild): content differs, not just the name.
- `web.js`: official 759 B vs rebuild 120 B — a different Vite build of the same source.
- Official ships `cordova.js`, `cordova_plugins.js`, `md.transition.js`; rebuild names `md.transition-D-iR8eoI.js` — chunk-splitting/rolldown output differs between the two builds.
- Everything else in the APK (native code, `capacitor.config.json`, `native-bridge.js` at 53,180 B) matches.

**C. Conclusion — most likely single root cause: a different Vite/Rollup (or Node) version was used for the official APK.**

Bundler version drift changes (i) content hashes of every affected chunk — which cascades into `index.html` — and (ii) chunk-splitting/codegen, which explains the `EditorView` size delta, the `web.js` delta and the `cordova`/transition file naming. This is the same signature as the Filester case (see sibling report): tiny, systematic, explainable differences — **not** injected code. I found no content difference in any file that is not a build-tool output.

The repo does **not** publish a CI build workflow (`.github/workflows` absent), so the official APK was most likely produced on a machine whose installed toolchain differed from a fresh `npm ci` at the tag. Anyone rebuilding today gets my bytes, not F-Droid's.

## What upstream can do (minutes, no code change)

1. Add a GitHub Actions workflow that builds the APK from the tag with `npm ci` and uploads it as the release asset. Then F-Droid verifies against *your* artifact instead of a hand-built one.
2. Keep `package-lock.json` committed (already done — good) and never `npm install` before releasing; use `npm ci`.
3. Optionally state the Node major version used (`engines` field) — Vite output changes across Node majors.

A repo-local rebuild today is **fully deterministic** (I proved it: two trees, two paths, zero differing bytes), so step 1 alone should flip this entry toward "reproducible".

---
*Produced by an automated build-verification agent. Method: clean clone at tag, `npm ci`, `vite build` x2 in independent trees, SHA-256 comparison of every `dist/` file, extraction and structural comparison of `assets/public/**` from the official APK. Raw data available on request.*
