Skip to content

Add apple-signer and keyring-signer packages#1

Open
hayes-mysten wants to merge 4 commits into
mainfrom
add-apple-and-keyring-signers
Open

Add apple-signer and keyring-signer packages#1
hayes-mysten wants to merge 4 commits into
mainfrom
add-apple-and-keyring-signers

Conversation

@hayes-mysten

@hayes-mysten hayes-mysten commented Apr 23, 2026

Copy link
Copy Markdown
Collaborator

Description

Two new incubation packages for Sui key management, plus matching fumadocs pages.

  • @mysten-incubation/apple-signer (macOS-only) — Secure Enclave signer + macOS Keychain signer, both fronted by a signed Swift helper with one-Touch-ID-per-process UX. Universal arm64+x86_64 binary ships inside the package at bin/apple-signer. Enclave mode works today with ad-hoc signing; Keychain mode throws errSecMissingEntitlement under ad-hoc and flips to working once a Developer ID–signed helper is wired into CI. Also exposes keypairFromP12 at the /recover subpath for decoding PKCS#12 exports from Keychain Access.app.
  • @mysten-incubation/keyring-signer (cross-platform) — OS-keyring-backed key persistence via @napi-rs/keyring with non-extractable WebCrypto signing. Ed25519 + Secp256r1. No backend abstraction — OS keyring is the one store; for env vars / files / fixtures, callers reach for @mysten/sui's standard keypair API directly.

Both packages carry prominent dev/test-only disclaimers at the top of their docs and are pre-1.0. Key-loss warnings (enclave = device-bound, non-recoverable; keyring = any same-user process can read silently) are surfaced in the lede, not buried in caveats.

Also included:

  • packages/docs/content/apple-signer/ + packages/docs/content/keyring-signer/ — single page per package, examples-first layout
  • .changeset/apple-signer-initial.md + .changeset/keyring-signer-initial.md
  • .gitignore: adds .build/ for SwiftPM output

Out of scope — follow-ups:

  • Developer ID CI pipeline (cert provision + security import + xcrun notarytool submit inside changesets.yml). Unblocks Keychain mode in production.
  • Windows / Linux siblings (windows-hello-signer, tpm-signer) as separate packages using the same shape.

Test plan

  • pnpm install — deps pull, .build/ ignored, universal binary picked up at packages/apple-signer/bin/apple-signer
  • pnpm --filter @mysten-incubation/keyring-signer test — 17 pass
  • pnpm --filter @mysten-incubation/apple-signer test — 19 pass (signer + recover)
  • pnpm --filter @mysten-incubation/keyring-signer build — typecheck + tsdown, clean
  • pnpm --filter @mysten-incubation/apple-signer buildswift build --arch arm64, swift build --arch x86_64, lipo, ad-hoc codesign, typecheck, tsdown
  • pnpm --filter @mysten-incubation/apple-signer roundtrip — enclave mode all checks green with one Touch ID prompt; keychain mode gracefully skipped under ad-hoc signing with the documented -34018 message
  • pnpm --filter @mysten-incubation/docs validate-docs — 0 errors
  • pnpm --filter @mysten-incubation/docs dev → browse /apple-signer and /keyring-signer

AI Assistance Notice

Please disclose the usage of AI. This is primarily to help inform reviewers of how careful they need to review PRs, and to keep track of AI usage across our team. Please fill this out accurately, and do not modify the content or heading for this section!

  • This PR was primarily written by AI.
  • I used AI for docs / tests, but manually wrote the source code.
  • I used AI to understand the problem space / repository.
  • I did not use AI for this PR.

Two new incubation packages for Sui key management, plus matching fumadocs pages.

- @mysten-incubation/apple-signer (macOS-only): Secure Enclave signer + Keychain
  signer behind a signed Swift helper. Ships a universal arm64+x86_64 binary
  inside the package at bin/apple-signer. Ad-hoc signed today; Developer ID CI
  pipeline is a follow-up. Also exposes keypairFromP12 at /recover for decoding
  PKCS#12 exports from Keychain Access.app.

- @mysten-incubation/keyring-signer (cross-platform): OS-keyring-backed key
  persistence via @napi-rs/keyring with non-extractable WebCrypto signing.
  Supports Ed25519 and Secp256r1. No backend abstraction — the OS keyring is
  the one store; non-keyring cases use @mysten/sui keypairs directly.

Both packages carry dev/test-only disclaimers and are pre-1.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Apr 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sui-dev-wallet Ready Ready Preview, Comment Apr 23, 2026 11:42pm
sui-ts-sdks-incubation Ready Ready Preview, Comment Apr 23, 2026 11:42pm

Request Review

- Delete packages/apple-signer/examples and packages/keyring-signer/examples.
  The roundtrip scripts were manual-verification only and aren't needed in the
  shipped tree.
- Remove "roundtrip" npm scripts from both packages now that the files are gone.
- Sort packages/docs dependencies alphabetically (manypkg check was failing).
- apple-signer README: drop the "Running the end-to-end roundtrip" section and
  fix the stale link to the deleted security.mdx page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Installing SIGINT/SIGTERM handlers that call process.exit() made the
package a hostile co-resident with other signal listeners — frameworks,
test runners, graceful-shutdown libs. App-owned shutdown coordinators
would have their async cleanup killed by our exit() call, and the async
cleanup in our own handlers never actually ran (.then microtasks can't
complete in a synchronous signal context before process.exit).

Rely instead on stdin EOF for child termination: during any normal Node
teardown, child stdin pipes get closed, the Swift helper's readLine()
returns nil, and the helper exits on its own. Keep a synchronous 'exit'
listener as belt-and-suspenders for edge cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Swift helper emits structured error codes (not_found, already_exists,
  auth_failed, user_canceled, missing_entitlement, unknown) in both
  JSON response `code` field and NSError userInfo. decodeOSStatus()
  maps common OSStatus values to friendly messages; catch sites forward
  the code via codeFromError().
- TS side adds HelperError subclass carrying code; replaces brittle
  /not found/i regex in tryEnclavePubkey and tryKeychainPubkey with
  err.code === 'not_found'.
- createEnclaveSigner / createKeychainSigner recover from a lost
  check-then-create race by falling back to pubkey lookup on
  already_exists (keychain fallback only for seed: 'random').
- Default helper self-heals: AppleHelper.onExit() lets the cache evict
  a dead subprocess promise so the next factory call respawns.
- __resetDefaultHelperForTesting now closes the prior helper instead
  of orphaning it.
- keyring-signer importKeyringSigner validates scheme via signerFromBech32
  BEFORE writing to the keyring, so unsupported Bech32 doesn't leave an
  unusable entry.
- keyring-signer memoizes NapiKeyringBackend.load() with a module-level
  promise.
- Both packages declare engines.node >=22 (WebCrypto Ed25519, modern
  Node features).
- build-universal.sh adds --options runtime --timestamp when
  APPLE_SIGNER_IDENTITY is a real signing identity (required for
  notarization); keeps ad-hoc as the default for local builds.
- turbo.json: move bin/** from build inputs to outputs; add Swift
  sources as inputs so cache invalidates correctly.
- .gitignore: scope .build/ to packages/apple-signer/native/ so it
  doesn't silently match unrelated future packages.
- Docs: fix broken #recovery anchor (→ #recovery-from-a-p12-file),
  drop two stray sui.keystore references, correct wrong package name
  in recover.ts JSDoc example.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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