@latchway/react-native 0.0.0-bootstrap.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/LatchwayReactNative.podspec +33 -0
  3. package/NOTICE +7 -0
  4. package/README.md +286 -3
  5. package/SECURITY.md +69 -0
  6. package/android/build.gradle.kts +62 -0
  7. package/android/consumer-rules.pro +2 -0
  8. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  9. package/android/gradle/wrapper/gradle-wrapper.properties +9 -0
  10. package/android/gradle.properties +4 -0
  11. package/android/gradlew +251 -0
  12. package/android/gradlew.bat +94 -0
  13. package/android/settings.gradle.kts +33 -0
  14. package/android/src/main/AndroidManifest.xml +3 -0
  15. package/android/src/main/java/dev/latchway/reactnative/LatchwayReactNativePackage.kt +25 -0
  16. package/android/src/main/java/dev/latchway/reactnative/NativeLatchwayModule.kt +964 -0
  17. package/android/src/test/java/dev/latchway/reactnative/NativeLatchwayModuleTest.kt +521 -0
  18. package/contract.lock +7 -0
  19. package/docs/architecture.md +145 -0
  20. package/docs/conformance.md +68 -0
  21. package/docs/native-installation.md +172 -0
  22. package/docs/physical-device-evidence.md +559 -0
  23. package/docs/releasing.md +360 -0
  24. package/docs/security.md +55 -0
  25. package/ios/LatchwayNativeBridge.swift +1551 -0
  26. package/ios/RCTNativeLatchway.h +9 -0
  27. package/ios/RCTNativeLatchway.mm +175 -0
  28. package/lib/client.d.ts +29 -0
  29. package/lib/client.d.ts.map +1 -0
  30. package/lib/client.js +939 -0
  31. package/lib/client.js.map +1 -0
  32. package/lib/component-client.d.ts +15 -0
  33. package/lib/component-client.d.ts.map +1 -0
  34. package/lib/component-client.js +141 -0
  35. package/lib/component-client.js.map +1 -0
  36. package/lib/config.d.ts +25 -0
  37. package/lib/config.d.ts.map +1 -0
  38. package/lib/config.js +261 -0
  39. package/lib/config.js.map +1 -0
  40. package/lib/coordinator.d.ts +19 -0
  41. package/lib/coordinator.d.ts.map +1 -0
  42. package/lib/coordinator.js +167 -0
  43. package/lib/coordinator.js.map +1 -0
  44. package/lib/errors.d.ts +5 -0
  45. package/lib/errors.d.ts.map +1 -0
  46. package/lib/errors.js +201 -0
  47. package/lib/errors.js.map +1 -0
  48. package/lib/index.d.ts +8 -0
  49. package/lib/index.d.ts.map +1 -0
  50. package/lib/index.js +13 -0
  51. package/lib/index.js.map +1 -0
  52. package/lib/native/NativeLatchway.d.ts +25 -0
  53. package/lib/native/NativeLatchway.d.ts.map +1 -0
  54. package/lib/native/NativeLatchway.js +3 -0
  55. package/lib/native/NativeLatchway.js.map +1 -0
  56. package/lib/native/bridge.d.ts +5 -0
  57. package/lib/native/bridge.d.ts.map +1 -0
  58. package/lib/native/bridge.js +17 -0
  59. package/lib/native/bridge.js.map +1 -0
  60. package/lib/native-output.d.ts +3 -0
  61. package/lib/native-output.d.ts.map +1 -0
  62. package/lib/native-output.js +43 -0
  63. package/lib/native-output.js.map +1 -0
  64. package/lib/request-id.d.ts +2 -0
  65. package/lib/request-id.d.ts.map +1 -0
  66. package/lib/request-id.js +5 -0
  67. package/lib/request-id.js.map +1 -0
  68. package/lib/testing.d.ts +7 -0
  69. package/lib/testing.d.ts.map +1 -0
  70. package/lib/testing.js +9 -0
  71. package/lib/testing.js.map +1 -0
  72. package/lib/types.d.ts +200 -0
  73. package/lib/types.d.ts.map +1 -0
  74. package/lib/types.js +2 -0
  75. package/lib/types.js.map +1 -0
  76. package/lib/version.d.ts +8 -0
  77. package/lib/version.d.ts.map +1 -0
  78. package/lib/version.js +8 -0
  79. package/lib/version.js.map +1 -0
  80. package/package.json +130 -6
  81. package/react-native.config.cjs +7 -0
  82. package/release-compatibility.json +64 -0
  83. package/src/client.ts +1022 -0
  84. package/src/component-client.ts +158 -0
  85. package/src/config.ts +368 -0
  86. package/src/coordinator.ts +195 -0
  87. package/src/errors.ts +225 -0
  88. package/src/index.ts +53 -0
  89. package/src/native/NativeLatchway.ts +75 -0
  90. package/src/native/bridge.ts +23 -0
  91. package/src/native-output.ts +43 -0
  92. package/src/request-id.ts +5 -0
  93. package/src/testing.ts +11 -0
  94. package/src/types.ts +241 -0
  95. package/src/version.ts +7 -0
@@ -0,0 +1,68 @@
1
+ # Conformance
2
+
3
+ Node conformance validates JavaScript request shaping with the explicit testing bridge, malicious credential-field output rejection, native response pull/backpressure, strict-CSP safety, redaction, cancellation, version mismatch, cross-instance coordination, and canonical contract fixtures. It does not emulate hardware trust or assert that a production native transport attached a valid proof.
4
+
5
+ The framework suite under `Conformance/framework` uses 16 applicable stable
6
+ `FW-*` case IDs from the JavaScript SDK suite and binds them to the canonical
7
+ `react-native-fetch` registry entry. It executes, rather than merely imports,
8
+ the exact OpenAI 7.8.0, Vercel AI 7.0.85, and LangChain OpenAI 1.5.10 consumer
9
+ paths. RN-specific `RN-FW-ANTHROPIC-001` separately runs
10
+ `@ai-sdk/anthropic` 4.0.46 against `/v1/messages`, without claiming a canonical
11
+ Anthropic framework registry entry. The official `@anthropic-ai/sdk` 0.120.0
12
+ client is recorded as unsupported because its credential-chain Node filesystem
13
+ imports fail an actual Metro bundle. The deterministic native-boundary fixture
14
+ returns protocol-shaped Responses, Chat Completions, Anthropic Messages,
15
+ embeddings, SSE usage, tool calls, structured output, quota failures, provider
16
+ failures, and retry responses. Tests prove feature binding, safe headers,
17
+ request-ID correlation, cancellation, fresh native operation IDs for framework
18
+ retries, placeholder stripping, exact-origin refusal, and isolation from global
19
+ fetch. The runnable consumer factory requires distinct Responses, Chat,
20
+ Embeddings, and Anthropic feature identifiers because one active Latchway
21
+ feature is bound to exactly one gateway protocol.
22
+
23
+ `RN-FW-REFRESH-001` separately proves the public explicit-refresh operation
24
+ followed by a framework request. It is intentionally not reported as shared
25
+ `FW-BEH-006`: automatic recovery from a pre-dispatch expired native session is
26
+ owned by the platform SDK and requires native/device evidence.
27
+
28
+ `RN-FW-OPAQUE-001` proves the bounded `/proxy/{feature}/...` pathway keeps its
29
+ feature binding through native dispatch. None of the RN-only cases extends the
30
+ shared `FW-*` registry set.
31
+
32
+ This suite deliberately does not manufacture DPoP proofs, refresh credentials,
33
+ or platform attestation in JavaScript. Fresh-proof generation and native
34
+ pre-dispatch retry classification remain native SDK/device evidence. The local
35
+ framework result therefore supports the registry's `experimental` state; it is
36
+ not hosted or physical-device release evidence.
37
+
38
+ Real platform conformance requires the exact core image and real provider configuration:
39
+
40
+ - iOS: App Attest development and production applications on physical devices; session creation, assertion reuse, nonce, refresh rotation, quota, streaming, diagnostics, and revocation.
41
+ - Android: Play-distributed application with Play Integrity standard requests; hardware/StrongBox policy variants, nonce, refresh rotation, quota, streaming, diagnostics, and revocation.
42
+ - Both: identity token expiry/reauthentication, installation platform validation, no secret material in errors/diagnostics, and published native dependency resolution.
43
+
44
+ Provider credentials and signing material belong in protected CI/device infrastructure and are never committed. Missing provider credentials block device conformance only; they do not justify fake evidence or trusted identity fields.
45
+
46
+ The production TurboModule intentionally cannot expose or mutate DPoP/session
47
+ credentials for a JavaScript evidence harness. Replay, proof tamper, session
48
+ credential rotation, and protocol-header mutation therefore have to be taken
49
+ from the separately linked native SDK physical-device reports. The v2 raw
50
+ React Native record omits those native-only claims. Its protected finalizer
51
+ validates the hash-pinned platform-native report, imports only the five exact
52
+ allowlisted proof objects, and rejects missing, failed, renamed, extended, or
53
+ coordinate-substituted proofs. The React Native report remains ineligible
54
+ unless all of its own runtime checks pass (eight on iOS, including direct
55
+ assertion reuse; seven on Android) and every imported proof passes.
56
+
57
+ The repository's native source jobs build official React Native 0.82 iOS and
58
+ Android hosts against the exact locked SDK source releases. The published
59
+ dependency jobs repeat those builds with local CocoaPods paths and Maven
60
+ repositories removed. Neither is physical-device attestation evidence.
61
+
62
+ Before v1 is called device-conformant, retain redacted run evidence for each
63
+ platform/provider/environment combination covering installation creation,
64
+ session reuse, identity expiry and reauthentication, nonce recovery, refresh
65
+ rotation, quota, an SSE stream, diagnostics, revocation, and a post-revocation
66
+ re-enrollment. The evidence must name the application release, device/OS,
67
+ gateway image digest, contract bundle hash, SDK package versions, and request
68
+ IDs without including tokens, proofs, keys, or raw attestations.
@@ -0,0 +1,172 @@
1
+ # Native installation
2
+
3
+ ## iOS
4
+
5
+ Configure `apple.rootKeychainAccessGroup` with the fully resolved private app-ID
6
+ group that appears first in the signed root target. List every explicit
7
+ extension-shared group in `apple.legacySharedKeychainAccessGroups`; the native
8
+ SDK scans only exact root-record coordinates in those groups. Missing,
9
+ wildcard, duplicate, or root-equal groups fail closed, and stale root records
10
+ require an explicit migration.
11
+
12
+ The podspec pins `Latchway/AppAttest` 1.0.0 and React Native codegen dependencies. Run CocoaPods from the host application after installing the npm package. Enable App Attest for the application identifier and use a real device for conformance; simulators report attestation unsupported.
13
+
14
+ The Firebase Authentication example pins React Native Firebase 25.1.0 and
15
+ Firebase Apple SDK 12.15.0 and uses CocoaPods static frameworks. The Latchway
16
+ package itself does not depend on Firebase. Firebase has announced that the
17
+ existing CocoaPods releases remain installable but new Firebase Apple SDK
18
+ versions stop shipping through CocoaPods after October 2026; migrate the
19
+ example to the compatible React Native Firebase SPM path only after its pinned
20
+ RN 0.82 native host build is green.
21
+
22
+ For a source-development run on a physical iPhone or iPad, the example offers a
23
+ separate opt-in Debug bootstrap. `scripts/copy-development-firebase-ios-config.sh`
24
+ validates an external, bundle-matched Firebase plist and copies it only into a
25
+ Debug `iphoneos` build; `scripts/run-development-react-native-ios.sh` keeps the
26
+ custom token and digest out of an allowlisted Xcode build environment, validates
27
+ the complete non-secret deployment coordinates, rechecks grant freshness, and
28
+ force-bundles the exact JavaScript checkout before handing the grant to one
29
+ no-debugger launch. The physical-device run therefore does not require Metro or
30
+ Local Network access, although iOS can still show React Native's one-time Debug
31
+ permission sheet on the first install. Later runs update the existing app so
32
+ that OS consent persists. The app signs in with a new grant, revokes the old
33
+ descriptor-bound family, verifies the root Responses/quota/diagnostics path,
34
+ and prepares the App Intent descriptor. After the one-use grant has been
35
+ destroyed, the root publishes a nonsecret exact-run shared-Keychain challenge
36
+ immediately before the waiting marker. The separately launched Debug App Intent
37
+ captures that challenge before constructing its client, refreshes an
38
+ independently keyed delegated session, and fully consumes one successful bounded
39
+ Responses body. It rechecks the challenge immediately before echoing the run in
40
+ a bounded shared-Keychain receipt. The containing app accepts only its
41
+ native-captured exact run, deletes both artifacts, retires that exact
42
+ descriptor-bound family, and signs out. The runner passes only after retrieving
43
+ the exact random-run
44
+ marker written after terminal cleanup. Its bounded post-wait abort path also
45
+ relaunches the containing app to finish and verify family retirement/sign-out
46
+ after interruption or timeout, deleting both challenge and receipt. The Debug
47
+ native module and marker writer are
48
+ absent from Release. This path verifies local integration only and cannot
49
+ satisfy the protected physical-evidence gate. See the example README for the
50
+ exact runner workflow and possible Shortcuts tap.
51
+
52
+ Firebase Authentication and Firebase App Check are distinct. The checked-in
53
+ example pins Firebase App/Auth but does not install the native App Check module,
54
+ and a Firebase web App Check registration does not apply to an iOS application.
55
+ When App Check enforcement is enabled for the Firebase resource, pin a
56
+ compatible React Native Firebase App Check dependency and activate the Apple
57
+ App Attest provider before the Auth exchange. A debug App Check provider/token
58
+ is never acceptable in a protected Release candidate.
59
+
60
+ A production App Attest run requires all of the following, none of which can be
61
+ substituted by a simulator build:
62
+
63
+ - an App Attest-capable physical iPhone or iPad;
64
+ - a registered App ID with the App Attest capability, a matching Team ID and
65
+ bundle ID, and a provisioning profile containing the entitlement;
66
+ - `development` or `production` selected consistently in the entitlement,
67
+ React Native client configuration, gateway application record, and Apple
68
+ verification policy; and
69
+ - a real application identity token plus the exact gateway/core release named
70
+ by the synchronized contract lock.
71
+
72
+ The root-application bridge constructs the App Attest provider with the exact
73
+ `rootKeychainAccessGroup`, `legacySharedKeychainAccessGroups`, and
74
+ `.reactNativeIOS` runtime, then passes the same groups to
75
+ `LatchwayConfiguration`. Keychain, Secure Enclave, session, and accepted App
76
+ Attest key state are runtime-isolated. An extension bridge constructs no App
77
+ Attest provider: iOS app extensions cannot call
78
+ `DCAppAttestService.generateKey`, so extension sessions remain independently
79
+ keyed and delegated from the already attested root application.
80
+
81
+ The checked-in App Intents target has two intentionally different build
82
+ boundaries. In Debug, its own CocoaPods target links `Latchway/AppExtensions`
83
+ and the native Swift intent performs the local delegated-request proof without
84
+ hosting a React Native JavaScript runtime. In Release, that dependency is not
85
+ linked, no executable Latchway client path is compiled, and the archive/signing
86
+ fixture's intent fails closed. The CocoaPods subspec is imported through module
87
+ `Latchway`, not the SwiftPM-only module name.
88
+
89
+ Candidate production requires a distinct child bundle ID and provisioning
90
+ profile. The signed root target lists its private app-ID Keychain group first
91
+ and the shared component group second; the first position keeps implicit root
92
+ Keychain writes private. The signed extension lists only the shared group and
93
+ therefore cannot read root-private key, credential, identity, or session state.
94
+ Each provisioning profile must authorize every group its target signs, either
95
+ exactly or with a well-formed terminal wildcard. The extension must not carry
96
+ App Attest. The Debug intent constructs its delegated client with
97
+ `.reactNativeIOS`; the gateway component definition must therefore use platform
98
+ `react_native_ios`, kind `app_intent_extension`, delegated-only trust, and the
99
+ same requested feature as the descriptor prepared by the root.
100
+
101
+ The root JavaScript API owns the descriptor lifecycle:
102
+
103
+ - `prepareComponents` provisions one or more exact descriptors;
104
+ - `replaceComponent` rotates/replaces one exact descriptor;
105
+ - root-side `componentDiagnostics` reads redacted local state without acquiring
106
+ application identity;
107
+ - `revokeComponent` retires one descriptor; and
108
+ - no-argument `revokeCurrentInstallationFamily()` retires the root plus every
109
+ component in the native iOS SDK's durable root-private descriptor registry;
110
+ the optional descriptor list additionally covers pre-registry legacy state.
111
+
112
+ Descriptors are normalized and snapshotted before asynchronous identity work.
113
+ The native SDK registers only their public Keychain coordinates before it can
114
+ create component-local state. Successful cleanup removes a coordinate, while a
115
+ failed Keychain erasure keeps it durable for retry after a later app launch.
116
+ Preparation, replacement, and returned diagnostics are checked against that
117
+ same snapshot, and serialized multi-component input larger than 65,536 bytes is
118
+ rejected in JavaScript before crossing the native bridge. Component keys,
119
+ grants, delegated sessions, and the root identity never cross into JavaScript.
120
+
121
+ For local native SDK work, declare the sibling `Latchway.podspec` by path in the
122
+ appropriate host targets. The root path declaration lets the autolinked React
123
+ Native pod resolve its App Attest dependency from that source, while the
124
+ extension subspec is Debug-only:
125
+
126
+ ```ruby
127
+ target "ContainingApp" do
128
+ pod "Latchway", :path => "../../../latchway-ios-sdk"
129
+ # use_native_modules! / use_react_native! follows here.
130
+ end
131
+
132
+ target "AppIntents" do
133
+ pod "Latchway/AppExtensions", :path => "../../../latchway-ios-sdk",
134
+ :configurations => ["Debug"]
135
+ end
136
+ ```
137
+
138
+ Local paths belong only in the host Podfile. They are absent from the published React Native podspec.
139
+
140
+ ## Android
141
+
142
+ The library pins the native 1.0.0 Maven coordinates. Those AARs publish Kotlin
143
+ 2.3 metadata and require compile SDK 37; the React Native 0.82 host therefore
144
+ pins Kotlin 2.3.21 while retaining RN's supported consumer AGP 8.12 baseline.
145
+ Play Integrity requires the decimal Google Cloud project number in
146
+ `android.playIntegrityCloudProjectNumber`; JavaScript never receives or
147
+ supplies the resulting integrity token.
148
+
149
+ A production Play Integrity run requires a physical device and a build whose
150
+ package name and signing-certificate digest match the gateway application
151
+ record. Configure the Play Integrity API and its decimal Google Cloud project
152
+ number, upload the signed build to a Play internal/closed/production track, and
153
+ install it from Google Play. A locally sideloaded debug APK can prove compilation
154
+ and bridge behavior, but it is not production Play Integrity evidence. Exercise
155
+ both hardware-backed/StrongBox-available and fallback policy variants on the
156
+ device matrix required by the deployment.
157
+
158
+ For local native SDK work, publish the native artifacts to a disposable Maven repository and set one of:
159
+
160
+ ```sh
161
+ ./android/gradlew -p android -PlatchwayNativeRepository=/absolute/path/to/maven check
162
+ ```
163
+
164
+ ```sh
165
+ LATCHWAY_NATIVE_REPOSITORY=/absolute/path/to/maven ./android/gradlew -p android check
166
+ ```
167
+
168
+ The repository is content-filtered to `dev.latchway`. Composite-build substitution is intentionally avoided because the Android SDK and React Native 0.82 currently use incompatible Gradle major versions.
169
+
170
+ ## Release dependency check
171
+
172
+ CI must run once against only the published CocoaPods/Maven coordinates, without local path or repository overrides, before npm publication. A local native build proving source compatibility does not replace that consumer check. The manual `Published dependency consumer` workflow performs that gate; the promotion-dispatched release workflow repeats it before npm publication.