Releasing
Maintainer steps for versioning, publishing, and recovery
This page is for maintainers publishing Expo Easy Passkey. App teams installing the package usually only need Install, Platforms, and Device E2E.
Release model
expo-easy-passkey is the only package published to npm. It contains the public TypeScript API, the Expo native module, the config plugin, iOS and Android native code, and generated binding files used during release validation.
The repository has internal source boundaries that are not npm package boundaries:
packages/moduleis the released package.crates/passkey-coreandcrates/passkey-ffiare internal Rust crates used for tests, fixtures, and generated native bindings.apps/example,apps/docs, andtooling/*are private workspaces.
Changesets handles package versioning and changelog entries. During the alpha stream, Changesets runs in prerelease mode with the alpha tag, so the first release PR should version expo-easy-passkey as 0.1.0-alpha.0.
Contributors
Every user-facing change should include a changeset:
pnpm changesetChoose the semver intent for expo-easy-passkey:
patchfor fixes and small documentation corrections that affect published package users.minorfor additive APIs, native platform support, or meaningful behavior improvements.majorfor breaking public API or config changes.
For the alpha stream, Changesets converts that intent into an alpha prerelease version. Commit the generated .changeset/*.md file with the code change.
Before merging a feature PR, CI should pass the repository verification jobs, including TypeScript, Rust, binding freshness, Android compile, and iOS compile.
Maintainers
After feature PRs land on main, the release workflow runs. If there are pending changesets, it opens or updates a Changesets release PR.
Review the release PR before merging it:
- Confirm only
expo-easy-passkeyis versioned. - Confirm
packages/module/CHANGELOG.mddescribes the release clearly. - Confirm package metadata still points to the single installable package.
- Confirm CI is green, including
pnpm checkandpnpm verify:release-acceptance. - Confirm required manual device E2E notes are recorded under Release evidence below.
- Confirm response shapes and public error codes are unchanged, or that the changelog includes an explicit compatibility note.
Merging the release PR runs the release workflow again. This time Changesets publishes expo-easy-passkey to npm, creates tags, and updates GitHub release metadata.
After publish, verify npm:
npm view expo-easy-passkey version
npm view expo-easy-passkey dist-tagsFor alpha releases, verify that the latest alpha is on the alpha dist-tag and not promoted to the stable latest tag.
Automation
The release workflow runs on pushes to main and on manual workflow_dispatch.
Before publication work starts, trusted jobs rebuild native Rust artifacts from the checked-out release commit and the committed Cargo.lock:
- Android builds every supported ABI (
arm64-v8a,armeabi-v7a,x86,x86_64) on Linux with the NDK. - Apple builds the device (
ios-arm64) and simulator (ios-arm64_x86_64-simulator) xcframework slices on macOS.
Those jobs write into isolated runner temp roots (--artifacts-root), then upload immutable workflow artifacts plus metadata that records the source commit, lockfile digest, toolchain identity, target inventory, and output checksums. A missing supported target or build failure blocks the downstream release job.
Separate smoke jobs then download those exact artifacts (they do not rebuild natives) into packages/module, pack expo-easy-passkey, install the tarball into disposable Android and Apple consumers, inspect every required ABI and Apple device/simulator slice, and execute one exported UniFFI helper from the packaged runtime for each platform family. Missing, stale, mismatched, or unloadable binaries fail before publication.
Native libraries, the Apple xcframework, and .rust-artifacts metadata are gitignored because they are not bit-reproducible across hosts. Checkout never supplies publication binaries. Trusted jobs rebuild from the release commit, and the release job stages only those smoke-validated outputs.
The release job downloads the same smoke-validated Android and Apple artifacts into isolated staging directories, runs prepare-trusted-native-artifacts to wipe package native paths and install only those trusted outputs, writes packages/module/.rust-artifacts/release-evidence.json (source commit, lockfile digest, per-platform toolchain, target inventory, and checksums), uploads that evidence, then performs:
pnpm install --frozen-lockfile
pnpm rust:fmt
pnpm rust:clippy
pnpm bindgen:check
pnpm check
pnpm verify:release-acceptance
pnpm build
pnpm pack:checkAny contract, native artifact, packed-consumer, web import, or acceptance-plan wiring failure blocks publication. Publication cannot fall back to checkout binaries: prepare replaces them before pack:check and changeset publish. npm provenance remains enabled via publishConfig.provenance and the workflow id-token: write permission, so the published package is attested to this workflow run and commit.
Then changesets/action either opens the release PR or publishes with:
pnpm version-packages
pnpm releasepnpm pack:check builds the package, runs the Expo module prepublish check, and performs a dry-run npm pack inspection for expo-easy-passkey.
GitHub and npm
The repository must allow the release workflow to write contents and pull requests. The workflow also requests id-token: write so npm can attach provenance to published artifacts.
Configure these settings before the first release:
- Add
NPM_TOKENas a GitHub Actions secret. - Use an npm automation token or trusted-publishing-compatible token policy that can publish
expo-easy-passkey. - Keep npm 2FA enabled for the maintainer account; use a token type compatible with CI publishing.
- Ensure the npm package name is available and the token has permission to publish it.
- Ensure GitHub Actions can create pull requests and tags with
GITHUB_TOKEN.
Release evidence
Before merging a release PR, review and record the evidence that proves the cross-platform contract:
Automated evidence (must be green in CI / release):
- Package-to-relying-party registration and authentication for the HTTPS iOS origin and configured Android APK-key-hash origins
- Exact origin trust, concurrent and replayed ceremony isolation, iOS pre-presentation policy validation, and web/SSR unsupported behavior
- Trusted native artifact build, packaged-target inspection, FFI smoke tests, and exact-artifact publication checks
release-evidence.jsonsource commit, lockfile digest, toolchain, target inventory, and checksums
Required real-device evidence from Device E2E (fixtures cannot prove these):
- Association: hosted
apple-app-site-associationandassetlinks.json - Signing: app build certificates match Digital Asset Links and the server Android origin allowlist
- System UI: platform passkey sheets appear for registration and authentication
- User verification: biometric or device credential completion succeeds end to end
Compatibility evidence:
- Existing WebAuthn response shapes and public error codes remain unchanged unless the changelog includes an explicit compatibility note
Record the device model, OS version, app build profile, relying-party domain, server environment, and which association / signing paths were exercised. Keep those notes with the release PR review.
Validation
Run the full local verification suite before merging a release PR when native, Rust, generated binding, or public API changes are involved:
pnpm verify
pnpm test:acceptance
pnpm verify:release-acceptance
pnpm bindgen:check
pnpm --filter expo-easy-passkey prepublishOnly
pnpm --filter expo-easy-passkey pack --dry-runpnpm verify already includes pnpm check, which runs the acceptance Jest files. pnpm test:acceptance is the local subset if you want to re-run only those suites.
Before release, run device E2E too. Passkey ceremonies depend on platform accounts, signing certificates, associated domains, Digital Asset Links, credential providers, and user verification UI. Follow Device E2E and complete the Release evidence checklist above.
When Rust, UniFFI, or native helper code changes, regenerate packageable native Rust artifacts locally for development and review. Trusted release jobs rebuild and upload immutable artifacts with metadata, then smoke-test the packed package against those outputs before publication:
pnpm build:rust-artifacts
# or platform-scoped:
# pnpm build:rust-artifacts android
# pnpm build:rust-artifacts apple
pnpm bindgen:check
pnpm --filter expo-easy-passkey build
# After metadata exists under packages/module/.rust-artifacts/:
# pnpm smoke:native-artifacts --platform apple
# pnpm smoke:native-artifacts --platform android
# pnpm prepare:trusted-native-artifacts --android-from <trusted-android> --apple-from <trusted-apple> \
# --expected-source-commit <sha> --evidence-output packages/module/.rust-artifacts/release-evidence.json
pnpm pack:checkLocal pnpm build:rust-artifacts writes Android shared libraries to packages/module/android/src/main/jniLibs, the iOS xcframework to packages/module/ios/rust, and metadata under packages/module/.rust-artifacts/ for development. Those paths are gitignored. Release jobs build into isolated temp roots, smoke-test the packed package against those outputs, then prepare:trusted-native-artifacts stages only the trusted CI artifacts and ships release-evidence.json inside the npm package.
Failures
If the release PR checks fail, fix the failing code or generated artifacts in a normal PR. The release PR will update after the fix lands on main.
If npm publish fails before a version is published, fix the token, package metadata, or workflow issue and rerun the release workflow.
If npm publish partially succeeds, do not republish the same version. Inspect npm first:
npm view expo-easy-passkey versions
npm view expo-easy-passkey dist-tagsIf an alpha version was published with the wrong dist-tag, correct the tag instead of publishing a replacement version:
npm dist-tag add expo-easy-passkey@0.1.0-alpha.0 alpha
npm dist-tag rm expo-easy-passkey latestIf a bad package has already been published, publish a new patch or prerelease version with a changeset that explains the fix. Avoid unpublishing unless the case satisfies npm's unpublish policy.