@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.
- package/CHANGELOG.md +97 -0
- package/LatchwayReactNative.podspec +33 -0
- package/NOTICE +7 -0
- package/README.md +286 -3
- package/SECURITY.md +69 -0
- package/android/build.gradle.kts +62 -0
- package/android/consumer-rules.pro +2 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +9 -0
- package/android/gradle.properties +4 -0
- package/android/gradlew +251 -0
- package/android/gradlew.bat +94 -0
- package/android/settings.gradle.kts +33 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/dev/latchway/reactnative/LatchwayReactNativePackage.kt +25 -0
- package/android/src/main/java/dev/latchway/reactnative/NativeLatchwayModule.kt +964 -0
- package/android/src/test/java/dev/latchway/reactnative/NativeLatchwayModuleTest.kt +521 -0
- package/contract.lock +7 -0
- package/docs/architecture.md +145 -0
- package/docs/conformance.md +68 -0
- package/docs/native-installation.md +172 -0
- package/docs/physical-device-evidence.md +559 -0
- package/docs/releasing.md +360 -0
- package/docs/security.md +55 -0
- package/ios/LatchwayNativeBridge.swift +1551 -0
- package/ios/RCTNativeLatchway.h +9 -0
- package/ios/RCTNativeLatchway.mm +175 -0
- package/lib/client.d.ts +29 -0
- package/lib/client.d.ts.map +1 -0
- package/lib/client.js +939 -0
- package/lib/client.js.map +1 -0
- package/lib/component-client.d.ts +15 -0
- package/lib/component-client.d.ts.map +1 -0
- package/lib/component-client.js +141 -0
- package/lib/component-client.js.map +1 -0
- package/lib/config.d.ts +25 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +261 -0
- package/lib/config.js.map +1 -0
- package/lib/coordinator.d.ts +19 -0
- package/lib/coordinator.d.ts.map +1 -0
- package/lib/coordinator.js +167 -0
- package/lib/coordinator.js.map +1 -0
- package/lib/errors.d.ts +5 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +201 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +13 -0
- package/lib/index.js.map +1 -0
- package/lib/native/NativeLatchway.d.ts +25 -0
- package/lib/native/NativeLatchway.d.ts.map +1 -0
- package/lib/native/NativeLatchway.js +3 -0
- package/lib/native/NativeLatchway.js.map +1 -0
- package/lib/native/bridge.d.ts +5 -0
- package/lib/native/bridge.d.ts.map +1 -0
- package/lib/native/bridge.js +17 -0
- package/lib/native/bridge.js.map +1 -0
- package/lib/native-output.d.ts +3 -0
- package/lib/native-output.d.ts.map +1 -0
- package/lib/native-output.js +43 -0
- package/lib/native-output.js.map +1 -0
- package/lib/request-id.d.ts +2 -0
- package/lib/request-id.d.ts.map +1 -0
- package/lib/request-id.js +5 -0
- package/lib/request-id.js.map +1 -0
- package/lib/testing.d.ts +7 -0
- package/lib/testing.d.ts.map +1 -0
- package/lib/testing.js +9 -0
- package/lib/testing.js.map +1 -0
- package/lib/types.d.ts +200 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/lib/version.d.ts +8 -0
- package/lib/version.d.ts.map +1 -0
- package/lib/version.js +8 -0
- package/lib/version.js.map +1 -0
- package/package.json +130 -6
- package/react-native.config.cjs +7 -0
- package/release-compatibility.json +64 -0
- package/src/client.ts +1022 -0
- package/src/component-client.ts +158 -0
- package/src/config.ts +368 -0
- package/src/coordinator.ts +195 -0
- package/src/errors.ts +225 -0
- package/src/index.ts +53 -0
- package/src/native/NativeLatchway.ts +75 -0
- package/src/native/bridge.ts +23 -0
- package/src/native-output.ts +43 -0
- package/src/request-id.ts +5 -0
- package/src/testing.ts +11 -0
- package/src/types.ts +241 -0
- 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.
|