Skip to content

Foundation for iOS client support#4

Merged
dingwen07 merged 5 commits into
mainfrom
feature/kmp-ios
Jun 26, 2026
Merged

Foundation for iOS client support#4
dingwen07 merged 5 commits into
mainfrom
feature/kmp-ios

Conversation

@dingwen07

Copy link
Copy Markdown
Owner

Summary

  • Convert :protocol to Kotlin Multiplatform and add an iOS NotiSyncProtocol XCFramework target while keeping JVM consumers/tests intact.
  • Add Kotlin-generated golden vectors for Swift compatibility across client IDs, CBOR payloads, HTTP signing, key epochs, HPKE sealing, and envelope opening.
  • Improve APNs delivery for iOS: support env-scoped sandbox/production signing keys, include message type in push data, use alert + mutable-content for encrypted notifications, and keep dismissal/sync pushes silent.
  • Polish the Android iOS bridge UI with shipped/generic iOS icons, iOS-shaped icon masking, app icon attachment for test notifications, and self-mirroring exclusions for NotiSync bundle IDs.
  • Retry relay fetches with the identity root after an operational-key 401 to handle epoch mismatch during rotation/convergence.

Generate Kotlin→Swift golden vectors and convert the protocol module to KMP.

- Add GoldenVectorsTest (protocol-crypto) to emit JSON golden vectors for the iOS XCTest suite and validate on the JVM.
- Convert :protocol to Kotlin Multiplatform (jvm + iOS targets), produce an XCFramework, and reorganize sourceSets (commonMain + jvmTest) with serialization/cbor deps.
- Enable kotlin.multiplatform plugin in root build and libs.versions.toml; add kotlinx.serialization to protocol-crypto tests.
- Add BrokerClient.resetVerificationBackoff() and call it from SettingsScreen refresh action for diagnostics.
- Minor import fix in Identifiers.kt.
Pass the envelope message type through push payloads so transports (APNs) can choose alert vs background delivery. Broker.buildPushData now accepts a MessageType and adds an "mtyp" field to the push map; call sites were updated to pass envelope.typ. ApnsPushTransport reads mtyp to decide apns-push-type and apns-priority (alert/10 for NOTIFICATION, background/5 otherwise) and builds an appropriate APS payload (mutable-content alert placeholder vs content-available). A unit test was added to verify NOTIFICATION uses alert push type and priority.
APNs push transport now supports separate provider-token signers for production and sandbox, with sandbox config falling back to the primary key for unscoped keys. Server config adds dedicated sandbox key ID/path env vars, and transport initialization now enables APNs when at least one environment is configured.

Token invalidation on APNs 403 was tightened to only re-mint for remintable reasons (ExpiredProviderToken, MissingProviderToken), avoiding repeated re-mints on InvalidProviderToken misconfiguration that can trigger APNs 429s. Tests were updated for the new provider map API and add coverage to ensure InvalidProviderToken does not invalidate cached tokens.
Bump version to 1.3.1-rc.1 and add manifest query for net.extrawdw.notifly. Add launcher icon asset and wire GraphicsPipeline into AppGraph so captured notifications include app icons. Introduce IosBundleIdExclusions to keep NotiSync bundle IDs discoverable but never enable them; update registry logic and add unit tests. iOS screen: generic iOS icon, new masked icon shape, square icon rendering, and disable toggles for excluded bundles.
Use the operational key for relay message fetches, then retry once with the identity root on 401 when the broker has not yet ingested the current epoch. Also bumps the app version to 1.3.1-rc.2 and updates broker App Check allow-listing for the iOS app ID.
Copilot AI review requested due to automatic review settings June 26, 2026 19:48
@dingwen07
dingwen07 merged commit 07cd4cf into main Jun 26, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR lays the groundwork for iOS client support by moving the shared protocol module to Kotlin Multiplatform (with an iOS XCFramework), adding cross-platform crypto “golden vectors” for Swift compatibility testing, and updating server/APNs delivery and Android bridge UI behaviors needed for iOS.

Changes:

  • Convert :protocol to Kotlin Multiplatform and add an iOS NotiSyncProtocol XCFramework target while keeping JVM usage intact.
  • Improve APNs delivery semantics (env-scoped signing keys, message-type-aware alert vs background behavior, and safer token invalidation behavior).
  • Add Kotlin-generated golden vectors (to be consumed by iOS XCTest) and refine Android-side iOS bridge UX (icons, exclusions, relay fetch retry behavior, diagnostics).

Reviewed changes

Copilot reviewed 22 out of 25 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
server/src/test/kotlin/net/extrawdw/notisync/server/ApnsPushTransportTest.kt Updates APNs transport tests for env-scoped providers and new alert notification behavior.
server/src/main/kotlin/net/extrawdw/notisync/server/delivery/push/Apns.kt Adds env-scoped APNs token providers, alert vs background branching, and remint rules.
server/src/main/kotlin/net/extrawdw/notisync/server/Config.kt Adds sandbox APNs key/path config fields.
server/src/main/kotlin/net/extrawdw/notisync/server/broker/Broker.kt Includes message type (mtyp) in push data for APNs delivery branching.
protocol/src/main/kotlin/net/extrawdw/notisync/protocol/Identifiers.kt Adds JvmInline import for value classes under KMP.
protocol/build.gradle.kts Converts :protocol to Kotlin Multiplatform and defines iOS XCFramework build.
protocol-crypto/src/test/kotlin/net/extrawdw/notisync/protocol/crypto/GoldenVectorsTest.kt Adds JVM test that emits Swift-consumable golden vectors JSON.
protocol-crypto/build.gradle.kts Enables Kotlin serialization plugin for test-only @Serializable vector schema.
gradle/libs.versions.toml Adds Kotlin Multiplatform plugin alias.
deploy/notisync-broker-v2.service Expands App Check allow-list to include iOS app ID.
build.gradle.kts Adds Kotlin Multiplatform plugin to root plugin management.
app/src/test/java/net/extrawdw/apps/notisync/appicon/ShippedIconsTest.kt Tests generic iOS icon fallback behavior without suppressing App Store fetches.
app/src/test/java/net/extrawdw/apps/notisync/ancs/IosAppRegistryTest.kt Adds tests for excluded bundle IDs and enabled filtering.
app/src/main/java/net/extrawdw/apps/notisync/ui/SettingsScreen.kt Adds a UI refresh action to reset verification backoff.
app/src/main/java/net/extrawdw/apps/notisync/ui/IosScreen.kt Improves iOS bridge UI (icon masking, toggle exclusions, square icon rendering).
app/src/main/java/net/extrawdw/apps/notisync/transport/BrokerClient.kt Retries relay fetch with identity root after operational-key 401.
app/src/main/java/net/extrawdw/apps/notisync/notification/GraphicsPipeline.kt Refactors app icon attachment into a reusable suspend function.
app/src/main/java/net/extrawdw/apps/notisync/appicon/ShippedIcons.kt Adds a separate generic iOS fallback icon API.
app/src/main/java/net/extrawdw/apps/notisync/appicon/IconResolver.kt Adds optional inclusion of generic iOS fallback to avoid suppressing App Store fetch.
app/src/main/java/net/extrawdw/apps/notisync/AppGraph.kt Attaches app icon for test notifications before mirroring.
app/src/main/java/net/extrawdw/apps/notisync/ancs/IosAppRegistry.kt Prevents enabling excluded bundle IDs and persists iOS app discovery/enabled set.
app/src/main/AndroidManifest.xml Adds Notifly to package visibility queries for diagnostic source.
app/build.gradle.kts Bumps app version to 1.3.1-rc.2.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +162 to +168
// Sandbox (api.sandbox.push.apple.com) — its own env-scoped key, else reuse the (unscoped) one.
val sandboxKeyId = config.apnsKeyIdSandbox.ifBlank { config.apnsKeyId }
val sandboxPath = config.apnsPrivateKeyPathSandbox.ifBlank { config.apnsPrivateKeyPath }
if (sandboxKeyId.isNotBlank() && sandboxPath.isNotBlank()) {
val key = Es256.loadEcPrivateKeyPem(File(sandboxPath).readText())
providers[RouteEnvironment.DEVELOPMENT] = ApnsJwtProvider(config.apnsTeamId, sandboxKeyId, key)
}
Comment on lines +87 to +91
_enabled.update {
if (IosBundleIdExclusions.isExcluded(bundleId)) {
it - bundleId
} else if (enabled) {
it + bundleId
* Phase 0 of the iOS plan: generate the Kotlin→Swift golden vectors and assert their self-consistency
* on the JVM so this doubles as a regression guard. The emitted JSON file is consumed by the iOS
* XCTest suite (Phases 2–3) to prove the native Swift crypto is byte-compatible with this reference
* implementation. See ~/.claude/plans/read-docs-notisync-ios-library-implement-giggly-sky.md §B.5 / §E.
val hpkeSealToRaw: HpkeRawVec,
val envelopeOpen: EnvelopeOpenVec,
) {
fun vectorCount() = 10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants