diff --git a/README.md b/README.md
index 23948cffcd..75a9808c23 100644
--- a/README.md
+++ b/README.md
@@ -9,18 +9,18 @@ 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 24 LTS**, previewing the `v24.22.0` release proposal
-[#66667](https://github.com/nodejs/node/pull/66667) at a fixed commit. The
-official upstream tag is not published yet. `process.version` reports the
+Current line: **Node.js 24 LTS** (`v24.22.1`, official security release).
+The pin is the official tag `v24.22.1` at commit
+`ce7aaf1cee78b8cf0ce17364260535c418d3ed26`. `process.version` reports the
upstream version unchanged, so version-parsing tools keep working; the
-mobile build is identified by `process.versions.mobile` (`"24.22.0-0"`).
-See [the 24.22 preview notes](docs/UPSTREAM-24.22.md) for source provenance.
+mobile build is identified by `process.versions.mobile` (`"24.22.1-0"`).
+See [the 24.22 line notes](docs/UPSTREAM-24.22.md) for source provenance.
## Maintained version lines
| Branch | Node.js base | Purpose |
| --- | --- | --- |
-| [`recipe`](https://github.com/fogtape/nodejs-mobile/tree/recipe) | 24.22.0 proposal | Node 24 LTS maintenance and CI previews |
+| [`recipe`](https://github.com/fogtape/nodejs-mobile/tree/recipe) | 24.22.1 (official tag) | Node 24 LTS maintenance and CI previews |
| [`recipe-v26`](https://github.com/fogtape/nodejs-mobile/tree/recipe-v26) | 26.1.0 | Independent Node 26 port and CI previews |
Each branch owns its upstream tag, patches, source-tree checksum and toolchain.
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 6c5c2769b0..9232a128b2 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -6,6 +6,7 @@
+24.22.1-0
24.22.0-0
24.21.0-0
24.20.0-0
@@ -40,6 +41,42 @@
|
+
+## 2026-10-21, Version 24.22.1-0 (Current)
+
+- Move off the 24.22.0 release-proposal snapshot to the official Node.js
+ `v24.22.1` security release
+ (`ce7aaf1cee78b8cf0ce17364260535c418d3ed26`). The reviewed proposal commit
+ differs from the published `v24.22.0` tag only in
+ `doc/changelogs/CHANGELOG_V24.md`. The rebase re-emitted 20 patches; only
+ `common.gypi` and the mobile adjustment in `src/crypto/crypto_context.cc`
+ changed, and only in context/line numbers.
+- Pick up upstream's seven CVE fixes for this line: tls IP normalization and
+ non-canonical IP addresses, dns resolve null bytes, the http agent data
+ guard, the permission model in `realpathSync`, crypto snapshot random
+ caches and brotli block-switch decoding. Also carry OpenSSL 3.5.9,
+ c-ares 1.34.9, undici 7.30.1, tzdata 2026e, root certificates NSS 3.131.1
+ and the rejection of certificate chains with blocked public keys.
+- 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) stay runnable; the full device suite
+ confirms them.
+- Run `test-crypto-x509`'s malformed-certificate legacy conversion in-process
+ on Android and iOS instead of in a child Node. Upstream added the child to
+ report a native regression as a signal; an app cannot spawn one (Android's
+ `process.execPath` is `app_process`, which aborts with "Error changing
+ dalvik-cache ownership : Permission denied"). The same conversion and
+ assertions run in-process, and a native abort still fails the run through
+ the harness crash verdict.
+- Runtime and flavor configuration is unchanged: complete ICU API, the
+ reviewed danmu lite data profile, Android JIT/native WebAssembly and
+ pointer compression, iOS jitless V8 with polywasm.
+- This source upgrade does not itself publish. `release-ready.txt` and
+ `release-upstream-snapshot.txt` are left empty: an official tag now backs
+ the line, and arming a version is the reviewed Cut release step.
+
## 2026-10-13, Version 24.22.0-0 (Current)
diff --git a/docs/UPGRADING.md b/docs/UPGRADING.md
index bb4b17ea89..c69476cbbe 100644
--- a/docs/UPGRADING.md
+++ b/docs/UPGRADING.md
@@ -22,7 +22,7 @@ The upgrade builds both Android/iOS flavors, runs the full device gates and
packages versioned CI previews. Cut release and publication require an
official upstream tag on this line. Once the tag exists, compare it against
the pinned proposal, update the pin, regenerate and rerun the gates.
-See [the Node 24.22 proposal](UPSTREAM-24.22.md) and the current
+See [the Node 24.22 line notes](UPSTREAM-24.22.md) and the current
[manual release procedure](RELEASING.md).
## Re-base the series
diff --git a/docs/UPSTREAM-24.22.md b/docs/UPSTREAM-24.22.md
index 359ca0b769..c338310af9 100644
--- a/docs/UPSTREAM-24.22.md
+++ b/docs/UPSTREAM-24.22.md
@@ -1,16 +1,27 @@
-# Node 24.22 LTS upstream PR preview
-
-This recipe previews the official Node.js release proposal
-[#66667](https://github.com/nodejs/node/pull/66667), titled
-`2026-10-14, Version 24.22.0 'Krypton' (LTS)`. On 2026-10-13 it is still a
-draft PR, with no official `v24.22.0` tag.
-
-The reviewed baseline is pinned in `upstream-base.txt`:
-`a5d0eb95dd03b90986ad1a7c0cd3f2de1a0f383c`.
-The upstream header identifies Node 24.22.0 and the mobile header identifies
-24.22.0-0. Those version strings identify the proposed source; they do not
-mean that the upstream release has been published. The immutable commit and
-`expected-tree.txt` identify the build if the proposal branch changes.
+# Node 24.22 LTS upstream line
+
+`upstream-base.txt` pins the official tag `v24.22.1`
+(`ce7aaf1cee78b8cf0ce17364260535c418d3ed26`), upstream's security release
+for this line. It replaces the reviewed release-proposal snapshot of
+[#66667](https://github.com/nodejs/node/pull/66667), pinned at
+`a5d0eb95dd03b90986ad1a7c0cd3f2de1a0f383c`. That proposal was published as
+`v24.22.0` on 2026-10-14, and its commit differs from the reviewed snapshot
+only in `doc/changelogs/CHANGELOG_V24.md`.
+
+The mobile header now reports `24.22.1-0`, and `expected-tree.txt` records
+the rebuilt product tree. The rebase re-emitted 20 patches; only two changed
+and only in context/line numbers — `common.gypi` (`v8_embedder_string`) and
+the mobile adjustment in `src/crypto/crypto_context.cc`, whose upstream
+X.509 hardening lands ahead of the mobile hunks. The mobile hunks themselves
+are unchanged.
+
+The release carries upstream's security fixes for the line: seven CVEs
+covering tls IP normalization and non-canonical IP addresses, dns resolve
+null bytes, the http agent data guard, the permission model in
+`realpathSync`, crypto snapshot random caches and brotli block-switch
+decoding. It also updates OpenSSL to 3.5.9, c-ares to 1.34.9, undici to
+7.30.1, tzdata to 2026e and the root certificates to NSS 3.131.1, and
+rejects certificate chains containing blocked public keys.
## Lite configuration
@@ -197,8 +208,16 @@ The updated harness is included in source tree
## Move to the official release
-After upstream publishes `v24.22.0`, compare its commit with this baseline,
-review any differences, update `upstream-base.txt`, regenerate the recipe and
-checksum, and rerun platform/device validation. Follow
-[RELEASING.md](RELEASING.md) for reviewed version selection and explicit
-publication. A source upgrade or ordinary build does not publish by itself.
+This branch performed the move: it pins `v24.22.1`, the security release
+that followed the reviewed proposal. The proposal commit was compared with
+the published `v24.22.0` tag first — the two trees differ only in
+`doc/changelogs/CHANGELOG_V24.md` — and the recipe was then rebased onto
+`v24.22.1`, regenerated and re-verified.
+
+The procedure, retained for the record: after upstream publishes a release,
+compare its commit with the pin, review any differences, update
+`upstream-base.txt` to the official tag, regenerate the recipe and checksum,
+and rerun platform/device validation. Default Cut release and publication
+reject SHA baselines; follow [RELEASING.md](RELEASING.md) for reviewed
+version selection and explicit publication. A source upgrade or ordinary
+build does not publish by itself.
diff --git a/expected-tree.txt b/expected-tree.txt
index 1c24087e77..081bf8460e 100644
--- a/expected-tree.txt
+++ b/expected-tree.txt
@@ -14,4 +14,4 @@
# run prepare.sh (or regenerate-patches.py, which prints the hash) and
# update the value below in the same commit. If you forget, CI fails and
# prints the hash it got.
-786da7b5d33a6cabb31668f3089731b3c0a62713
+59a1f9b50b39080936dc0e78838b3945da83d8e0
diff --git a/mobile-src/src/node_mobile_version.h b/mobile-src/src/node_mobile_version.h
index 4c9c6a69fe..d3e39ec7af 100644
--- a/mobile-src/src/node_mobile_version.h
+++ b/mobile-src/src/node_mobile_version.h
@@ -5,7 +5,7 @@
#define NODE_MOBILE_MAJOR_VERSION 24
#define NODE_MOBILE_MINOR_VERSION 22
-#define NODE_MOBILE_PATCH_VERSION 0
+#define NODE_MOBILE_PATCH_VERSION 1
// Mobile rebuild revision of the same upstream Node version: the -R in the
// vX.Y.Z-R release tag. Computed and bumped by the Cut release workflow
diff --git a/patches/build-mobile-platform-settings-in-common.gypi.patch b/patches/build-mobile-platform-settings-in-common.gypi.patch
index 7fbc34d3b8..e1b93eca83 100644
--- a/patches/build-mobile-platform-settings-in-common.gypi.patch
+++ b/patches/build-mobile-platform-settings-in-common.gypi.patch
@@ -23,7 +23,7 @@ Co-authored-by: João Matos
1 file changed, 118 insertions(+), 7 deletions(-)
diff --git a/common.gypi b/common.gypi
-index bc690955d3..b3a74cb587 100644
+index b416129334..ad5d99fb7f 100644
--- a/common.gypi
+++ b/common.gypi
@@ -115,7 +115,7 @@
diff --git a/patches/files.map b/patches/files.map
index 62dc68ad71..b3debad779 100644
--- a/patches/files.map
+++ b/patches/files.map
@@ -33,6 +33,7 @@ test-mobile-test-harness.patch test/message/message.status
test-mobile-test-harness.patch test/message/testcfg.py
test-mobile-test-harness.patch tools/test.py
test-skip-adapt-parallel-and-sequential-tests-for-mo.patch test/parallel/parallel.status
+test-skip-adapt-parallel-and-sequential-tests-for-mo.patch test/parallel/test-crypto-x509.js
test-skip-adapt-parallel-and-sequential-tests-for-mo.patch test/parallel/test-dgram-bind-fd.js
test-skip-adapt-parallel-and-sequential-tests-for-mo.patch test/parallel/test-dgram-membership.js
test-skip-adapt-parallel-and-sequential-tests-for-mo.patch test/parallel/test-dgram-socket-buffer-size.js
diff --git a/patches/src-crypto-mobile-adjustment-in-crypto_context.cc.patch b/patches/src-crypto-mobile-adjustment-in-crypto_context.cc.patch
index 66fd2c1c8c..7ec99a956d 100644
--- a/patches/src-crypto-mobile-adjustment-in-crypto_context.cc.patch
+++ b/patches/src-crypto-mobile-adjustment-in-crypto_context.cc.patch
@@ -19,10 +19,10 @@ Co-authored-by: Julia Samól
1 file changed, 17 insertions(+)
diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc
-index 74ea622dad..575cbb51ff 100644
+index 71debb0805..a3ad327890 100644
--- a/src/crypto/crypto_context.cc
+++ b/src/crypto/crypto_context.cc
-@@ -336,6 +336,9 @@ bool isSelfIssued(X509* cert) {
+@@ -341,6 +341,9 @@ bool isSelfIssued(X509* cert) {
#ifdef __APPLE__
TrustStatus IsTrustDictionaryTrustedForPolicy(CFDictionaryRef trust_dict,
bool is_self_issued) {
@@ -32,7 +32,7 @@ index 74ea622dad..575cbb51ff 100644
// Trust settings may be scoped to a single application
// skip as this is not supported
if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsApplication)) {
-@@ -427,6 +430,10 @@ TrustStatus IsTrustDictionaryTrustedForPolicy(CFDictionaryRef trust_dict,
+@@ -432,6 +435,10 @@ TrustStatus IsTrustDictionaryTrustedForPolicy(CFDictionaryRef trust_dict,
return (trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
? TrustStatus::TRUSTED
: TrustStatus::UNSPECIFIED;
@@ -43,7 +43,7 @@ index 74ea622dad..575cbb51ff 100644
}
TrustStatus IsTrustSettingsTrustedForPolicy(CFArrayRef trust_settings,
-@@ -498,6 +505,8 @@ bool IsCertificateTrustedForPolicy(X509* cert, SecCertificateRef ref) {
+@@ -503,6 +510,8 @@ bool IsCertificateTrustedForPolicy(X509* cert, SecCertificateRef ref) {
bool trust_evaluated = false;
bool is_self_issued = isSelfIssued(cert);
@@ -52,7 +52,7 @@ index 74ea622dad..575cbb51ff 100644
// Evaluate user trust domain, then admin. User settings can override
// admin (and both override the system domain, but we don't check that).
for (const auto& trust_domain :
-@@ -536,6 +545,14 @@ bool IsCertificateTrustedForPolicy(X509* cert, SecCertificateRef ref) {
+@@ -541,6 +550,14 @@ bool IsCertificateTrustedForPolicy(X509* cert, SecCertificateRef ref) {
CFRelease(trust_settings);
}
}
diff --git a/patches/test-skip-adapt-parallel-and-sequential-tests-for-mo.patch b/patches/test-skip-adapt-parallel-and-sequential-tests-for-mo.patch
index ae8231074d..a049c2fad3 100644
--- a/patches/test-skip-adapt-parallel-and-sequential-tests-for-mo.patch
+++ b/patches/test-skip-adapt-parallel-and-sequential-tests-for-mo.patch
@@ -16,7 +16,8 @@ series itself.
Co-authored-by: Jaime Bernardo
Co-authored-by: Andre Staltz
---
- test/parallel/parallel.status | 1511 +++++++++++++++++
+ test/parallel/parallel.status | 1519 +++++++++++++++++
+ test/parallel/test-crypto-x509.js | 55 +-
test/parallel/test-dgram-bind-fd.js | 2 +-
test/parallel/test-dgram-membership.js | 10 +-
.../parallel/test-dgram-socket-buffer-size.js | 2 +-
@@ -55,13 +56,13 @@ Co-authored-by: Andre Staltz
test/sequential/sequential.status | 132 ++
test/sequential/test-async-wrap-getasyncid.js | 15 +-
test/sequential/test-fs-watch.js | 4 +
- 39 files changed, 1834 insertions(+), 51 deletions(-)
+ 40 files changed, 1876 insertions(+), 72 deletions(-)
diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status
-index d13efc8ac1..fccf6d71a2 100644
+index d13efc8ac1..61a2c70d1f 100644
--- a/test/parallel/parallel.status
+++ b/test/parallel/parallel.status
-@@ -160,6 +160,1517 @@ test-tls-set-default-ca-certificates-append-fetch: SKIP
+@@ -160,6 +160,1525 @@ test-tls-set-default-ca-certificates-append-fetch: SKIP
test-tls-set-default-ca-certificates-reset-fetch: SKIP
test-inspector-invalid-protocol: SKIP
@@ -119,6 +120,10 @@ index d13efc8ac1..fccf6d71a2 100644
+test-coverage-with-inspector-disabled: SKIP
+test-crypto-fips: SKIP
+test-crypto-provider-cache-snapshot: SKIP
++# Added by the v24.22.1 security rebase: the new snapshot regression builds
++# and runs snapshots through common/snapshot, which spawns child Node
++# executables (the same limitation as the entry above).
++test-crypto-random-cache-snapshot: SKIP
+test-crypto-secure-heap: SKIP
+test-cwd-enoent*: SKIP
+test-debug-usage: SKIP
@@ -790,6 +795,10 @@ index d13efc8ac1..fccf6d71a2 100644
+test-crypto-fips: SKIP
+test-crypto-no-algorithm: SKIP
+test-crypto-provider-cache-snapshot: SKIP
++# Added by the v24.22.1 security rebase: the new snapshot regression builds
++# and runs snapshots through common/snapshot, which spawns child Node
++# executables (the same limitation as the entry above).
++test-crypto-random-cache-snapshot: SKIP
+test-crypto-secure-heap: SKIP
+test-cwd-enoent*: SKIP
+test-debug-usage: SKIP
@@ -1579,6 +1588,72 @@ index d13efc8ac1..fccf6d71a2 100644
[$asan==on]
# https://github.com/nodejs/node/issues/39655
test-cluster-primary-error: PASS, FLAKY
+diff --git a/test/parallel/test-crypto-x509.js b/test/parallel/test-crypto-x509.js
+index 8e8ae2773d..b02fe84c48 100644
+--- a/test/parallel/test-crypto-x509.js
++++ b/test/parallel/test-crypto-x509.js
+@@ -498,27 +498,40 @@ UcXd/5qu2GhokrKU2cPttU+XAN2Om6a0
+ if (!isBoringSSL) {
+ const cert = new X509Certificate(certPem);
+
+- // Run the legacy conversion in a child so a native regression is reported
+- // as a signal rather than terminating the test runner.
+- const child = spawnSync(process.execPath, [
+- '-e',
+- `
+- 'use strict';
+- const assert = require('node:assert');
+- const { X509Certificate } = require('node:crypto');
+- const cert = new X509Certificate(process.argv[1]);
+- const legacy = cert.toLegacyObject();
+- assert.strictEqual(legacy.modulus, undefined);
+- assert.strictEqual(legacy.exponent, undefined);
+- assert.strictEqual(legacy.pubkey, undefined);
+- `,
+- '--',
+- certPem,
+- ], {
+- encoding: 'utf8',
+- });
+- assert.strictEqual(child.signal, null, child.stderr || child.stdout);
+- assert.strictEqual(child.status, 0, child.stderr || child.stdout);
++ if (common.isAndroid || common.isIOS) {
++ // nodejs-mobile patch: an app cannot spawn a child Node. Android's
++ // process.execPath is app_process, which aborts with "Error changing
++ // dalvik-cache ownership : Permission denied", and the iOS app has no
++ // executable to spawn either. Run the legacy conversion in-process and
++ // keep the same assertions; a native abort still fails the run through
++ // the crash verdict the harness records.
++ const legacy = cert.toLegacyObject();
++ assert.strictEqual(legacy.modulus, undefined);
++ assert.strictEqual(legacy.exponent, undefined);
++ assert.strictEqual(legacy.pubkey, undefined);
++ } else {
++ // Run the legacy conversion in a child so a native regression is
++ // reported as a signal rather than terminating the test runner.
++ const child = spawnSync(process.execPath, [
++ '-e',
++ `
++ 'use strict';
++ const assert = require('node:assert');
++ const { X509Certificate } = require('node:crypto');
++ const cert = new X509Certificate(process.argv[1]);
++ const legacy = cert.toLegacyObject();
++ assert.strictEqual(legacy.modulus, undefined);
++ assert.strictEqual(legacy.exponent, undefined);
++ assert.strictEqual(legacy.pubkey, undefined);
++ `,
++ '--',
++ certPem,
++ ], {
++ encoding: 'utf8',
++ });
++ assert.strictEqual(child.signal, null, child.stderr || child.stdout);
++ assert.strictEqual(child.status, 0, child.stderr || child.stdout);
++ }
+
+ assert.throws(() => cert.publicKey, {
+ message: hasOpenSSL(3) ? /decode error/ : /wrong tag/,
diff --git a/test/parallel/test-dgram-bind-fd.js b/test/parallel/test-dgram-bind-fd.js
index daf7f8064b..76c2ce82dd 100644
--- a/test/parallel/test-dgram-bind-fd.js
diff --git a/release-ready.txt b/release-ready.txt
index d65c6e6fbf..e69de29bb2 100644
--- a/release-ready.txt
+++ b/release-ready.txt
@@ -1 +0,0 @@
-24.22.0-0
diff --git a/release-upstream-snapshot.txt b/release-upstream-snapshot.txt
index ce77f73176..e69de29bb2 100644
--- a/release-upstream-snapshot.txt
+++ b/release-upstream-snapshot.txt
@@ -1 +0,0 @@
-a5d0eb95dd03b90986ad1a7c0cd3f2de1a0f383c
diff --git a/upstream-base.txt b/upstream-base.txt
index ef590b49ca..1b2d6c026e 100644
--- a/upstream-base.txt
+++ b/upstream-base.txt
@@ -1,4 +1,3 @@
-# Node.js 24.22.0 LTS release proposal; the official tag is not published yet.
-# https://github.com/nodejs/node/pull/66667 (v24.22.0-proposal)
-# Pin the reviewed PR commit rather than following the moving proposal branch.
-a5d0eb95dd03b90986ad1a7c0cd3f2de1a0f383c
+# Node.js 24.22.1 LTS security release.
+# https://github.com/nodejs/node/releases/tag/v24.22.1
+v24.22.1