@burnt-labs/expo-satya-attest 0.1.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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/PROVIDER_REGISTRY.md +271 -0
  3. package/PUBLISHING.md +291 -0
  4. package/README.md +374 -0
  5. package/android/build.gradle +37 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/expo/modules/satyaattest/SatyaAttestModule.kt +620 -0
  8. package/android/src/main/java/uniffi/satya_ffi/satya_ffi.kt +1423 -0
  9. package/android/src/main/jniLibs/arm64-v8a/libsatya_ffi.so +0 -0
  10. package/android/src/main/jniLibs/armeabi-v7a/libsatya_ffi.so +0 -0
  11. package/android/src/main/jniLibs/x86_64/libsatya_ffi.so +0 -0
  12. package/app.plugin.js +131 -0
  13. package/build/SatyaAttest.types.d.ts +200 -0
  14. package/build/SatyaAttest.types.d.ts.map +1 -0
  15. package/build/SatyaAttest.types.js +10 -0
  16. package/build/SatyaAttest.types.js.map +1 -0
  17. package/build/SatyaAttestModule.d.ts +29 -0
  18. package/build/SatyaAttestModule.d.ts.map +1 -0
  19. package/build/SatyaAttestModule.js +3 -0
  20. package/build/SatyaAttestModule.js.map +1 -0
  21. package/build/SatyaAttestModule.web.d.ts +14 -0
  22. package/build/SatyaAttestModule.web.d.ts.map +1 -0
  23. package/build/SatyaAttestModule.web.js +28 -0
  24. package/build/SatyaAttestModule.web.js.map +1 -0
  25. package/build/SatyaMobileClient.d.ts +37 -0
  26. package/build/SatyaMobileClient.d.ts.map +1 -0
  27. package/build/SatyaMobileClient.js +82 -0
  28. package/build/SatyaMobileClient.js.map +1 -0
  29. package/build/SatyaProviderAttestButton.d.ts +99 -0
  30. package/build/SatyaProviderAttestButton.d.ts.map +1 -0
  31. package/build/SatyaProviderAttestButton.js +801 -0
  32. package/build/SatyaProviderAttestButton.js.map +1 -0
  33. package/build/index.d.ts +13 -0
  34. package/build/index.d.ts.map +1 -0
  35. package/build/index.js +16 -0
  36. package/build/index.js.map +1 -0
  37. package/build/providerPolicy.d.ts +10 -0
  38. package/build/providerPolicy.d.ts.map +1 -0
  39. package/build/providerPolicy.js +81 -0
  40. package/build/providerPolicy.js.map +1 -0
  41. package/build/providerRegistry.d.ts +17 -0
  42. package/build/providerRegistry.d.ts.map +1 -0
  43. package/build/providerRegistry.js +522 -0
  44. package/build/providerRegistry.js.map +1 -0
  45. package/build/providerWebview.d.ts +37 -0
  46. package/build/providerWebview.d.ts.map +1 -0
  47. package/build/providerWebview.js +524 -0
  48. package/build/providerWebview.js.map +1 -0
  49. package/build/providers.json +981 -0
  50. package/expo-module.config.json +9 -0
  51. package/ios/Frameworks/libsatya_ffi-rs.xcframework/Info.plist +29 -0
  52. package/ios/Frameworks/libsatya_ffi-rs.xcframework/ios-arm64/Headers/module.modulemap +7 -0
  53. package/ios/Frameworks/libsatya_ffi-rs.xcframework/ios-arm64/Headers/satya_ffi.swift +989 -0
  54. package/ios/Frameworks/libsatya_ffi-rs.xcframework/ios-arm64/Headers/satya_ffiFFI.h +585 -0
  55. package/ios/Frameworks/libsatya_ffi-rs.xcframework/ios-arm64/libsatya_ffi.a +0 -0
  56. package/ios/Generated/satya_ffi.swift +989 -0
  57. package/ios/SatyaAttest.podspec +27 -0
  58. package/ios/SatyaAttestModule.swift +932 -0
  59. package/package.json +83 -0
  60. package/scripts/build-rust-android.sh +51 -0
  61. package/scripts/build-rust-ios.sh +129 -0
  62. package/src/SatyaAttest.types.ts +239 -0
  63. package/src/SatyaAttestModule.ts +36 -0
  64. package/src/SatyaAttestModule.web.ts +36 -0
  65. package/src/SatyaMobileClient.ts +122 -0
  66. package/src/SatyaProviderAttestButton.tsx +1142 -0
  67. package/src/index.ts +48 -0
  68. package/src/providerPolicy.ts +122 -0
  69. package/src/providerRegistry.ts +683 -0
  70. package/src/providerWebview.ts +548 -0
  71. package/src/providers.json +981 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,271 @@
1
+ # Provider Registry
2
+
3
+ `@burnt-labs/expo-satya-attest` ships with a built-in provider registry at
4
+ [`src/providers.json`](./src/providers.json). Apps can also supply their own registry
5
+ when they want to keep provider templates outside the SDK package.
6
+
7
+ Use a custom registry when:
8
+
9
+ - your app needs providers that are not in the SDK package,
10
+ - your app wants to pin a private allow-list,
11
+ - your team wants to update provider templates through its own release process.
12
+
13
+ ## Loading A Custom Registry
14
+
15
+ For a hosted registry:
16
+
17
+ ```tsx
18
+ import { SatyaProviderAttestButton } from '@burnt-labs/expo-satya-attest';
19
+
20
+ export function PayrollProof() {
21
+ return (
22
+ <SatyaProviderAttestButton
23
+ providerId="acme.payroll.v1"
24
+ providerRegistrySource={{
25
+ url: 'https://example.com/.well-known/satya/providers.json',
26
+ timeoutMs: 10000,
27
+ maxBytes: 524288,
28
+ }}
29
+ providerPolicyRefresh="verifier-required"
30
+ transportConfig={{
31
+ notaryUrl: 'https://verifier.example.com',
32
+ cloudProjectNumber: 0,
33
+ }}
34
+ verifyBackend
35
+ buttonLabel="Open payroll"
36
+ />
37
+ );
38
+ }
39
+ ```
40
+
41
+ For an in-app registry object:
42
+
43
+ ```tsx
44
+ import { SatyaProviderAttestButton, type SatyaProviderRegistry } from '@burnt-labs/expo-satya-attest';
45
+
46
+ const registry: SatyaProviderRegistry = {
47
+ schemaVersion: '0.1.0',
48
+ providers: [
49
+ // provider templates
50
+ ],
51
+ };
52
+
53
+ <SatyaProviderAttestButton
54
+ providerId="acme.payroll.v1"
55
+ providerRegistrySource={registry}
56
+ providerPolicyRefresh="verifier-required"
57
+ transportConfig={{ notaryUrl: 'https://verifier.example.com' }}
58
+ />;
59
+ ```
60
+
61
+ To populate your own selector UI:
62
+
63
+ ```tsx
64
+ import { listProviderRefs, loadProviderRegistry } from '@burnt-labs/expo-satya-attest';
65
+
66
+ const registry = await loadProviderRegistry({
67
+ url: 'https://example.com/.well-known/satya/providers.json',
68
+ });
69
+ const providers = listProviderRefs(registry);
70
+ ```
71
+
72
+ `providerTemplate` still works for one-off integrations and wins over
73
+ `providerRegistrySource` when both are supplied.
74
+
75
+ ## Registry URL Rules
76
+
77
+ Hosted registries are fetched with `GET` and must use HTTPS by default.
78
+
79
+ ```ts
80
+ providerRegistrySource={{
81
+ url: 'https://example.com/.well-known/satya/providers.json',
82
+ headers: { Authorization: 'Bearer ...' },
83
+ timeoutMs: 10000,
84
+ maxBytes: 524288
85
+ }}
86
+ ```
87
+
88
+ Security rules enforced by the SDK:
89
+
90
+ - `https://` is required by default.
91
+ - URL credentials such as `https://user:pass@example.com/...` are rejected.
92
+ - Request headers cannot contain newline characters.
93
+ - Fetches are timeout bounded.
94
+ - Response bodies are size bounded.
95
+ - The JSON is validated and cloned before use.
96
+
97
+ `allowInsecureHttp: true` exists only for local development:
98
+
99
+ ```ts
100
+ providerRegistrySource={{
101
+ url: 'http://127.0.0.1:8080/providers.json',
102
+ allowInsecureHttp: true
103
+ }}
104
+ ```
105
+
106
+ Do not use insecure HTTP for production registries. A registry controls which provider
107
+ host, template hash, parser hash, request match rules, and reveal rules the SDK uses.
108
+
109
+ ## File Format
110
+
111
+ Top-level shape:
112
+
113
+ ```json
114
+ {
115
+ "schemaVersion": "0.1.0",
116
+ "providers": []
117
+ }
118
+ ```
119
+
120
+ Provider template shape:
121
+
122
+ ```json
123
+ {
124
+ "schemaVersion": "0.1.0",
125
+ "providerId": "acme.payroll.v1",
126
+ "label": "Acme Payroll",
127
+ "description": "Current employee payroll proof",
128
+ "displayName": "Acme Payroll",
129
+ "version": "v1",
130
+ "target": {
131
+ "scheme": "https",
132
+ "host": "payroll.example.com",
133
+ "port": 443
134
+ },
135
+ "webLogin": {
136
+ "startUrl": "https://payroll.example.com/login",
137
+ "callbackUrl": "https://payroll.example.com/",
138
+ "oauthHosts": ["payroll.example.com"]
139
+ },
140
+ "userAgent": {
141
+ "ios": null,
142
+ "android": null
143
+ },
144
+ "proofTarget": {
145
+ "method": "GET",
146
+ "url": "https://payroll.example.com/api/current-user",
147
+ "requestMatchRules": [
148
+ {
149
+ "type": "methodEquals",
150
+ "pattern": "GET"
151
+ },
152
+ {
153
+ "type": "urlContains",
154
+ "pattern": "/api/current-user"
155
+ }
156
+ ],
157
+ "revealRules": [
158
+ {
159
+ "action": "REVEAL",
160
+ "direction": "RECV",
161
+ "part": "BODY",
162
+ "params": {
163
+ "type": "json",
164
+ "path": "$.employeeId"
165
+ }
166
+ }
167
+ ],
168
+ "maxSentData": 4096,
169
+ "maxRecvData": 131072
170
+ },
171
+ "privacyPolicy": {
172
+ "allowedRequestHeaders": ["accept", "cookie", "content-type", "user-agent"],
173
+ "allowedResponseBodyJsonPaths": ["$.employeeId"]
174
+ },
175
+ "tlsPolicy": {
176
+ "requireTls13": true,
177
+ "allowZeroRtt": false,
178
+ "allowResumption": false,
179
+ "allowKeyUpdate": false,
180
+ "preferredCipher": "TLS_CHACHA20_POLY1305_SHA256",
181
+ "maxSentBytes": 4096,
182
+ "maxRecvBytes": 131072
183
+ },
184
+ "tlsIdentityPolicy": {
185
+ "expectedSni": "payroll.example.com",
186
+ "allowedAlpn": ["h2", "http/1.1"],
187
+ "spkiSha256Hex": null,
188
+ "spkiSha256B64Pins": ["KWQsjul7mJDk+i6fAisGLhWLd43FUs5ayVmkz5Y54DU="],
189
+ "tlsTrustStorePolicy": "web-pki-os-roots-no-user-roots",
190
+ "userRootsAllowed": false,
191
+ "redirectPolicyId": "no-cross-origin-redirects-v1"
192
+ },
193
+ "templateGovernance": {
194
+ "templateHash": "prod-acme-payroll-v1-template",
195
+ "parserHash": "prod-acme-payroll-v1-parser",
196
+ "fixtureSetHash": "prod-acme-payroll-v1-fixtures",
197
+ "governancePolicyVersion": "prod-governance-v1",
198
+ "providerActivationState": "active"
199
+ },
200
+ "freshness": {
201
+ "mode": "notary_nonce",
202
+ "echoCapable": false,
203
+ "injection": null,
204
+ "reflectionPath": null,
205
+ "cacheKeyAffecting": true,
206
+ "semanticallyInert": true
207
+ },
208
+ "credentialCustody": {
209
+ "model": "webview_session_cookie",
210
+ "allowImportedCookies": false,
211
+ "accountBindingRequired": true
212
+ }
213
+ }
214
+ ```
215
+
216
+ ## Validation Rules
217
+
218
+ The SDK rejects custom registries unless each provider satisfies these checks:
219
+
220
+ - provider ids are unique and non-empty,
221
+ - `target.scheme` is `https`,
222
+ - `target.port` is `443`,
223
+ - `proofTarget.url` is HTTPS,
224
+ - `proofTarget.url` host matches `target.host`,
225
+ - `proofTarget.url` host matches `tlsIdentityPolicy.expectedSni`,
226
+ - `tlsIdentityPolicy.tlsTrustStorePolicy` is `web-pki-os-roots-no-user-roots`,
227
+ - `tlsIdentityPolicy.userRootsAllowed` is `false`,
228
+ - SPKI pins are valid SHA-256 base64 strings when present,
229
+ - `templateGovernance.templateHash` and `parserHash` are non-empty,
230
+ - `privacyPolicy.allowedRequestHeaders` is non-empty,
231
+ - `privacyPolicy.allowedResponseBodyJsonPaths` is non-empty,
232
+ - `proofTarget.requestMatchRules` includes a `methodEquals` rule matching
233
+ `proofTarget.method`,
234
+ - `proofTarget.requestMatchRules` includes a `urlContains` rule matching the
235
+ `proofTarget.url` path or query.
236
+
237
+ The SDK validates the registry before opening the WebView and the native iOS/Android
238
+ modules repeat critical checks before replay.
239
+
240
+ ## Security Review Notes
241
+
242
+ Threat model: a provider registry controls the login URL, request matching rules, replay
243
+ URL, trusted SNI, template hash, parser hash, reveal rules, and optional SPKI pins. Treat
244
+ it as trusted policy, not as ordinary app content.
245
+
246
+ Confirmed protections in the SDK:
247
+
248
+ - Remote registries are HTTPS-only by default, with timeout and size limits.
249
+ - Registry URLs with embedded credentials are rejected.
250
+ - Registry request headers are sanitized for newline injection.
251
+ - Templates are validated and cloned before use.
252
+ - Custom templates cannot enable user-installed roots.
253
+ - Replay URL, target host, and expected SNI must agree.
254
+ - Request matching must bind both method and URL.
255
+ - Verifier SPKI policy is still checked against provider id, host, template hash, and
256
+ parser hash before those pins are used.
257
+ - Backend verification independently re-derives trusted template and pin policy.
258
+
259
+ Residual risks and required operating practices:
260
+
261
+ - A malicious host app can intentionally pass a malicious registry or template. The SDK is
262
+ not a trust boundary against the application that embeds it.
263
+ - A compromised registry host can change templates. Production backends must reject
264
+ unknown `templateHash` / `parserHash` values and should use
265
+ `providerPolicyRefresh="verifier-required"`.
266
+ - Do not put secrets in registry URLs. Use short-lived headers if a private registry needs
267
+ authentication.
268
+ - Do not use `allowInsecureHttp` outside local development.
269
+ - Keep verifier provider policy in sync with custom registry templates. The client-side
270
+ registry protects replay setup; the verifier policy decides whether an artifact is
271
+ acceptable.
package/PUBLISHING.md ADDED
@@ -0,0 +1,291 @@
1
+ # Publishing `@burnt-labs/expo-satya-attest`
2
+
3
+ This SDK should publish as an npm package. Expo and React Native apps install native
4
+ modules from npm, then CocoaPods and Gradle consume the iOS/Android native code from
5
+ `node_modules`. You do not need a separate CocoaPods trunk package or Maven package for
6
+ the current layout.
7
+
8
+ Use GitHub Releases only as an optional companion for binary provenance, checksums, or
9
+ very large native artifacts. The npm tarball still needs the native artifacts unless the
10
+ package is redesigned to download or build them during install, which is not recommended
11
+ for a plug-and-play Expo SDK.
12
+
13
+ ## Package Name
14
+
15
+ Use the scoped public package owned by the Burnt npm organization:
16
+
17
+ ```bash
18
+ @burnt-labs/expo-satya-attest
19
+ ```
20
+
21
+ Check before release:
22
+
23
+ ```bash
24
+ npm view @burnt-labs/expo-satya-attest version
25
+ ```
26
+
27
+ If npm returns `E404`, the package has not been published yet. The `NPM_TOKEN` used by
28
+ GitHub Actions must belong to an npm account or automation token with publish rights for
29
+ the `@burnt-labs` scope. Scoped public publishes must use `npm publish --access public`.
30
+
31
+ ## Native Artifacts That Must Ship
32
+
33
+ The SDK wraps Rust through UniFFI, so the npm package must include prebuilt native
34
+ artifacts. Consumers should not have to install Rust, Android NDK, cargo-ndk, or Xcode
35
+ toolchains just to install the SDK.
36
+
37
+ Required Android files:
38
+
39
+ ```text
40
+ android/src/main/java/uniffi/satya_ffi/satya_ffi.kt
41
+ android/src/main/jniLibs/arm64-v8a/libsatya_ffi.so
42
+ android/src/main/jniLibs/armeabi-v7a/libsatya_ffi.so
43
+ android/src/main/jniLibs/x86_64/libsatya_ffi.so
44
+ ```
45
+
46
+ Required iOS files:
47
+
48
+ ```text
49
+ ios/Generated/satya_ffi.swift
50
+ ios/Frameworks/libsatya_ffi-rs.xcframework/
51
+ ```
52
+
53
+ These outputs are gitignored because they are generated build products. A release job
54
+ must build them before `npm publish`, or the release manager must build them locally
55
+ before publishing.
56
+
57
+ Build them from the module directory:
58
+
59
+ ```bash
60
+ cd platform_sdks/expo-satya-attest
61
+ npm run build:native:android
62
+ npm run build:native:ios
63
+ ```
64
+
65
+ iOS artifacts can only be built on macOS with Xcode command line tools. Android artifacts
66
+ need the Android NDK, JDK 17, `cargo-ndk`, and Rust Android targets.
67
+
68
+ The iOS build strips local/debug symbols from the static archives by default. For a
69
+ diagnostic unstripped iOS build:
70
+
71
+ ```bash
72
+ SATYA_IOS_STRIP=0 npm run build:native:ios
73
+ ```
74
+
75
+ By default, the iOS release build includes only the physical-device `arm64` slice. That
76
+ is intentional: the real App Attest flow does not work on the iOS simulator, and shipping
77
+ simulator slices makes the npm package much larger without helping the production claim
78
+ path.
79
+
80
+ ## Release Checks
81
+
82
+ Install dependencies:
83
+
84
+ ```bash
85
+ cd platform_sdks/expo-satya-attest
86
+ npm install
87
+ ```
88
+
89
+ Run the full release gate:
90
+
91
+ ```bash
92
+ npm run release:check
93
+ ```
94
+
95
+ This runs:
96
+
97
+ ```bash
98
+ npm run check:metadata
99
+ npm run clean
100
+ npm run build
101
+ npm run lint
102
+ npm run test:providers
103
+ npm run check:example
104
+ npm run check:native-artifacts
105
+ npm run check:pack
106
+ ```
107
+
108
+ What each check covers:
109
+
110
+ | Check | Purpose |
111
+ |---|---|
112
+ | `check:metadata` | Confirms package metadata, README, LICENSE, plugin, and module config exist. |
113
+ | `clean` + `build` | Rebuilds TypeScript output in `build/`. |
114
+ | `lint` | Runs ESLint over `src/`. |
115
+ | `test:providers` | Validates provider ids, template hashes, parser hashes, request match rules, HTTPS proof URLs, SNI, and SPKI pins. |
116
+ | `check:example` | Type-checks the Expo example when its dependencies are installed. |
117
+ | `check:native-artifacts` | Fails if Android `.so`, generated Kotlin, iOS XCFramework, or generated Swift glue are missing. |
118
+ | `check:pack` | Runs `npm pack --dry-run --json` and verifies the actual tarball contents. |
119
+
120
+ The package uses a `files` whitelist in `package.json`, so `node_modules`, the example
121
+ app, `.env` files, `.DS_Store`, build caches, and generated tarballs are kept out of the
122
+ npm package.
123
+
124
+ ## Dry Run
125
+
126
+ After `release:check` passes, inspect the package:
127
+
128
+ ```bash
129
+ npm run pack:dry-run
130
+ npm publish --dry-run
131
+ ```
132
+
133
+ Check that the tarball contains:
134
+
135
+ ```text
136
+ build/
137
+ src/providers.json
138
+ app.plugin.js
139
+ expo-module.config.json
140
+ android/src/main/
141
+ ios/SatyaAttest.podspec
142
+ ios/SatyaAttestModule.swift
143
+ ios/Generated/
144
+ ios/Frameworks/
145
+ README.md
146
+ LICENSE
147
+ ```
148
+
149
+ Check that it does not contain:
150
+
151
+ ```text
152
+ expo-satya-attest-example/
153
+ node_modules/
154
+ .env
155
+ .DS_Store
156
+ *.tgz
157
+ internal/module_scripts/
158
+ ```
159
+
160
+ ## Publish
161
+
162
+ Log in and publish:
163
+
164
+ ```bash
165
+ npm login
166
+ npm publish
167
+ ```
168
+
169
+ For a scoped public package, use:
170
+
171
+ ```bash
172
+ npm publish --access public
173
+ ```
174
+
175
+ For prereleases:
176
+
177
+ ```bash
178
+ npm version prerelease --preid alpha
179
+ npm publish --tag alpha
180
+ ```
181
+
182
+ For stable releases:
183
+
184
+ ```bash
185
+ npm version patch
186
+ npm publish
187
+ ```
188
+
189
+ ## Post-Publish Smoke Test
190
+
191
+ Create a temporary Expo app and install from npm:
192
+
193
+ ```bash
194
+ npx create-expo-app SatyaSmoke
195
+ cd SatyaSmoke
196
+ npx expo install @burnt-labs/expo-satya-attest react-native-webview
197
+ ```
198
+
199
+ Add the plugin to `app.json`:
200
+
201
+ ```json
202
+ {
203
+ "expo": {
204
+ "plugins": [["@burnt-labs/expo-satya-attest", { "appAttestEnvironment": "development" }]]
205
+ }
206
+ }
207
+ ```
208
+
209
+ Render `SatyaProviderAttestButton`, rebuild a development client, and run on a real
210
+ device:
211
+
212
+ ```bash
213
+ npx expo run:ios
214
+ npx expo run:android
215
+ ```
216
+
217
+ The app still needs access to a running `satya-verifier-service` for verifier-backed SPKI
218
+ policy refresh and backend artifact verification.
219
+
220
+ ## GitHub And Local Install Smoke Tests
221
+
222
+ Consumers may also install the SDK from a local directory or GitHub release tarball.
223
+ Both paths must include the same built files as the npm package.
224
+
225
+ Local directory smoke test:
226
+
227
+ ```bash
228
+ cd platform_sdks/expo-satya-attest
229
+ npm run release:check
230
+
231
+ cd /private/tmp/SatyaSmoke
232
+ npm install /path/to/tls-app-attest/platform_sdks/expo-satya-attest
233
+ npx expo install react-native-webview
234
+ ```
235
+
236
+ GitHub release tarball smoke test:
237
+
238
+ ```bash
239
+ cd /private/tmp/SatyaSmoke
240
+ npm install https://github.com/<owner>/<repo>/releases/download/expo-satya-attest-v0.1.0/burnt-labs-expo-satya-attest-0.1.0.tgz
241
+ npx expo install react-native-webview
242
+ ```
243
+
244
+ For a raw Git install, publish the SDK from a dedicated repo or a subtree/split branch
245
+ where the SDK package is at the Git root:
246
+
247
+ ```bash
248
+ npm install github:<owner>/<sdk-repo>#v0.1.0
249
+ ```
250
+
251
+ Do not document a normal monorepo Git URL as plug-and-play unless the Git ref has
252
+ `platform_sdks/expo-satya-attest` split to the package root. npm does not install a
253
+ package from a GitHub subdirectory directly, and this SDK needs its prebuilt native
254
+ artifacts in the installed package.
255
+
256
+ ## Binary Size Guidance
257
+
258
+ The native Rust artifacts are expected and should ship in the npm package. They are not
259
+ "extra" optional files for this SDK because the native replay path depends on them.
260
+
261
+ Keep watching:
262
+
263
+ - `npm run check:native-artifacts` for Android/iOS unpacked binary size.
264
+ - `npm run check:pack` for npm tarball and unpacked package size.
265
+ - Whether every release includes only the ABIs you actually support.
266
+
267
+ Current default iOS output includes only device `arm64`. If you need simulator artifacts
268
+ for local fallback/debug work, opt in explicitly:
269
+
270
+ ```bash
271
+ SATYA_IOS_INCLUDE_SIMULATOR=1 npm run build:native:ios
272
+ ```
273
+
274
+ For Apple-Silicon-simulator-only fallback builds:
275
+
276
+ ```bash
277
+ SATYA_IOS_SIMULATOR_TARGETS="aarch64-apple-ios-sim" npm run build:native:ios
278
+ ```
279
+
280
+ Do not publish simulator slices unless you intentionally support a simulator fallback
281
+ experience. They are not useful for real App Attest verification.
282
+
283
+ If the tarball becomes too large for npm or painful for app installs, the next design
284
+ step is a binary-release model:
285
+
286
+ 1. Publish checksummed XCFramework and Android AAR/native artifacts to GitHub Releases.
287
+ 2. Update the podspec/Gradle integration to consume those artifacts deterministically.
288
+ 3. Keep install scripts fail-closed and checksum verified.
289
+
290
+ Do that only if size forces it. For the current plug-and-play SDK shape, bundling the
291
+ native artifacts inside the npm package is simpler and safer for users.