Repository navigation
v0.1.3
v0.1.3 — maintenance update
A maintenance release for the Android full node. It fixes the findings of a full-section audit of v0.1.2 (thirteen sections; no critical- or high-severity issues — three medium and seventeen low, of which the three mediums and the consequential lows are fixed here, and two lows are deferred and named below). No changes to the node's launch arguments, the database format, or the app's permissions: it installs over v0.1.2 and keeps your settings and synced data — verified on the reference device, where the node resumed at the chain tip after the update without re-syncing, a stop → immediate start worked cleanly, and the in-app footer now shows the real version.
What's fixed
- The cancelled-start fix from v0.1.2 is now complete — the bounded wait for a destroyed child reported an outcome the teardown never checked, so the pid record was cleared even when the child had outlived the 5-second bound, reopening exactly the double-start the wait was meant to close. The record is now cleared only when the exit is confirmed; otherwise it stays, and a later launch waits on the process through the usual leftover path. (Verified on the JVM with real processes, including a child that ignores SIGTERM.)
- An orphaned node can no longer block starting forever — if the app process died while its node child survived, every later start refused for as long as the orphan lived. When the launching session is provably the first one of this process (so the leftover has no living owner) and the recorded pid still strictly names this app's own node executable, the orphan is terminated and startup proceeds. Any doubt falls back to the previous wait-and-refuse behaviour.
- A new session starts from unknown values — best/highest/peers were retained across sessions, so a new session's first judgement could call it synced on the old session's numbers, and its starting screen and first notification showed stale values. They are now reset at session establishment.
- Missing RPC fields no longer overwrite known values — a health response without "peers" wrote a real 0 over the retained count, and a sync response without "currentBlock" flipped a synced node back to syncing. An absent field now means "not said", following the same principle as the highestBlock fix in v0.1.2.
- Display-name sanitizing covers the directional marks — U+200E, U+200F and U+061C joined the stripped set, along with the word joiner, the deprecated invisible operators and the soft hyphen. The zero-width non-joiner and joiner are still kept: they carry meaning in Arabic-script and Indic names.
- Sharing the log is bounded and coordinated — the share read used to load the whole file outside the writers' lock, so a rotation mid-read could hand over a partial log that looked complete, and the tail cut could split a character. It now reads a bounded tail under the writers' own lock, starts at a line boundary, and never splits a pair. What is shared is unchanged: the last 100,000 characters.
- Log rotation is atomic — the retained tail is written to a temp file and renamed over the log, instead of truncate-and-rewrite in place, which lost the whole retained half if the process died mid-rotation.
- Settings reads survive a damaged preferences file — a stored value of the wrong type crashed the UI with a ClassCastException (only reachable if the preferences file is damaged outside the app). Reads now fall back to defaults, and a failed synchronous uptime save is retried asynchronously.
- The in-app footer shows the real version again — v0.1.2 shipped with "v0.1.1" in the footer of all nine languages, the same staleness class an earlier release had already fixed once. All nine footers now read v0.1.3, and the project's release checklist names the footer as a version-bump item with a mechanical check.
- The usage guides describe the present again — all nine USAGE files still described the v0.1.0 state (how to obtain the APK, telemetry listing as unverified). They now state the current release, point at GitHub Releases, and record the verified telemetry listing. Their measured figures were audited and are unchanged. SECURITY.md's status and audit record and the app README's figures were likewise brought current.
- Build hygiene — the manual build now verifies the signed APK before placing it at the release path (a failed verification fails the build), and the stale-APK set-aside now happens before every preflight exit, closing the last gap of the v0.1.2 fix.
- Deferred, deliberately — two lows were left for later and are recorded in the CHANGELOG: a cosmetic log-line confusion when a leftover refusal also triggers the linker retry, and an unoptimized per-second text refresh in the UI (its visible harm was already suppressed by the scroll fix).
Verification
- The audit was run in three parallel passes over all thirteen sections; its two most consequential claims were independently re-read in the code before this release was cut.
- JVM checks (55 + 13 cases): the name-sanitizing suite with the new invisible characters (and the kept ZWNJ/ZWJ regression cases), the bounded exit wait's both branches against real processes, log rotation including the atomic rewrite, the bounded share read (line boundaries, surrogate safety), the status-judgement expressions over the audit's scenarios, translation tables (49 keys × 9 languages, all footers at v0.1.3), and an API-surface re-check (no reference beyond API 31).
- Negative tests: an invalid build-mode setting now still sets the previous APK aside before failing; the post-signing verification step passes on real builds.
- Byte-level comparison with the v0.1.2 APK: only
classes.dex, the version inAndroidManifest.xml(exactly two bytes), and the signature files differ — the node binary, the CA bundle and all resources are byte-identical. - On device (Pixel 9a, GrapheneOS): updated in place over v0.1.2, resumed at the tip, steady state; footer shows v0.1.3; stop → immediate start confirmed working.
- Honest notes: the orphan-recovery kill path and the race sequences are verified by code tracing and JVM exercises, not reproduced on a device; a full Gradle build remains not runnable in the maintainer's build environment, so Gradle compilation itself is still unverified.
Requirements
- Android 12 or newer, arm64 device
- Verified on: Pixel 9a + GrapheneOS. Other devices are expected to work, but vendor power management may stop the background service
- About 3 GB of free storage (measured after a full sync: 1.30 GB total)
- Wi-Fi recommended for the first sync (about 2–3 hours; battery went from 71% to 27% in our first-sync test, including time on mobile data)
Install
- Download
quantus-node-v0.1.3.apkfrom the assets below. - Install it (you may need to allow installs from your browser/file manager). If v0.1.2 is installed, install over it — do not uninstall first, or your synced data will be lost.
- Open the app, tap Start, and keep it charging on Wi-Fi for the first sync.
- Optional: set a node name and turn telemetry on in Settings if you want to appear on the public telemetry list.
See the repository's Usage Guide for details and troubleshooting.
Checksum (SHA-256)
dff1df99e5fa75f253477dcae1e46c04a2cf2672c4411c88b8207cdf639680e0 — quantus-node-v0.1.3.apk (55,700,181 bytes)