diff --git a/specs/projects/sdk/workstreams/native-shells/SPEC.md b/specs/projects/sdk/workstreams/native-shells/SPEC.md index 938d30701..bf927b93f 100644 --- a/specs/projects/sdk/workstreams/native-shells/SPEC.md +++ b/specs/projects/sdk/workstreams/native-shells/SPEC.md @@ -78,15 +78,17 @@ ## Backlog -| ID | Title | Status | Priority | Depends On | Plan | PR | -| ----- | ------------------------------------------------------------- | -------- | -------- | ---------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| NS-01 | Physical-device validation matrix for Android + iOS NFC flows | Done | High | - | [plans/NS-01-physical-device-validation.md](./plans/NS-01-physical-device-validation.md) | - | -| NS-02 | iOS Camera MRZ Phase 2 | Deferred | Medium | NS-01 | - | - | -| NS-03 | Publishing readiness for AAR + XCFramework artifacts | Ready | High | NS-01 | [plans/NS-03-publishing-readiness.md](./plans/NS-03-publishing-readiness.md) | - | -| NS-04 | APDU allowlist in KMP NFC bridge handler | Ready | High | - | [plans/NS-04-apdu-allowlist.md](./plans/NS-04-apdu-allowlist.md) | - | -| NS-05 | LifecycleBridgeHandler type/error semantics on iOS | Ready | Low | - | [plans/NS-05-lifecycle-handler-semantics.md](./plans/NS-05-lifecycle-handler-semantics.md) | - | -| NS-06 | Align KMP callback/result contract with canonical SDK types | Done | Medium | NS-01 | [plans/NS-06-kmp-callback-contract-alignment.md](./plans/NS-06-kmp-callback-contract-alignment.md) | - | -| NS-07 | Remove legacy `{ type }` shim from native lifecycle handlers | Blocked | Medium | - | - | Blocked on WebView bundle emitting canonical outcomes instead of flat `{ type }` payloads. Requires coordinated change: update `ConfirmIdentificationScreen.tsx` and `ProvingScreen.tsx` to send canonical `VerificationResult`, then remove the `type != null` branch from both Android and iOS `LifecycleBridgeHandler`. | +| ID | Title | Status | Priority | Depends On | Plan | PR | +| ----- | ------------------------------------------------------------- | -------- | -------- | ------------ | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| NS-01 | Physical-device validation matrix for Android + iOS NFC flows | Done | High | - | [plans/NS-01-physical-device-validation.md](./plans/NS-01-physical-device-validation.md) | - | +| NS-02 | iOS Camera MRZ Phase 2 | Deferred | Medium | NS-01 | - | - | +| NS-03 | Publishing readiness for AAR + XCFramework artifacts | Done | High | NS-01 | [plans/NS-03-publishing-readiness.md](./plans/NS-03-publishing-readiness.md) | N/A (audit-only) | +| NS-04 | APDU allowlist in KMP NFC bridge handler | Ready | High | - | [plans/NS-04-apdu-allowlist.md](./plans/NS-04-apdu-allowlist.md) | - | +| NS-05 | LifecycleBridgeHandler type/error semantics on iOS | Ready | Low | - | [plans/NS-05-lifecycle-handler-semantics.md](./plans/NS-05-lifecycle-handler-semantics.md) | - | +| NS-06 | Align KMP callback/result contract with canonical SDK types | Done | Medium | NS-01 | [plans/NS-06-kmp-callback-contract-alignment.md](./plans/NS-06-kmp-callback-contract-alignment.md) | - | +| NS-07 | Remove legacy `{ type }` shim from native lifecycle handlers | Blocked | Medium | - | - | Blocked on WebView bundle emitting canonical outcomes instead of flat `{ type }` payloads. Requires coordinated change: update `ConfirmIdentificationScreen.tsx` and `ProvingScreen.tsx` to send canonical `VerificationResult`, then remove the `type != null` branch from both Android and iOS `LifecycleBridgeHandler`. | +| NS-08 | Ship AAR + XCFramework to MiniPay | Blocked | High | NS-03, NS-09 | [plans/NS-08-ship-artifacts-to-minipay.md](./plans/NS-08-ship-artifacts-to-minipay.md) | ~10 LOC Gradle config for Maven repo target, switch XCFramework to release variants, host XCFramework for SPM. iOS handoff blocked until NS-09 resolves the private NFCPassportReader dependency. | +| NS-09 | Make NFCPassportReader fork accessible to external consumers | Ready | High | - | [plans/NS-09-nfcpassportreader-distribution.md](./plans/NS-09-nfcpassportreader-distribution.md) | Decision: make fork public or vendor it. Blocks any external iOS consumer. | Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done` @@ -95,10 +97,12 @@ Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done` | Plan | IDs | Status | | -------------------------------------------------------------------------------------------------- | ----- | ------ | | [plans/NS-01-physical-device-validation.md](./plans/NS-01-physical-device-validation.md) | NS-01 | Done | -| [plans/NS-03-publishing-readiness.md](./plans/NS-03-publishing-readiness.md) | NS-03 | Ready | +| [plans/NS-03-publishing-readiness.md](./plans/NS-03-publishing-readiness.md) | NS-03 | Done | | [plans/NS-04-apdu-allowlist.md](./plans/NS-04-apdu-allowlist.md) | NS-04 | Ready | | [plans/NS-05-lifecycle-handler-semantics.md](./plans/NS-05-lifecycle-handler-semantics.md) | NS-05 | Ready | | [plans/NS-06-kmp-callback-contract-alignment.md](./plans/NS-06-kmp-callback-contract-alignment.md) | NS-06 | Done | +| [plans/NS-08-ship-artifacts-to-minipay.md](./plans/NS-08-ship-artifacts-to-minipay.md) | NS-08 | Ready | +| [plans/NS-09-nfcpassportreader-distribution.md](./plans/NS-09-nfcpassportreader-distribution.md) | NS-09 | Ready | ## Completion Checklist @@ -112,6 +116,7 @@ Allowed statuses: `Ready`, `In Progress`, `Blocked`, `Deferred`, `Done` - `NS-01` completed on 2026-03-10 after operator-assisted real-device NFC validation confirmed Android and iOS success and failure paths in the KMP test app. See [plans/NS-01-physical-device-validation.md](./plans/NS-01-physical-device-validation.md) for the validation log. - `NS-06` completed on 2026-03-10. KMP now exposes the canonical `VerificationResult` shape (`success`, `userId`, `verificationId`, `proof`, `claims`, `error`), and `claims` now carries heterogeneous values via `Map`. - Flat lifecycle `{ type }` payloads remain supported as an internal compatibility shim while the embedded WebView bundle still emits them, but KMP host apps no longer receive a public `VerificationResult.type` field. Tracked as `NS-07` for removal once the WebView sends canonical outcomes. +- `NS-03` completed on 2026-03-10. Audit validated AAR and XCFramework generation. Four items block shipping to MiniPay: Maven repo target (~10 LOC config), release XCFramework variants (~3 LOC), hosted XCFramework for SPM, and NFCPassportReader fork accessibility. Tracked as NS-08 and NS-09. ## Overview diff --git a/specs/projects/sdk/workstreams/native-shells/plans/NS-03-publishing-readiness.md b/specs/projects/sdk/workstreams/native-shells/plans/NS-03-publishing-readiness.md index aff12d2d2..7643b317b 100644 --- a/specs/projects/sdk/workstreams/native-shells/plans/NS-03-publishing-readiness.md +++ b/specs/projects/sdk/workstreams/native-shells/plans/NS-03-publishing-readiness.md @@ -1,67 +1,130 @@ # Publishing Readiness for AAR and XCFramework > Last updated: 2026-03-10 -> Status: Ready +> Status: Done (audit complete) - Workstream: native-shells - Backlog IDs: NS-03 - Owner: Native Shells -- Branch: TBD -- PR: TBD +- Branch: N/A (audit-only, no code changes) +- PR: N/A -## Why +## Goal -- `Production publishing (npm + AAR + XCFramework)` is still open at the SDK level. -- Packaging gaps are easy to lose track of because implementation and release concerns are split across multiple specs. -- This plan isolates artifact-readiness work from feature work. - -## Scope - -- Audit current AAR and XCFramework generation paths. -- Define missing packaging metadata, versioning, release inputs, and validation steps. -- Update the relevant specs so release readiness is tracked in one place. +Answer one question: **can MiniPay (or another host app) integrate the KMP SDK today?** Document what works, what doesn't, and the shortest path to a shippable artifact. ## Out of Scope -- Performing the actual release/publish step. +- Enterprise publishing (Maven Central signing, full POM compliance, CocoaPods). - RN npm publishing. - Feature changes to handlers or bridge contracts. -## Files to Modify +--- -- `specs/projects/sdk/workstreams/native-shells/SPEC.md` -- `specs/projects/sdk/OVERVIEW.md` -- packaging/handoff docs if needed +## Audit Results -## Files Not to Modify +### Android AAR -- runtime handler implementations unless packaging audit exposes a required packaging-only change +**Status: Builds and publishes to local Maven. Not yet configured for a remote repository.** -## Preconditions +**Validated (ran locally on 2026-03-10):** -- Physical-device validation should be complete or explicitly waived. -- Versioning/release ownership is identified. +| Step | Command | Result | +| ---------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| Release AAR | `./gradlew :shared:assembleRelease` | `shared/build/outputs/aar/shared-release.aar` | +| Maven local publish | `./gradlew :shared:publishToMavenLocal` | Installs AAR + POM + sources + Gradle metadata to `~/.m2/repository/xyz/self/sdk/shared-android/0.1.0/` | +| POM transitive deps | Inspected generated POM | Declares kotlin-stdlib, webkit, jmrtd, bouncycastle, mlkit, camera, biometrics with correct scopes | +| WebView asset bundling | `copyWebViewAssets` (runs on preBuild) | Copies `webview-app/dist/` into Android assets | -## Implementation Notes +**Not validated:** Consumer resolution (a separate Gradle project resolving `xyz.self.sdk:shared-android:0.1.0` from mavenLocal and compiling against it). The POM and Gradle metadata look correct but this hasn't been tested end-to-end from a clean consumer project. -- Keep this focused on release readiness, not implementation cleanup. -- If packaging requires code changes, split those into child backlog IDs or follow-up plans. +**To ship to MiniPay:** -## Validation +1. Add a `publishing { repositories {} }` block pointing to GitHub Packages (or any Maven repo MiniPay can reach). ~10 lines of Gradle config. +2. Bump version from `0.1.0` when ready. +3. Ideally, validate consumer resolution from a clean project before the first external handoff. + +**Later (not blocking):** ProGuard consumer rules, CI publish job, Maven Central migration if needed. + +### iOS KMP Framework / XCFramework + +**Status: Builds. Shippable via local SPM path or manual XCFramework handoff.** + +**What works:** + +| Step | Command | Result | +| ------------------- | ------------------------------------------------------- | ------------------------------------------------------ | +| XCFramework | `./gradlew createXCFramework` | `SelfSdk.xcframework` (arm64 device + arm64 simulator) | +| SPM Package.swift | Exists | Points to local XCFramework binary target | +| Simulator framework | `./gradlew :shared:linkDebugFrameworkIosSimulatorArm64` | Works | + +**To ship to an iOS host app:** + +1. Switch `createXCFramework` from debug to release framework variants. In `build.gradle.kts`, change `linkDebugFrameworkIos*` → `linkReleaseFrameworkIos*` in the `createXCFramework` task's `dependsOn` and path strings (~3 lines). +2. Build: `./gradlew createXCFramework` → `shared/build/xcframework/SelfSdk.xcframework`. +3. Zip + checksum: `zip -r SelfSdk-0.1.0.xcframework.zip SelfSdk.xcframework && swift package compute-checksum SelfSdk-0.1.0.xcframework.zip`. +4. Upload the zip to a GitHub Release (e.g., `kmp-sdk@0.1.0`) or any HTTPS-reachable location. +5. Update `packages/kmp-sdk/Package.swift` to use `.binaryTarget(name: "SelfSdk", url: "", checksum: "")` instead of the local path. +6. Tag the release. SPM consumers add the repo URL with the version tag. + +**Ownership:** Whoever cuts the release owns steps 2–6. Version naming follows `packages/kmp-sdk/shared/build.gradle.kts` `version` field. Git tag format: `kmp-sdk@`. + +**Not yet decided:** Whether the host app consumes `SelfSdk.xcframework` (KMP) directly and adds `self-sdk-swift` as a separate SPM dependency, or whether a wrapper package bundles both. Current default: two separate dependencies — KMP XCFramework + Swift companion SPM package. This is fine for MiniPay (single known consumer) but should be revisited if the consumer count grows. + +**Later (not blocking):** x86_64 simulator slice (Intel Macs), CocoaPods podspec, CI automation. + +### Swift Companion Package (`self-sdk-swift`) + +**Status: Works in Xcode and on real devices. `swift build` CLI fails (irrelevant for iOS distribution).** + +**What works:** + +- CI builds via `xcodebuild` for iOS Simulator +- Real-device NFC validated through KMP test app +- Providers wire into KMP iOS via `SdkProviderRegistry` + +**Known issue:** `swift build` fails because OpenSSL headers don't resolve under SPM CLI on macOS. This doesn't affect iOS distribution (always goes through Xcode). + +**To ship:** + +1. The `NFCPassportReader` fork is private (`git@github.com:selfxyz/NFCPassportReader.git`). Any external consumer needs access. Simplest fix: make the fork public, or vendor it. +2. That's the only real blocker. Everything else (versioning, CI) is polish. + +--- + +## Summary: What Blocks Shipping + +| What | Effort | Blocks | +| ----------------------------------------------------------- | -------------- | ------------------------------- | +| Add Maven repository target to Gradle | ~10 LOC config | Android distribution to MiniPay | +| Switch XCFramework to release variants | ~3 LOC config | iOS production builds | +| Host XCFramework somewhere reachable + update Package.swift | Small | iOS SPM distribution | +| Make NFCPassportReader fork accessible | Decision | Any external iOS consumer | + +Everything else (Maven Central, GPG signing, full POM metadata, ProGuard rules, x86_64 slices, CocoaPods, CI publish jobs, version automation) is real work but doesn't block getting artifacts into MiniPay's hands. + +--- + +## Validated Commands ```bash -cd packages/kmp-sdk && ./gradlew :shared:assembleDebug -cd packages/kmp-sdk && ./gradlew :shared:linkDebugFrameworkIosSimulatorArm64 -cd packages/self-sdk-swift && swift build +cd packages/kmp-sdk && ./gradlew :shared:assembleRelease # ✅ AAR +cd packages/kmp-sdk && ./gradlew :shared:publishToMavenLocal # ✅ AAR + POM + sources + metadata to ~/.m2 +cd packages/kmp-sdk && ./gradlew :shared:linkDebugFrameworkIosSimulatorArm64 # ✅ iOS framework +cd packages/kmp-sdk && ./gradlew createXCFramework # ✅ XCFramework +cd packages/self-sdk-swift && swift build # ❌ Known OpenSSL header issue (not a distribution blocker) ``` +**Not yet validated:** Consumer resolution from a clean Gradle project. POM and metadata look correct but no end-to-end consume test has been run. + ## Definition of Done -- [ ] AAR generation path documented and validated -- [ ] XCFramework or framework packaging path documented and validated -- [ ] Remaining blockers explicitly listed -- [ ] Backlog row updated +- [x] AAR generation path documented and validated +- [x] XCFramework or framework packaging path documented and validated +- [x] Remaining blockers explicitly listed +- [x] Backlog row updated ## Status Log - 2026-03-10: Created from SDK publishing follow-up. +- 2026-03-10: Audit complete. Artifacts build. Four items block shipping to external consumers; all are small config or decision items. diff --git a/specs/projects/sdk/workstreams/native-shells/plans/NS-08-ship-artifacts-to-minipay.md b/specs/projects/sdk/workstreams/native-shells/plans/NS-08-ship-artifacts-to-minipay.md new file mode 100644 index 000000000..561523a03 --- /dev/null +++ b/specs/projects/sdk/workstreams/native-shells/plans/NS-08-ship-artifacts-to-minipay.md @@ -0,0 +1,59 @@ +# Ship AAR + XCFramework to MiniPay + +> Last updated: 2026-03-10 +> Status: Ready + +- Workstream: native-shells +- Backlog IDs: NS-08 +- Owner: Native Shells +- Depends on: NS-03 +- Branch: TBD +- PR: TBD + +## Goal + +Make KMP SDK artifacts consumable by MiniPay. Android via Maven repository, iOS via SPM with hosted XCFramework. + +## Scope + +### Android + +1. Add `publishing { repositories { maven { ... } } }` block to `shared/build.gradle.kts` targeting GitHub Packages (or whichever Maven repo MiniPay can resolve). +2. Validate: run `./gradlew :shared:publish`, then resolve `xyz.self.sdk:shared-android:0.1.0` from a clean consumer Gradle project. + +### iOS + +1. Switch `createXCFramework` task from debug to release variants (~3 lines in `build.gradle.kts`). +2. Build, zip, compute checksum, upload to GitHub Release. +3. Update `Package.swift` binary target from local path to release URL + checksum. +4. Tag release. Validate: add SPM dependency from a clean Xcode project. + +## Files to Modify + +- `packages/kmp-sdk/shared/build.gradle.kts` — add publishing repository block +- `packages/kmp-sdk/shared/build.gradle.kts` — switch `createXCFramework` to release variants +- `packages/kmp-sdk/Package.swift` — remote binary target URL + +## Out of Scope + +- Maven Central (GPG signing, full POM metadata). GitHub Packages or equivalent is sufficient. +- CocoaPods. SPM is sufficient for MiniPay. +- CI automation for publishing. Manual release is fine for now. + +## Validation + +```bash +# Android +cd packages/kmp-sdk && ./gradlew :shared:publish +# Then from a separate consumer project: resolve xyz.self.sdk:shared-android: + +# iOS +cd packages/kmp-sdk && ./gradlew createXCFramework +# Then from a clean Xcode project: add SPM dependency and build +``` + +## Definition of Done + +- [ ] MiniPay can resolve the Android AAR from a Maven repository +- [ ] MiniPay can add the iOS XCFramework via SPM from a hosted URL +- [ ] Consumer resolution validated end-to-end on both platforms diff --git a/specs/projects/sdk/workstreams/native-shells/plans/NS-09-nfcpassportreader-distribution.md b/specs/projects/sdk/workstreams/native-shells/plans/NS-09-nfcpassportreader-distribution.md new file mode 100644 index 000000000..5ca5cb18f --- /dev/null +++ b/specs/projects/sdk/workstreams/native-shells/plans/NS-09-nfcpassportreader-distribution.md @@ -0,0 +1,42 @@ +# NFCPassportReader Distribution Strategy + +> Last updated: 2026-03-10 +> Status: Ready + +- Workstream: native-shells +- Backlog IDs: NS-09 +- Owner: Native Shells +- Branch: TBD +- PR: TBD + +## Goal + +Make `self-sdk-swift` consumable by external iOS host apps. The blocker is the private `NFCPassportReader` fork dependency. + +## Problem + +`self-sdk-swift/Package.swift` depends on `git@github.com:selfxyz/NFCPassportReader.git` (SSH, private fork). Any external consumer needs GitHub SSH access to the selfxyz org to resolve this dependency. MiniPay (or any host app outside the org) can't build without it. + +## Options + +| Option | Effort | Trade-off | +| ----------------------------------------------------- | ------ | ------------------------------------------------------------------------------- | +| Make the fork public | Low | Exposes fork changes; may have upstream license implications to check | +| Vendor NFCPassportReader source into `self-sdk-swift` | Medium | No external dependency; increases repo size; must manually sync upstream | +| Pre-build NFCPassportReader as XCFramework binary | Medium | Consumers get a binary; but adds a build/release step for the dependency itself | +| Switch Package.swift to HTTPS + access token | Low | Still requires credentials; doesn't solve the external consumer problem | + +## Recommendation + +Make the fork public unless there's a specific reason it's private. Lowest effort, solves the problem completely. + +## Scope + +1. Decide on approach (decision, not code). +2. Execute the chosen option. +3. If the fork remains an external dependency, update `packages/self-sdk-swift/Package.swift` from the SSH URL to an HTTPS URL and pin a tag/revision that external consumers can resolve. +4. Validate: `self-sdk-swift` resolves from a clean environment without org SSH credentials. + +## Definition of Done + +- [ ] An external iOS consumer can resolve all `self-sdk-swift` dependencies without selfxyz org credentials