chio-kernel-mobile 0.1.2

Mobile FFI bindings (iOS / Android) for the portable Chio kernel core via UniFFI
Documentation
# chio-kernel-mobile: operator bindings guide

This directory documents how to generate the Swift (iOS) and Kotlin
(Android) bindings for `chio-kernel-mobile` and link the static /
shared library into a mobile app. The crate itself ships pure Rust;
all language bindings are emitted at build time by the UniFFI
toolchain from `src/chio_kernel_mobile.udl`.

## Prerequisites

- Rust toolchain matching the workspace `rust-version` (1.93+).
- `uniffi-bindgen` binary. Not a default Cargo install because
  UniFFI publishes its binary under the namespaced crate
  `uniffi_bindgen`. On this repo install with:

  ```bash
  cargo install --git https://github.com/mozilla/uniffi-rs \
      --tag v0.28.3 --bin uniffi-bindgen uniffi_bindgen
  ```

  (Pinning to `v0.28.3` matches the `uniffi = "0.28"` dependency in
  `Cargo.toml`. If the workspace bumps the UniFFI version, bump the
  tag here in lockstep.)

  If your operator host has no Git access, build the binary from the
  workspace itself by adding a `[[bin]] name = "uniffi-bindgen"`
  target to `chio-kernel-mobile/Cargo.toml` (see
  `uniffi/docs/tutorial/foreign_language_bindings.md` upstream).

- iOS target: `rustup target add aarch64-apple-ios aarch64-apple-ios-sim
  x86_64-apple-ios`.
- Android target: `rustup target add aarch64-linux-android
  armv7-linux-androideabi i686-linux-android x86_64-linux-android`, plus
  the Android NDK (r25+) with a `cargo-ndk` wrapper or hand-rolled
  linker config pointing at the NDK-supplied clang.

## One-command verification

Run the repo-local verification suite from the workspace root:

```bash
./scripts/qualify-mobile-kernel.sh
```

It records four lane results under
`target/release-qualification/mobile-kernel/`:

- `host_ffi`: Rust-side JSON-in / JSON-out roundtrip tests
- `ios_device`: `aarch64-apple-ios` static library build
- `ios_sim`: `aarch64-apple-ios-sim` static library build when the target
  is installed
- `android_arm64`: `aarch64-linux-android` shared-library build when a
  real NDK toolchain is provisioned through `cargo-ndk`

Status values are explicit:

- `pass`: the lane ran on this host and succeeded
- `environment_dependent`: the host is missing the required SDK, target, or
  NDK tooling, so the script records that honestly instead of pretending the
  lane was qualified
- `fail`: the host had the required prerequisites and the lane still failed

The overall qualification gate fails unless at least one target-backed iOS or
Android lane runs and passes. The host FFI test is required coverage, but it
does not by itself qualify the mobile target surface.

## Eleven-entry mobile surface

The UDL exports eleven functions. Six are the portable kernel surface:

- `evaluate(request_json)`: evaluate a tool-call request against a
  capability token.
- `sign_receipt(body_json, canonical_content_hex, signing_seed_hex)`: the
  public WYSIWYS receipt signer. Recomputes `content_hash` from the
  canonical content preimage inside the trust boundary and refuses to
  sign on a render/sign mismatch.
- `sign_receipt_relaying_trusted_body(body_json, signing_seed_hex)`:
  relay-sign a receipt body an upstream trusted producer already
  minted. Trusts the caller-supplied `content_hash` and does not
  recompute it; content-bearing callers must use `sign_receipt` instead.
- `verify_capability(token_json, authority_pub_hex)`: verify a capability
  token against a single trusted authority key.
- `verify_capability_with_context(request_json)`: verify a capability
  token with the full portable JSON context (trust roots, parent-budget
  snapshots) so delegated tokens can be checked.
- `verify_passport(envelope_json, issuer_pub_hex, now_secs)`: verify a
  portable passport envelope offline.

The remaining five are mobile-attestation entries:

- `attest_app_attest(key_id, challenge_hex)`: produce the App Attest
  challenge envelope the native DeviceCheck attestation object must bind
  to.
- `verify_app_attest_evidence(key_id, challenge_hex, app_id, attestation_cbor_hex, previous_counter)`:
  verify an Apple App Attest attestation object against the pinned Apple
  root, the issued challenge, the app id, and counter monotonicity.
- `attest_play_integrity(nonce_hex)`: produce the Play Integrity nonce
  envelope the platform JWS must bind to.
- `verify_play_integrity_evidence(token, expected_nonce, expected_package_name, expected_audience, jwks_json)`:
  verify a Play Integrity JWS against the pinned Google JWKS and the
  expected nonce, package name, and audience claims.
- `verify_mobile_receipt(receipt_json, evidence_json)`: shape-check a
  mobile receipt against App Attest or Play Integrity evidence before it
  is handed to the hosted oracle. Returns an explicit non-authoritative
  status; it does not authorize a capability or prove device integrity.

## Generating the Swift bindings

```bash
# 1. Build the static library for every iOS architecture you ship for.
CARGO_TARGET_DIR=target/mobile cargo build \
    --release --target aarch64-apple-ios -p chio-kernel-mobile
CARGO_TARGET_DIR=target/mobile cargo build \
    --release --target aarch64-apple-ios-sim -p chio-kernel-mobile
CARGO_TARGET_DIR=target/mobile cargo build \
    --release --target x86_64-apple-ios -p chio-kernel-mobile

# 2. Emit the Swift bindings.
mkdir -p out/swift
uniffi-bindgen generate \
    --language swift \
    --out-dir out/swift \
    crates/kernel/chio-kernel-mobile/src/chio_kernel_mobile.udl
```

`out/swift/chio_kernel_mobile.swift` is the module file to drop into
Xcode. `out/swift/chio_kernel_mobileFFI.h` is the matching C header;
package it together with a `.xcframework` that lipos the three
static libraries (`libchio_kernel_mobile.a`) from step 1.

### Linking in Xcode

1. Create an xcframework with `xcodebuild -create-xcframework`.
2. Add the framework to your app target's **Frameworks, Libraries,
   and Embedded Content** section.
3. Import the module in Swift: `import chio_kernel_mobile`.
4. Call the entry points directly -- `try evaluate(requestJson:)`,
   `try signReceipt(bodyJson:canonicalContentHex:signingSeedHex:)`,
   `try signReceiptRelayingTrustedBody(bodyJson:signingSeedHex:)`,
   `try verifyCapability(tokenJson:authorityPubHex:)`,
   `try verifyCapabilityWithContext(requestJson:)`, and
   `try verifyPassport(envelopeJson:issuerPubHex:nowSecs:)`. The mobile-attestation entries add
   `try attestAppAttest(keyId:challengeHex:)`,
   `try verifyAppAttestEvidence(keyId:challengeHex:appId:attestationCborHex:previousCounter:)`,
   `try attestPlayIntegrity(nonceHex:)`,
   `try verifyPlayIntegrityEvidence(token:expectedNonce:expectedPackageName:expectedAudience:jwksJson:)`, and
   `try verifyMobileReceipt(receiptJson:evidenceJson:)`.

## Generating the Kotlin bindings

```bash
# 1. Build the shared library for every Android ABI you ship for. Use
#    cargo-ndk (`cargo install cargo-ndk`) to hand the correct linker
#    to rustc automatically.
CARGO_TARGET_DIR=target/mobile cargo ndk \
    --target aarch64-linux-android --target armv7-linux-androideabi \
    --target x86_64-linux-android --target i686-linux-android \
    -o android/jniLibs build --release -p chio-kernel-mobile

# 2. Emit the Kotlin bindings.
mkdir -p out/kotlin
uniffi-bindgen generate \
    --language kotlin \
    --out-dir out/kotlin \
    crates/kernel/chio-kernel-mobile/src/chio_kernel_mobile.udl
```

`out/kotlin/uniffi/chio_kernel_mobile/chio_kernel_mobile.kt` is the
module file to drop into the `src/main/java` tree of your Android
Gradle module. `android/jniLibs/<abi>/libchio_kernel_mobile.so` goes
into `src/main/jniLibs/<abi>/` alongside the module's resources.

### Linking in Gradle

1. Add `net.java.dev.jna:jna:5.14.0@aar` to the module dependencies
   (UniFFI-generated Kotlin uses JNA to load the shared library).
2. Confirm the JNI libs are packaged under `src/main/jniLibs`.
3. Import the module in Kotlin: `import uniffi.chio_kernel_mobile.*`.
4. Call the entry points directly -- `evaluate(requestJson)`,
   `signReceipt(bodyJson, canonicalContentHex, signingSeedHex)`,
   `signReceiptRelayingTrustedBody(bodyJson, signingSeedHex)`,
   `verifyCapability(tokenJson, authorityPubHex)`,
   `verifyCapabilityWithContext(requestJson)`, and
   `verifyPassport(envelopeJson, issuerPubHex, nowSecs)`. The mobile-attestation entries add
   `attestAppAttest(keyId, challengeHex)`,
   `verifyAppAttestEvidence(keyId, challengeHex, appId, attestationCborHex, previousCounter)`,
   `attestPlayIntegrity(nonceHex)`,
   `verifyPlayIntegrityEvidence(token, expectedNonce, expectedPackageName, expectedAudience, jwksJson)`, and
   `verifyMobileReceipt(receiptJson, evidenceJson)`.

## Offline receipt sync pattern

The offline-first workflow caches a capability, evaluates tool calls
locally while disconnected, and syncs the resulting receipts to a
backend when connectivity returns. The FFI exposes the primitives for
all three halves:

1. **Cache** a capability token (JSON) to the device keystore
   (`KeychainService` on iOS, `EncryptedSharedPreferences` on Android).
2. **Gate** each tool call with `evaluate()` using the cached token
   and the device wall-clock (`MobileClock` is wired up automatically
   when `now_secs <= 0`).
3. **Sign** a receipt for each gated call with `signReceipt()` and
   append the returned JSON to a local queue (SQLite or the
   platform's durable key-value store).
4. **Sync** on reconnect: drain the queue and POST each receipt to
   the operator's `chio-siem` ingestion endpoint or Merkle-committed
   receipt log. The receipt's signature remains verifiable
   regardless of sync timing.

## Qualification artifacts

`./scripts/qualify-mobile-kernel.sh` emits:

- `target/release-qualification/mobile-kernel/report.md`
- `target/release-qualification/mobile-kernel/summary.json`
- one `*.log` file per lane

That output is the authoritative host-local record of which mobile lanes are
currently qualified versus environment-dependent. A run with only
environment-dependent target lanes is not release-qualified.

## UniFFI bindgen invocation verification

`uniffi-bindgen` is NOT installed on every operator host. To run the
verification step during local development:

```bash
# After cargo install uniffi-bindgen (see Prerequisites):
uniffi-bindgen generate --language swift --out-dir out \
    crates/kernel/chio-kernel-mobile/src/chio_kernel_mobile.udl
uniffi-bindgen generate --language kotlin --out-dir out \
    crates/kernel/chio-kernel-mobile/src/chio_kernel_mobile.udl
```

The Swift / Kotlin files listed in this directory
(`bindings/swift/ChioKernel.md` and `bindings/kotlin/ChioKernel.md`)
are hand-authored API references that mirror the UDL interface
verbatim. They are the single source of truth for the Swift /
Kotlin surface; the generated files should match them shape-for-shape.