Skip to content

feat: share Pubky identities with Bitkit - #342

Draft
Jasonvdb wants to merge 8 commits into
mainfrom
feat/android-shared-pubky-relaunch
Draft

feat: share Pubky identities with Bitkit#342
Jasonvdb wants to merge 8 commits into
mainfrom
feat/android-shared-pubky-relaunch

Conversation

@Jasonvdb

@Jasonvdb Jasonvdb commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Important

Dependencies and merge/release order

  1. Publish the final legacy Pubky Ring v1.17 sunset build first. The latest public release is still v1.16; do not merge or release this replacement before v1.17 is published.
  2. Merge the Bitkit parent UI stacks before their consumers: Bitkit iOS #636, and Bitkit Android #1084 followed by #1097.
  3. Complete this PR's release gates: confirm the Android Play-installed signing-certificate model and provision the iOS shared Keychain group/profiles.
  4. Merge this PR and release the new Ring identity provider/authenticator.
  5. Rebase and merge the dependent consumers, Bitkit iOS #643 and Bitkit Android #1109, then release them after Ring.

#342 is blocked by the v1.17 publication and its Apple/Play release gates. The Bitkit consumer branches are stacked on their own parent PRs. At runtime, #342 and #643/#1109 are counterparts: Ring-to-Bitkit becomes available when Ring ships first and the consumers then ship; Ring's reverse “Use from Bitkit” flow becomes available only after the corresponding consumer ships its Bitkit-owned provider.

Summary

  • Relaunch Android Ring as app.pubkyring version 2.0 after the legacy sunset release.
  • Share Ring-owned Pubky identities with Bitkit through a signature-protected Android provider and a shared iOS Keychain group.
  • Discover and explicitly connect Bitkit-owned identities in Ring without sharing mnemonics or persisting borrowed private keys.
  • Keep source ownership explicit, retrieve borrowed credentials just in time, and disconnect when the source app or identity disappears.
  • Serialize identity writes, deletions, adoption, reconciliation, and wipe operations so partial failures remain recoverable.
  • Add a temporary Ring-native “Use from Bitkit” path using the existing Ring visual language until dedicated reverse-flow designs arrive.

Protocol and security

  • The protocol is versioned and carries a canonical 52-character bare Pubky plus a 64-character lowercase secret-key encoding.
  • Android providers are read-only, use signature permissions, enforce exact package/UID ownership, and verify matching signing certificates at runtime.
  • iOS records use WhenUnlockedThisDeviceOnly in $(AppIdentifierPrefix)pubky.shared and never synchronize through iCloud.
  • Discovery returns public identity metadata only; credentials are read only for the identity the user explicitly selects.
  • Every credential is re-derived and matched to its claimed public key before use.
  • Borrowed secrets are excluded from backups, exports, and Ring's canonical private identity store.
  • Availability URL schemes are hints only; shared entitlement/provider checks and payload validation are the trust boundary.

Migration and source ownership

The app-private Ring keychain remains the canonical source. Migration and reconciliation never delete it: Ring writes the shared mirror only after the private write succeeds, reads the mirror back for verification, and prunes stale Ring-owned mirrors only after the current record is valid. Explicit destructive flows delete and verify the shared mirror before deleting private identity state, and stop if shared cleanup fails.

The released-user migration is safe because no prior public Ring build shipped this sharing model. The v7 migration therefore treats legacy Ring state as Ring-owned. Old draft or proof-of-concept builds may have copied a Bitkit key without durable provenance; anyone who installed those private drafts must uninstall/wipe Ring before testing this PR. If any such build was distributed externally, its state needs provenance quarantine rather than automatic ownership inference.

Borrowed Bitkit identities retain only a source reference and a local session. Ring never silently promotes a borrowed identity to owned state, never replaces an active/recoverable identity, and drops local access when the source identity is no longer available.

Release gates

Android

Release packaging requires all five production signing inputs, validates the keystore/key and pinned certificate, rejects the debug certificate, and verifies the final APK/AAB signer. Debug Ring and all Bitkit debug variants currently use the same checked-in certificate:

FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:66:75:91:03:3B:9C

Production release remains blocked until Play Console confirms whether Bitkit's CI keystore certificate is the installed app-signing certificate or only an upload certificate. If Play uses a distinct app-signing key, keep a separate Ring upload-certificate check, pin the installed Bitkit certificate independently, and verify the Play-generated Ring APK against the installed Bitkit artifact.

iOS

Code and simulator work can proceed before App Store Connect access is available. Shipping remains blocked until pubky.shared is created and attached to both App IDs, provisioning profiles are regenerated, and signed two-app physical-device interoperability passes. Missing entitlements fail closed and leave the canonical private source untouched.

Verification

  • yarn typecheck
  • yarn format:check
  • yarn lint — 0 errors; existing non-blocking warnings remain
  • yarn test --runInBand — 5 suites, 45 tests
  • Android debug assembly and APK signer inspection pass for all produced APKs.
  • Android E2E packages JavaScript into its Metro-less debug APK behind an explicit CI-only Gradle flag; a clean x86_64 build passes and both generated APKs contain assets/index.android.bundle.
  • Hosted lint/checks and the full hosted iOS E2E job pass.
  • Hosted Android E2E builds and installs the APK successfully and the rerun passes 5/6 flows: onboarding, default-homeserver creation, deletion, mnemonic import, and key migration. The only repeated failure is the existing dynamic staging-signup/authorization flow timing out while waiting for PubkyBox-StagingTestPubky-0; this is not an APK assembly, installation, or JavaScript-packaging failure, but it remains an open integration gate rather than being waived.
  • Android release verification and actual release assembly fail closed when the five production signer inputs are absent.
  • iOS plist, entitlement, and project validation pass.
  • Unsigned generic iOS simulator build passes with the shared-Keychain native module compiled for both simulator architectures.
  • git diff --check

Android application unit tests are NO-SOURCE. Android lint currently crashes inside the react-native-worklets dependency with Cannot find a KaModule for the VirtualFile; an isolated retry reproduces the dependency failure without a Ring diagnostic.

QA

  • Existing legacy Ring user → update to v1.17 first: sunset messaging/data behavior is preserved.
  • Same-certificate Ring and Bitkit debug installs → Ring-owned identity: Bitkit discovers and connects it.
  • Same-certificate Ring and Bitkit debug installs → Bitkit-owned identity: Ring discovers and connects it through “Use from Bitkit.”
  • Borrowed identity → restart source and consumer: the session restores only while the source record exists.
  • Borrowed identity → delete source identity or uninstall source app → foreground consumer: local access disconnects.
  • Owned identity → update from pre-sharing build: private state remains canonical and a verified mirror is created.
  • Owned identity → delete/wipe: shared mirror is removed before private state.
  • Mismatched-signature Android build: provider access is denied.
  • Signed iOS device builds with regenerated profiles: both apps read only the expected source-owned records.
  • Old internal draft tester: uninstall/wipe before exercising migration.

@Jasonvdb
Jasonvdb force-pushed the release/legacy-android-sunset branch from d9617a3 to 369b704 Compare July 21, 2026 13:44
Base automatically changed from release/legacy-android-sunset to main July 22, 2026 13:19
@Jasonvdb
Jasonvdb force-pushed the feat/android-shared-pubky-relaunch branch from fa2f0ae to 5b6eac8 Compare July 24, 2026 13:26
@Jasonvdb Jasonvdb changed the title feat(android): relaunch Ring with Bitkit sharing feat: share Pubky identities with Bitkit Jul 24, 2026
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.

1 participant