Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/android-emulator-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ on:
required: false
default: ""
workflow_dispatch:
inputs:
build_run_id:
description: Build run containing the combined Android artifacts
type: string
required: true
flavor:
description: Runtime flavors available in that build
type: choice
options: [both, full, lite]
default: both

env:
ANDROID_NDK_VERSION: r29
Expand Down Expand Up @@ -51,7 +61,7 @@ jobs:
strategy:
fail-fast: false
matrix:
flavor: [full, lite]
flavor: ${{ fromJSON(inputs.flavor == 'lite' && '["lite"]' || inputs.flavor == 'full' && '["full"]' || '["full","lite"]') }}

runs-on: ubuntu-24.04
timeout-minutes: 90
Expand Down
15 changes: 14 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ jobs:
# change to these flags must invalidate the cached libnode. Never in
# workflow env: it would leak into the iOS link, which Apple's ld
# rejects ("ld: unknown options: -z").
ANDROID_LDFLAGS: "-Wl,-z,max-page-size=16384"
ANDROID_LDFLAGS: "-Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384"

steps:
- uses: actions/checkout@v7
Expand Down Expand Up @@ -193,6 +193,7 @@ jobs:
# binary, so they must change the key.
H=$( { git rev-parse "HEAD:src" "HEAD:deps" "HEAD:lib" "HEAD:tools/v8_gypfiles" "HEAD:tools/gyp" "HEAD:tools/icu"; \
git hash-object node.gyp node.gypi common.gypi configure configure.py android-configure android_configure.py tools/android_build.sh tools/copy_libnode_headers.sh; \
if [ -f tools/android_openssl.sh ]; then git hash-object tools/android_openssl.sh; else echo no-android-openssl; fi; \
printf '%s\n' "$ANDROID_LDFLAGS" "$ANDROID_TARGET_SDK_VERSION"; \
} | git hash-object --stdin )
echo "key=rust${NODEJS_MOBILE_RUST_TOOLCHAIN}-libnode-android-${TARGET_ARCH}${FLAVOR_SUFFIX}-ndk${ANDROID_NDK_VERSION}-${H}" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -248,6 +249,18 @@ jobs:
run: |
./tools/android_build.sh $MY_ANDROID_NDK_HOME $ANDROID_TARGET_SDK_VERSION $TARGET_ARCH

# The Android build links a prebuilt static OpenSSL 4.x tree through
# --shared-openssl (docs/OPENSSL-4.md). Prove the shipped library carries
# it. Runs on cache hits too, where out_android is restored.
- name: Verify OpenSSL 4.x linkage
run: |
case "$TARGET_ARCH" in
arm) abi=armeabi-v7a ;;
arm64) abi=arm64-v8a ;;
x86_64) abi=x86_64 ;;
esac
grep -a -o -m1 'OpenSSL 4\.0\.[0-9]*' "out_android/$abi/libnode.so"

# NAPI/addon headers are static src/ + deps/ files (no build output, no
# toolchain), so stage them unconditionally — this survives a libnode cache
# hit (where the Build step is skipped) and lets combine-android just merge
Expand Down
132 changes: 132 additions & 0 deletions .github/workflows/openssl4-android.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: OpenSSL 4 Android client

on:
push:
branches: [openssl4/node26.12.0]
paths: [patches/**, mobile-src/**, expected-tree.txt, .github/workflows/openssl4-android.yml]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: openssl4-android-${{ github.ref }}
cancel-in-progress: false

env:
NODEJS_MOBILE_FLAVOR: lite
SCCACHE_IDLE_TIMEOUT: '0'
SCCACHE_LOG: warn

jobs:
build:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
arch: [arm64, arm, x86_64]
env:
LDFLAGS: -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/materialize
- name: Install build tools
run: |
sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib clang-19
echo CC_host=clang-19 >> "$GITHUB_ENV"
echo CXX_host=clang++-19 >> "$GITHUB_ENV"
- uses: actions/setup-python@v6
with:
python-version: '3.13'
- run: python -m pip install setuptools packaging
- uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779
id: ndk
with:
ndk-version: r29
add-to-path: false
- uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba
- uses: ./.github/actions/restore-compiler-cache
id: objects
with:
lane: android-${{ matrix.arch }}-lite-ndkr29
# This development workflow only uses the local cache. No R2 credentials,
# tags, releases or publication permissions enter this job.
- name: Build OpenSSL 4 and native QUIC
env:
NODEJS_MOBILE_SCCACHE: '1'
NDK: ${{ steps.ndk.outputs.ndk-path }}
ARCH: ${{ matrix.arch }}
run: |
./tools/android_build.sh "$NDK" 24 "$ARCH"
./tools/copy_libnode_headers.sh android
python tools/strip-android-runtime.py --ndk "$NDK" out_android
- name: Flush compiler objects
if: always() && steps.objects.outcome == 'success'
run: sccache --stop-server || true
- uses: actions/cache/save@v6
if: always() && steps.objects.outcome == 'success'
with:
path: ${{ runner.temp }}/nodejs-mobile-sccache
key: ${{ steps.objects.outputs.key }}
- uses: actions/upload-artifact@v7
with:
name: openssl4-${{ matrix.arch }}
path: out_android
retention-days: 30
package:
needs: build
runs-on: ubuntu-24.04
steps:
- uses: actions/download-artifact@v8
with:
pattern: openssl4-*
merge-multiple: true
path: out_android
- name: Package the verified ABI set
run: |
mkdir -p artifacts/bin artifacts/include
for abi in arm64-v8a armeabi-v7a x86_64; do
test -s "out_android/$abi/libnode.so"
grep -a -o -m1 'OpenSSL 4\.0\.3' "out_android/$abi/libnode.so"
cp -R "out_android/$abi" artifacts/bin/
done
cp -R out_android/libnode/include/* artifacts/include/
- uses: actions/upload-artifact@v7
with:
name: nodejs-mobile-android-lite
path: artifacts
retention-days: 90
smoke:
needs: package
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/materialize
- uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779
id: ndk
with:
ndk-version: r29
add-to-path: false
- uses: actions/download-artifact@v8
with:
name: nodejs-mobile-android-lite
path: artifacts
- name: Enable KVM and build embedder
env:
NDK: ${{ steps.ndk.outputs.ndk-path }}
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
./tools/mobile-test/smoke/build-android-embedder.sh "$NDK" x86_64 "$PWD/artifacts/bin/x86_64" "$PWD/node_smoke"
cp "$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so" "$PWD/libc++_shared.so"
- uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d
with:
api-level: 30
arch: x86_64
target: default
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: ./tools/mobile-test/smoke/run-android-smoke.sh "$PWD/node_smoke" "$PWD/artifacts/bin/x86_64/libnode.so" "$PWD/libc++_shared.so" && ./tools/mobile-test/smoke/run-android-quic-smoke.sh "$PWD/node_smoke" "$PWD/artifacts/bin/x86_64/libnode.so" "$PWD/libc++_shared.so"
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@ in-app, with npm modules and N-API native addons.
| Android | `libnode.so` + headers | `arm64-v8a`, `armeabi-v7a`, `x86_64` (16 KB page size) |
| iOS | `NodeMobile.xcframework` | `arm64` device, `arm64` simulator |

Current line: **Node.js 26** (`v26.11.0`, upstream PR preview).
This checkout pins [official release proposal #66546](https://github.com/nodejs/node/pull/66546)
at `49072a0be4c7410982557aebf5b5c924fe7db597`, before its merge/tag.
Builds are CI previews; [provenance and validation](docs/UPSTREAM-26.11.md)
record how to move to the official tag after release.
Current line: **Node.js 26** (`v26.12.1`, official release).
This checkout pins the official tag `v26.12.1` at commit
`bc2e01dcf6acd5cf7d169b5f26cf89abaaeb41f1`. It is upstream's security
release for the 26.12 line, and the patch series moves forward from the
reviewed 26.12.0 line with no mobile-code changes beyond upstream context
updates. [Provenance and validation](docs/UPSTREAM-26.11.md) record the
rebase.
Android builds link a prebuilt static OpenSSL 4.0.3 tree instead of the
bundled 3.5.x line; see [Android OpenSSL 4.x](docs/OPENSSL-4.md).
The stable Node 24 line continues independently on [`recipe`](https://github.com/fogtape/nodejs-mobile/tree/recipe).
See [maintained version lines](docs/VERSION-LINES.md) for branch selection,
build instructions and download locations. `process.version` reports the
upstream version unchanged, so version-parsing tools keep working; the
mobile build is identified by `process.versions.mobile` (e.g. `"26.11.0-0"`).
mobile build is identified by `process.versions.mobile` (e.g. `"26.12.1-0"`).

## Get it

Expand Down Expand Up @@ -57,7 +61,7 @@ for building one — about 1.5 MB instead of a 1 GB fork:

| | |
|---|---|
| `upstream-base.txt` | the pinned upstream PR commit (see [26.11 provenance](docs/UPSTREAM-26.11.md)) |
| `upstream-base.txt` | the pinned upstream release tag (see [26.11 provenance](docs/UPSTREAM-26.11.md)) |
| `patches/` | per-concern patches to upstream files, plus `series` (apply order) and `files.map` (which patch owns which file) |
| `mobile-src/` | files that have no upstream counterpart — build scripts, the iOS framework project, test apps and harness |
| `expected-tree.txt` | the git tree hash the reconstruction must produce |
Expand Down
17 changes: 14 additions & 3 deletions docs/BUILDING.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Build Instructions

These instructions describe the Node 26 line. This checkout is the pinned
26.11 upstream PR preview on `upgrade/node26.11-mobile-lite`;
see [provenance](UPSTREAM-26.11.md). For Node 24,
26.12 upstream line on `openssl4/node26.12.0`; see
[provenance](UPSTREAM-26.11.md). For Node 24,
use the `recipe` branch and its toolchain instructions. See
[VERSION-LINES.md](VERSION-LINES.md) for the maintenance policy and the
Node 26 FFI/Temporal configuration.
Expand Down Expand Up @@ -88,7 +88,7 @@ sdkmanager "ndk;29.0.14206865"
This repository holds the recipe, not the source. Generate a tree from it:

```sh
git clone -b upgrade/node26.11-mobile-lite https://github.com/fogtape/nodejs-mobile
git clone -b openssl4/node26.12.0 https://github.com/fogtape/nodejs-mobile
cd nodejs-mobile && scripts/prepare.sh && cd out
```

Expand Down Expand Up @@ -120,6 +120,17 @@ make
# -> out/Release/lib.target/libnode.so
```

### Android OpenSSL 4.x

Android builds link a prebuilt static OpenSSL 4.x tree instead of the
bundled 3.5.x line. `tools/android_build.sh` fetches and builds the pinned
release asset per ABI (`tools/android_openssl.sh`, OpenSSL 4.0.3, SHA-256
verified) and passes it to configure through
`NODEJS_MOBILE_ANDROID_OPENSSL_DIR`; the linked `libnode.so` is checked for
the embedded version string. Set `NODEJS_MOBILE_ANDROID_OPENSSL=0` to build
against the bundled line instead. iOS is unaffected. Details, rationale and
maintenance: [OPENSSL-4.md](OPENSSL-4.md).

---

## iOS — build on macOS
Expand Down
84 changes: 84 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
</tr>
<tr>
<td>
<a href="#26.12.1-0">26.12.1-0</a><br/>
<a href="#26.12.0-0">26.12.0-0</a><br/>
<a href="#26.11.1-0">26.11.1-0</a><br/>
<a href="#26.11.0-0">26.11.0-0</a><br/>
<a href="#26.10.0-0">26.10.0-0</a><br/>
<a href="#26.1.0-0">26.1.0-0</a><br/>
Expand Down Expand Up @@ -42,6 +45,87 @@
</tr>
</table>

<a id="26.12.1-0"></a>
## 2026-10-21, Version 26.12.1-0 (Current)

- Rebase onto the official Node.js `v26.12.1` security release
(`bc2e01dcf6acd5cf7d169b5f26cf89abaaeb41f1`); the regenerated series
changes only patch context and line numbers, and the mobile hunks in the
two upstream files it touches (`common.gypi`,
`src/crypto/crypto_context.cc`) are unchanged.
- Pick up upstream's ten CVE fixes (four High): tls non-canonical IP
addresses and IP normalization, dns resolve null bytes, the http agent
data guard, brotli block-switch decoding, zlib ZIP local headers,
permission-model wildcard/`realpathSync` checks and crypto snapshot
random caches. Also carry c-ares 1.34.9, undici 8.12.2, root certificates
NSS 3.131.1 and the "synchronously load most ES modules" revert.
- Android keeps the prebuilt static OpenSSL 4.0.3 tree with native ECH/H3,
so this release's bundled-OpenSSL 3.5.9 update does not apply to the
Android runtime. iOS and the bundled fallback keep their existing line.
- Keep the danmu lite feature contract unchanged: complete ICU API with the
reviewed data profile, web encodings, Chinese collation and normalization;
omit Temporal, FFI, Inspector, SQLite/Web Storage, Amaro and SEA.
- Exclude the new `test-crypto-random-cache-snapshot` regression on Android
and iOS: it builds and runs snapshots through `common/snapshot`, which
spawns child Node executables. The remaining new in-process regressions
(tls non-canonical IP and SPKI blocklist, dns null bytes, http agent
pending read, X.509 name conversion, permission audit `realpathSync`) stay
runnable.
- Triage the rest of the full device suite. `test-crypto-x509` runs its
malformed-certificate legacy conversion in-process on both platforms
instead of in a child, and `test-fastutf8stream-flush-mocks` keeps every
in-process case while guarding only the one that needs a child with a
regular-file stdout. `test-bench-cli-ack`, `test-bench-run-exit-code`,
`test-common-wpt-allowed-rejections`, `test-permission-allow-fs-vfs-flags`
and `sequential/test-benchmark-node-bench-tools` are excluded because they
spawn child Node executables, and `test-sqlite-statement` because its
oversized-string cases outgrow the app heap.
- Exclude `test-webcrypto-fips-refresh` on both platforms: switching the FIPS
provider at runtime does not finish inside the suite timeout on a device,
the same limitation that already excludes `test-crypto-fips`.
- Android QUIC key-update and H3 regressions are excluded with their reason
recorded: seven tests abort inside ngtcp2 with
`conn->state == NGTCP2_CS_POST_HANDSHAKE` in `conn_initiate_key_update`.
They are unchanged by 26.12, so this belongs to the OpenSSL 4 QUIC line and
is carried as a follow-up rather than a regression of this upgrade; the
other 261 QUIC tests still run on Android.
- This source upgrade does not itself switch `danmu-api-android` to 26.12.1.
A new runtime archive, checksum update and device validation are separate,
reviewed steps.

<a id="26.12.0-0"></a>
## 2026-10-18, Version 26.12.0-0 (Current)

- Rebase the mobile patch series onto the official Node.js `v26.12.0` tag
(`26e72d85a611e222e3f06025de8a3c496923bacd`). All 24 patches apply; the
OpenSSL 4 ECH/H3 and QUIC changes carry forward unchanged, and the
upstream-workflow removal patch absorbs 16 upstream workflow edits.
- 26.12.0 is a regular Current minor release with no security designation:
V8, libuv, npm and OpenSSL versions are unchanged from 26.11.1. Relevant
mobile changes are upstream HTTP/stream/ESM fixes and the new
`http.websocketMask()` / `websocketUnmask()` helpers.
- Keep the danmu lite feature contract unchanged: complete ICU API with the
reviewed data profile, web encodings, Chinese collation and normalization;
omit Temporal, FFI, Inspector, SQLite/Web Storage, Amaro and SEA. Android
retains JIT/native WASM, JNI/ECH/H3 and 16 KB page alignment.
- This source upgrade does not itself switch `danmu-api-android` to 26.12.
A new runtime archive, checksum update and device validation are separate,
reviewed steps.

<a id="26.11.1-0"></a>
## 2026-10-14, Version 26.11.1-0 (Current)

- Rebase onto the official Node.js `v26.11.1` release tag
(`364593d0631def13e3043dd84f77ebca2c767ce5`); the regenerated series
changes only the upstream-workflow removal patch.
- Android links a prebuilt static OpenSSL 4.0.3 tree built by
`tools/android_openssl.sh` (`--shared-openssl`, SHA-256-verified release
asset) instead of the bundled 3.5.x line. Android enables native QUIC/H3
with the vendored ngtcp2/nghttp3 libraries. TLS and QUIC client options
accept a mandatory ECHConfigList and expose authenticated acceptance;
rejected ECH cannot fall back to plaintext SNI. iOS and the bundled
OpenSSL fallback retain their previous library and no ECH support.

<a id="26.11.0-0"></a>
## 2026-10-07, Version 26.11.0-0 (Current)

Expand Down
2 changes: 1 addition & 1 deletion docs/DANMU-LITE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ cannot be inferred from today's imports.
before/after trimming and lets icupkg validate package dependencies. A unique
intermediate package name prevents an installed host's full ICU from hiding
missing resources. The final conventional data entry point is preserved.
With the Node 26.11 proposal’s bundled ICU data, the production helper
With the Node 26.12 line’s bundled ICU data, the production helper
reduces 33,107,952 to 11,026,480 bytes (22,081,472 bytes removed). This
measures data only, not a complete newly compiled libnode or compressed ZIP.

Expand Down
Loading
Loading