Skip to content

fix(kotlin): verify native download checksums#508

Open
shubhamsinnh wants to merge 3 commits into
RunanywhereAI:mainfrom
shubhamsinnh:codex/issue-210-kotlin-native-download-checks
Open

fix(kotlin): verify native download checksums#508
shubhamsinnh wants to merge 3 commits into
RunanywhereAI:mainfrom
shubhamsinnh:codex/issue-210-kotlin-native-download-checks

Conversation

@shubhamsinnh

@shubhamsinnh shubhamsinnh commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Fixes #210

Summary

  • Add a shared Kotlin Gradle helper for native zip downloads.
  • Use HttpURLConnection with a 30s connect timeout and 120s read timeout.
  • Fetch .sha256 sidecar files and verify downloaded zips before extraction.
  • Fail native download tasks on failed download or checksum mismatch instead of warning and continuing.
  • Publish Android backend zip .sha256 files from the release workflow for LlamaCPP and ONNX backend artifacts.

Context

A previous PR, #416, attempted this issue but was closed unmerged. One blocker noted there was that backend .sha256 files did not exist on GitHub Releases, so checksum verification could not run end-to-end. This PR includes the release packaging changes needed for future releases to publish those backend checksum sidecars.

Testing

Passed locally:

  • git diff --check
  • bash -n sdk/runanywhere-commons/scripts/build-android.sh
  • ./gradlew.bat buildSrc:build --no-daemon --console=plain
  • ./gradlew.bat help --no-daemon --stacktrace --console=plain
  • ./gradlew.bat test --no-daemon --console=plain -Prunanywhere.useLocalNatives=false -x downloadJniLibs -x :modules:runanywhere-core-llamacpp:downloadJniLibs -x :modules:runanywhere-core-onnx:downloadJniLibs
  • ./gradlew.bat lint --no-daemon --console=plain -Prunanywhere.useLocalNatives=false -x downloadJniLibs -x :modules:runanywhere-core-llamacpp:downloadJniLibs -x :modules:runanywhere-core-onnx:downloadJniLibs
  • ./gradlew.bat check --no-daemon --console=plain -Prunanywhere.useLocalNatives=false -x downloadJniLibs -x :modules:runanywhere-core-llamacpp:downloadJniLibs -x :modules:runanywhere-core-onnx:downloadJniLibs -x ktlintCheck -x ktlintMainSourceSetCheck

Known blockers observed locally:

  • Full remote-native check correctly fails against the current v0.19.13 release because RABackendLLAMACPP-android-arm64-v8a-v0.19.13.zip.sha256 returns HTTP 404. This PR fixes the release workflow for future releases, but cannot retroactively add sidecars to old releases.
  • Full unmodified check currently fails on unrelated ktlint violations in VoiceAgentMicDriver.kt and RunAnywhere.kt, which are outside this PR's scope.
  • Local-native mode on Windows fails in existing code with Unsupported host for Android NDK runtime lookup: Windows 10.

Note

Medium Risk
Builds now hard-fail on bad or unverifiable native downloads, and release/Android packaging must produce new backend artifacts; supply-chain behavior changes but does not touch runtime auth or app logic.

Overview
Kotlin Gradle native fetch now verifies release zips before extraction: a shared NativeLibraryDownload helper (in buildSrc) downloads each .zip.sha256 sidecar, compares SHA-256, uses HTTP timeouts, and fails the build on HTTP/checksum errors instead of logging a warning. The root SDK and LlamaCPP/ONNX modules all use this path for downloadJniLibs.

Android release packaging now emits per-ABI RABackendLLAMACPP-android-* and RABackendONNX-android-* archives (plus checksums) from build-android.sh, and the release workflow uploads them and requires them in the publish manifest—so future tags ship the sidecars backend downloads need. Older releases without those .sha256 files will still break remote-native builds until a new release is cut.

Reviewed by Cursor Bugbot for commit c89be6c. Configure here.

Summary by CodeRabbit

  • New Features

    • Android releases now publish backend-specific ZIP artifacts for each ABI (LlamaCPP and ONNX), each accompanied by matching .sha256 checksum files.
  • Bug Fixes

    • JNI ZIP downloads now require valid SHA-256 checksums and fail the build on mismatch or download/extraction errors, with cleanup of partially downloaded artifacts and clearer error details.
  • Chores

    • Release packaging/publishing verification was tightened to require the new backend ZIPs and checksum sidecars for all supported ABIs.
    • Android/Kotlin build scripts were updated to generate and fetch these checksum-verified artifacts reliably.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Android release packaging now emits backend-specific ZIPs and checksum files, the release workflow requires them, and JNI download tasks use a shared helper to verify ZIP checksums before extraction.

Changes

Android backend release packaging and publish checks

Layer / File(s) Summary
Backend ZIP artifacts and publish checks
sdk/runanywhere-commons/scripts/build-android.sh, .github/workflows/release.yml
Adds backend-specific Android ZIP packaging with .sha256 sidecars, uploads the new artifacts, and asserts their presence before publishing.

Shared checksum-verified download utility and JNI migration

Layer / File(s) Summary
Shared checksum-verified download utility
sdk/runanywhere-kotlin/buildSrc/build.gradle.kts, sdk/runanywhere-kotlin/buildSrc/src/main/kotlin/com/runanywhere/gradle/NativeLibraryDownload.kt
Configures buildSrc for Kotlin DSL and adds NativeLibraryDownload for HTTP ZIP downloads with timeouts, checksum parsing, digest computation, and cleanup on failure.
JNI download tasks use verified ZIPs
sdk/runanywhere-kotlin/build.gradle.kts, sdk/runanywhere-kotlin/modules/runanywhere-core-llamacpp/build.gradle.kts, sdk/runanywhere-kotlin/modules/runanywhere-core-onnx/build.gradle.kts
Migrates JNI download tasks to checksum-verified ZIP downloads and changes failures to cleanup plus GradleException.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GradleTask as downloadJniLibs task
  participant NativeLibraryDownload
  participant ReleaseServer as release ZIP host
  participant FileSystem as file system

  GradleTask->>NativeLibraryDownload: downloadZipWithChecksum(zipUrl, tempZip)
  NativeLibraryDownload->>ReleaseServer: GET zipUrl.sha256
  ReleaseServer-->>NativeLibraryDownload: checksum text
  NativeLibraryDownload->>ReleaseServer: GET zipUrl
  ReleaseServer-->>NativeLibraryDownload: zip bytes
  NativeLibraryDownload->>FileSystem: write tempZip
  NativeLibraryDownload->>FileSystem: compute SHA-256
  NativeLibraryDownload->>NativeLibraryDownload: compare checksums
  alt mismatch or HTTP error
    NativeLibraryDownload->>FileSystem: delete tempZip
    NativeLibraryDownload-->>GradleTask: throw GradleException
  else match
    NativeLibraryDownload-->>GradleTask: return success
  end
Loading

Suggested labels: kotlin-sdk

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: native download checksum verification for Kotlin builds.
Description check ✅ Passed The description covers summary, context, and testing, but omits several template sections like type of change and labels.
Linked Issues check ✅ Passed The changes implement #210’s goals: shared download helper, timeouts, SHA256 verification, and release sidecars for native artifacts.
Out of Scope Changes check ✅ Passed No clear out-of-scope changes are evident; the added build and workflow edits support the checksum-verification work.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 789-790: The assertions for Android LlamaCPP and ONNX backend
artifacts only validate ZIP file presence but omit checking for required
`.sha256` sidecar files. Add additional `assert_present` calls after the
existing assertions on lines 789-790 to validate the corresponding `.sha256`
files for both the LlamaCPP and ONNX backend artifacts, using the same glob
patterns but with `.sha256` appended to match files like
RABackendLLAMACPP-android-*-v*.zip.sha256 and
RABackendONNX-android-*-v*.zip.sha256.

In `@sdk/runanywhere-commons/scripts/build-android.sh`:
- Around line 114-116: The conditional check at lines 114-116 silently skips the
operation when extra_file is set but the file does not exist at
${STAGING}/jni/${extra_file}. Make extra_file mandatory by modifying the logic
so that if extra_file is non-empty (requested), the script fails with an error
message if the file is not found at the expected location, rather than silently
continuing. Only proceed with the cp command if the file exists, but exit the
script with an appropriate error if extra_file is set but missing.

In `@sdk/runanywhere-kotlin/modules/runanywhere-core-llamacpp/build.gradle.kts`:
- Line 220: When the download and verification fails and throws a
GradleException at line 220, the already-copied .so files from previous
successful ABIs remain on disk. On subsequent task runs, the existence check at
line 183 will find these partial files and skip the download entirely, leaving
the build in an inconsistent state. Before throwing the GradleException in the
failure path, clean up any .so files that were already copied during this task
execution so that the next retry starts from a clean state and properly
re-downloads all required ABIs.

In `@sdk/runanywhere-kotlin/modules/runanywhere-core-onnx/build.gradle.kts`:
- Line 223: When a download fails for a specific ABI in the loop, the exception
at the GradleException throw statement leaves partial .so files in outputDir.
Before throwing the exception, clean up any incomplete files that were
downloaded for the current ABI iteration to prevent the early-exit check
(existingLibs > 0) from incorrectly skipping downloads on retry and using the
broken native library set. Ensure the cleanup happens in the catch block before
the GradleException is thrown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e647cc76-5c66-43f9-86e2-3b994b3bd15c

📥 Commits

Reviewing files that changed from the base of the PR and between c272a1f and c89be6c.

📒 Files selected for processing (7)
  • .github/workflows/release.yml
  • sdk/runanywhere-commons/scripts/build-android.sh
  • sdk/runanywhere-kotlin/build.gradle.kts
  • sdk/runanywhere-kotlin/buildSrc/build.gradle.kts
  • sdk/runanywhere-kotlin/buildSrc/src/main/kotlin/com/runanywhere/gradle/NativeLibraryDownload.kt
  • sdk/runanywhere-kotlin/modules/runanywhere-core-llamacpp/build.gradle.kts
  • sdk/runanywhere-kotlin/modules/runanywhere-core-onnx/build.gradle.kts

Comment thread .github/workflows/release.yml Outdated
Comment thread sdk/runanywhere-commons/scripts/build-android.sh Outdated
@shubhamsinnh
shubhamsinnh force-pushed the codex/issue-210-kotlin-native-download-checks branch from b81c341 to 3d69032 Compare July 4, 2026 12:39
@shubhamsinnh

shubhamsinnh commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Hi @shubhammalhotra28 - All 4 CodeRabbit review suggestions have been addressed in commit b81c341 (.sha256 sidecar validation in release.yml, mandatory extra_file check in build-android.sh, cleanup-on-failure for both backend build.gradle.kts modules).

Please let me know if anything else is needed before this can be merged.

@Siddhesh2377

Copy link
Copy Markdown
Collaborator

Let us review your changes
thanks :)

@shubhamsinnh
shubhamsinnh force-pushed the codex/issue-210-kotlin-native-download-checks branch from 3d69032 to 8622e37 Compare July 10, 2026 14:49
@shubhamsinnh

shubhamsinnh commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Hi @shubhammalhotra28 @Siddhesh2377 - just following up on this.

CI is green now after the rebase. Please let me know if you want me to change anything when you get a chance.

# Conflicts:
#	.github/workflows/release.yml
#	sdk/runanywhere-commons/scripts/build-android.sh
#	sdk/runanywhere-kotlin/build.gradle.kts
#	sdk/runanywhere-kotlin/modules/runanywhere-core-onnx/build.gradle.kts
# Conflicts:
#	.github/workflows/release.yml
#	sdk/runanywhere-commons/scripts/build-android.sh
@shubhamsinnh
shubhamsinnh force-pushed the codex/issue-210-kotlin-native-download-checks branch from 8622e37 to e23c66c Compare July 13, 2026 02:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
.github/workflows/release.yml (3)

284-288: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Avoid direct template expansion of version inside the shell script.

zizmor flags this as a template-injection risk: ${{ needs.validate.outputs.version }} is expanded directly into the run: block rather than passed through an env: var, unlike the RAC_RELEASE_VERSION pattern used in native_ios/native_android. Since version is a workflow-controlled value, exploitability is low here, but aligning with the safer env-var pattern removes the flagged risk and improves consistency.

🔒 Suggested fix
           (
             cd dist
-            sha256sum "RACommons-windows-x64-v${{ needs.validate.outputs.version }}.zip" \
-              > "RACommons-windows-x64-v${{ needs.validate.outputs.version }}.zip.sha256"
+            sha256sum "RACommons-windows-x64-v${VERSION}.zip" \
+              > "RACommons-windows-x64-v${VERSION}.zip.sha256"
           )

Add the corresponding env: to the step (outside this range):

      - name: Package Windows outputs
        working-directory: sdk/runanywhere-commons
        shell: bash
        env:
          VERSION: ${{ needs.validate.outputs.version }}
        run: |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 284 - 288, Update the “Package
Windows outputs” step to pass needs.validate.outputs.version through an env
entry such as VERSION, then replace the direct GitHub template expansion in the
shell commands with that environment variable when constructing the ZIP and
checksum filenames.

Source: Linters/SAST tools


342-350: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Same template-injection pattern as the Windows job — use an env var instead.

Same zizmor finding as the Windows checksum step: ${{ needs.validate.outputs.version }} is expanded directly into tar/shasum commands here.

🔒 Suggested fix
           mkdir -p dist
-          tar czf "dist/RACommons-web-v${{ needs.validate.outputs.version }}.tar.gz" \
+          tar czf "dist/RACommons-web-v${VERSION}.tar.gz" \
             -C packages core/wasm llamacpp/wasm onnx/wasm
           (
             cd dist
-            shasum -a 256 "RACommons-web-v${{ needs.validate.outputs.version }}.tar.gz" \
-              > "RACommons-web-v${{ needs.validate.outputs.version }}.tar.gz.sha256"
+            shasum -a 256 "RACommons-web-v${VERSION}.tar.gz" \
+              > "RACommons-web-v${VERSION}.tar.gz.sha256"
           )

Add env: VERSION: ${{ needs.validate.outputs.version }} to this step, mirroring the RAC_RELEASE_VERSION pattern used elsewhere in the file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 342 - 350, Add a step-level
VERSION environment variable sourced from needs.validate.outputs.version, then
update the tar archive and shasum filename references in the release step to use
VERSION instead of directly interpolating the workflow expression, matching the
existing RAC_RELEASE_VERSION pattern.

Source: Linters/SAST tools


52-52: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Harden actions/checkout steps with persist-credentials: false.

zizmor flags every actions/checkout invocation in this workflow for not setting persist-credentials: false. By default the checkout action writes the job's GITHUB_TOKEN (or PAT) into local git config for the duration of the job; the publish job holds contents: write, and several jobs clone third-party starter repos (swift/kotlin/react-native-starter-app). Disabling credential persistence on checkouts that don't need to push is a low-cost defense-in-depth improvement.

🔒 Example fix (apply to each checkout step)
       - uses: actions/checkout@v7
+        with:
+          persist-credentials: false

Also applies to: 98-98, 139-139, 170-170, 200-200, 226-226, 257-257, 306-306, 374-374, 404-404, 432-432, 523-524, 528-533, 631-631, 636-641, 934-934, 1053-1053, 1118-1118, 1125-1130, 1366-1366

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml at line 52, Update every actions/checkout step
in the release workflow, including the listed checkout invocations, to set with
persist-credentials disabled. Apply this consistently to checkout steps that
clone the repository or third-party starter repos, without changing their
existing checkout inputs or behavior.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 284-288: Update the “Package Windows outputs” step to pass
needs.validate.outputs.version through an env entry such as VERSION, then
replace the direct GitHub template expansion in the shell commands with that
environment variable when constructing the ZIP and checksum filenames.
- Around line 342-350: Add a step-level VERSION environment variable sourced
from needs.validate.outputs.version, then update the tar archive and shasum
filename references in the release step to use VERSION instead of directly
interpolating the workflow expression, matching the existing RAC_RELEASE_VERSION
pattern.
- Line 52: Update every actions/checkout step in the release workflow, including
the listed checkout invocations, to set with persist-credentials disabled. Apply
this consistently to checkout steps that clone the repository or third-party
starter repos, without changing their existing checkout inputs or behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 79139cdf-db89-4c18-af49-c9ed46ccb44e

📥 Commits

Reviewing files that changed from the base of the PR and between 8622e37 and e23c66c.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

@Siddhesh2377

Copy link
Copy Markdown
Collaborator

Hey @shubhamsinnh there are few upgrades in ci-cd and kt releases so this pr has to wait

@shubhamsinnh

Copy link
Copy Markdown
Contributor Author

Noted, @Siddhesh2377 thanks for the update.

I have a few other PRs open for review. I would appreciate your feedback on them as well.

happy to incorporate any changes or feedback you might have!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add timeout and SHA256 checksum verification for native library downloads

2 participants