Skip to content

feat(mero-sign/mobile): iOS and Android on Calimero 1.0 - #694

Open
frdomovic wants to merge 5 commits into
mainfrom
feat/mero-sign-ios-core-1.0
Open

frdomovic wants to merge 5 commits into
mainfrom
feat/mero-sign-ios-core-1.0

Conversation

@frdomovic

@frdomovic frdomovic commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Moves Mero Sign's native apps (apps/mero-sign/mobile/ios and apps/mero-sign/mobile/android) to Calimero core 1.0: sealed channel to an attested node, passkeys, orgs and app instances, org invites, approvals on the node's consent page. Both call the contract exactly as the web port #726 declares it (logic/res/abi.json) and use the web's layout, so a phone and a browser on the same account see the same agreements and share one signature library. Same screens, flows and copy on both platforms.

Mobile only: nothing outside apps/mero-sign/mobile/**. Based on main, not on #726.

SDK pins

  • iOS: swift-sdk#85 at 37c09f3f96e7d98440f6618eef8a0e83ddee8ebd (ios/project.yml, ios/SignCore/Package.swift, Package.resolved).
  • Android: kotlin-sdk#68 at 61b65e9c0c87db8fbb2d3b2ce827603b0c84d5f9 (android/kotlin-sdk.ref, composite build).

Both pins move to the SDK PRs' merge commits once those land.

Contract (from #726)

SignClient (Swift and Kotlin) is written from the component ABI, since no codegen targets one yet. Its Swift and Kotlin surface is the same as before.

  • Method names, arguments and answer fields are camelCase.
  • u64 arguments are sent as decimal strings; answers are read as either strings or numbers.
  • Accounts are strings, blob ids are 64-character hex, and timestamps are milliseconds (were nanoseconds).
  • none is sent as an explicit null.
  • setup(isPrivate, contextName) replaces the init arguments.
  • ContractSurfaceTests (Swift) and ContractSurfaceTest (Kotlin) check every method name, parameter set, view/call split and the wire encoding against the ABI.

Layout (the web's app/src/lib/agreements.ts)

  • Workspace: an org.
  • Agreement: an app instance in that org. Opening one registers you as a signer if you are not on the roster. Conflict and Forbidden (a guest) are fine; you can still read it. The agreement is then recorded in your library with joinSharedContext. "Joined" now means you are on the roster.
  • Signature library: an instance named mero-sign:library, owned by the account and in no org, set up with setup(true, "default"). This replaces the 0.x "Personal" namespace marked by metadata, with its restricted subgroup.
  • Blobs: both apps use the client's uploadBlob(app, bytes, contentType) (application/pdf for signed versions, image/png for signatures) and blobContent(app, id). The contract stores the hex id.

Sign-in, invites, approvals (same on both)

  • Sign-in: a Calimero Cloud cluster by name (nodes from the node directory) or a node URL, then a passkey: sign in or create an account. A development node (merod local, mock attestation) uses a software passkey kept on the phone.
  • Invites: the SDK's OrgInvitation links, the web's own ?invite=inv_…&agreement=<app id> links (pasted), and bare inv_… tokens. Redeeming the invite tells the app which workspace it belongs to.
  • Approvals: the Inbox card "Needs your approval" shows the confirmation code first, then opens the consent page (system browser on iOS, Custom Tab on Android), which returns to mero-sign://consent.
  • Errors in plain words: ConfirmCancelled, LoginRequired, WrongNode, Unavailable, ReleaseDeadline, ConsentStale, and the contract's APP_ERROR codes. Conflict on signDocument reloads the document and asks you to sign the new version.
  • Android: wallet enrolment, the relay, signed group invitations and the group-metadata marker are gone. The Activity's PlatformPasskey is set on MeroAppSession.passkey in onCreate and cleared in onDestroy. The Gradle properties are now meroCluster, meroDevNodeUrl and meroConsentCallback.

Verification (build only, no simulator or emulator)

  • iOS: swift test in ios/SignCore passes 39 tests. xcodegen generate + xcodebuild build -destination 'generic/platform=iOS' CODE_SIGNING_ALLOWED=NO succeeds.
  • Android: ./gradlew ktlintCheck detekt testDebugUnitTest lint assembleDebug passes, with 23 unit tests.

Gaps

  • Blob uploads: per feat(mero-sign): move to the component SDK, merod local and CalimeroClient #726, a node at core 1.0.0-rc.7 answers 409 NOT_SUPPORTED on a component app's blob routes until core#4815 ships. Until then, signing and saving a signature fail with the node's message on rc.7.
  • Dev bundles: merod local with a dev bundle (not a package) is not wired. The apps list and create instances by package.
  • Empty workspaces: the phones list orgs of this app (founded for it, or running an instance). A workspace created on the web without an agreement yet doesn't show up.
  • Untested live: not run against a live cluster or merod local.
  • scripts/regenerate-client.sh is removed because the codegen only targets v1 ABIs.

🤖 Generated with Claude Code

frdomovic and others added 2 commits October 30, 2026 13:55
Pin swift-sdk to the head of its Calimero 1.0 branch (35148e4, PR #85)
and adapt the iOS app: passkey sign-in and account creation with a
node-URL path (mock attestation for merod local), orgs/teams/app
instances in place of namespaces/groups/contexts, org invite tokens in
invitation links, pending releases and upgrades approved on the node's
consent page in the system browser, and the typed 1.0 errors in words.
Tests and README updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Calimero 1.0 branch moved by one additive fix (the consent
confirmation code); no app change needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@frdomovic frdomovic changed the title feat(mero-sign-ios): run on Calimero 1.0 feat(mero-sign/mobile-ios): native iOS app on Calimero 1.0 (mobile only, web app untouched) Oct 30, 2026
frdomovic and others added 2 commits November 2, 2026 11:08
- SignClient (Swift, Kotlin) calls the component ABI from #726: camelCase
  methods, arguments and answers, u64s as decimal strings, explicit nulls,
  setup() in place of init args; ContractSurfaceTests pin it to the ABI.
- The web's layout: agreements are app instances in the workspace org, the
  signature library is an account-owned instance (mero-sign:library) set up
  private; joined = on the roster.
- Blobs through the signed-in client with content types (uploadBlob,
  blobContent); timestamps in milliseconds.
- Invitations: OrgInvitation links, the web's ?invite= links and bare tokens.
- Android moves off 0.x: cluster by name or node URL, passkey sign-in or
  create account, development node with a soft passkey, approvals card with
  the confirmation code first. iOS: swift-sdk 37c09f3, cluster names,
  confirmation code before the consent page.
- kotlin-sdk 61b65e9 (kotlin-sdk#68).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@frdomovic frdomovic changed the title feat(mero-sign/mobile-ios): native iOS app on Calimero 1.0 (mobile only, web app untouched) feat(mero-sign/mobile): iOS and Android on Calimero 1.0 Nov 2, 2026
iOS takes the swift-sdk release (exactVersion 0.5.0) instead of the
swift-sdk#85 head; Android's kotlin-sdk.ref moves to the v0.2.0 tag
commit (still a composite build: the release is on GitHub Packages,
not Maven Central).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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