Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ jobs:
with:
persist-credentials: false

- uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly
- uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly-2026-01-10
with:
toolchain: nightly
toolchain: nightly-2026-01-10
components: rustfmt, clippy

- uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # stable
Expand All @@ -43,11 +43,11 @@ jobs:
- name: cargo check wasm32
run: cargo check --target wasm32-unknown-unknown -p truapi-server

- name: cargo +nightly fmt --check
run: cargo +nightly fmt --check
- name: cargo +nightly-2026-01-10 fmt --check
run: cargo +nightly-2026-01-10 fmt --check

- name: cargo +nightly clippy
run: cargo +nightly clippy --workspace --all-targets --all-features -- -D warnings
- name: cargo +nightly-2026-01-10 clippy
run: cargo +nightly-2026-01-10 clippy --workspace --all-targets --all-features -- -D warnings

- name: cargo test
run: cargo test --workspace --all-features
Expand Down Expand Up @@ -77,9 +77,9 @@ jobs:
with:
toolchain: stable

- uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly
- uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly-2026-01-10
with:
toolchain: nightly
toolchain: nightly-2026-01-10
components: rustfmt

- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ jobs:
- name: Configure Pages
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0

- uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly
- uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly-2026-01-10
with:
toolchain: nightly
toolchain: nightly-2026-01-10
components: rustfmt

- name: Install workspace dependencies
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/deploy-playground.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ jobs:
with:
toolchain: stable

- uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly
- uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly-2026-01-10
with:
toolchain: nightly
toolchain: nightly-2026-01-10
components: rustfmt

- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
Expand Down
20 changes: 13 additions & 7 deletions .github/workflows/diagnosis-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,27 @@ jobs:
run: |
set -euo pipefail

# Host mode from the report title (`## Truapi <mode> Diagnosis`).
# Host mode and modality from the report title
# (`## Truapi <mode> [Chat] Diagnosis`).
mode=$(printf '%s' "$BODY" \
| grep -ioP '##\s+Truapi\s+\K(Web|Desktop|Android|iOS)(?=\s+Diagnosis)' \
| grep -ioP '##\s+Truapi\s+\K(Web|Desktop|Android|iOS)(?=\s+(?:Chat\s+)?Diagnosis)' \
| head -1 || true)
if [ -z "$mode" ]; then
gh issue comment "$ISSUE" --body \
"Could not read a host from this report (expected a \`## Truapi <Web|Desktop|Android|iOS> Diagnosis\` heading). Not filing a PR."
"Could not read a host from this report (expected a \`## Truapi <Web|Desktop|Android|iOS> [Chat] Diagnosis\` heading). Not filing a PR."
exit 1
fi
modality=spa
if printf '%s' "$BODY" | grep -iqP '##\s+Truapi\s+(Web|Desktop|Android|iOS)\s+Chat\s+Diagnosis'; then
modality=chat
fi
host=$(printf '%s' "$mode" | tr '[:upper:]' '[:lower:]')
branch="diagnosis-report/$host"
file="explorer/diagnosis-reports/$host.md"
branch="diagnosis-report/$modality-$host"
file="explorer/diagnosis-reports/$modality/$host.md"

git fetch origin main
git switch -C "$branch" origin/main
mkdir -p "$(dirname "$file")"
printf '%s\n' "$BODY" > "$file"

git config user.name "github-actions[bot]"
Expand All @@ -64,7 +70,7 @@ jobs:
exit 0
fi
git add "$file"
git commit -m "diagnosis: update $host report (from #$ISSUE)"
git commit -m "diagnosis: update $modality/$host report (from #$ISSUE)"
git push -f "https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git" "$branch"

# One open PR per host branch: reuse it while it is open, open a new
Expand All @@ -73,7 +79,7 @@ jobs:
url=$(gh pr list --head "$branch" --state open --json url --jq '.[0].url // empty')
if [ -z "$url" ]; then
url=$(gh pr create --base main --head "$branch" \
--title "diagnosis: $mode host report" \
--title "diagnosis: $mode $modality report" \
--body "Updates \`$file\` from the diagnosis in #$ISSUE. The compatibility matrix is regenerated from the reports at build time.")
fi

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ jobs:
fi

- if: steps.version.outputs.proceed == 'true'
uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly
uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly-2026-01-10
with:
toolchain: nightly
toolchain: nightly-2026-01-10
components: rustfmt

- if: steps.version.outputs.proceed == 'true'
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ lerna-debug.log*
# Build artifacts
node_modules
target
/artifacts/

# Gradle (Android workspace at repo root)
/.gradle/
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ After regenerating, rebuild the client and refresh the playground's link copy:

```bash
cargo build --workspace
cargo +nightly fmt --check
cargo +nightly-2026-01-10 fmt --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace
```
Expand Down
79 changes: 76 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
# Run `make help` for the list of targets.

.DEFAULT_GOAL := help
.PHONY: help setup build codegen test check clean playground wasm wasm-crypto-test uniffi uniffi-kotlin android-jni android-publish-local check-android-parity dotli-link dev dev-bootstrap dev-link-check e2e-dotli headless install matrix explorer
.PHONY: help setup build codegen test check clean playground wasm wasm-crypto-test uniffi uniffi-kotlin ios-build ios-run ios-chat-run ios-chat-host-playground-run ios-chat-all android-jni android-publish-local check-android-parity dotli-link dev dev-bootstrap dev-link-check e2e-dotli headless install matrix explorer

CARGO ?= cargo
NIGHTLY_TOOLCHAIN ?= nightly-2026-01-10
TRUAPI_PKG := js/packages/truapi
PLAYGROUND := playground
JS_PACKAGES := js/packages
Expand Down Expand Up @@ -41,6 +42,7 @@ setup: ## First-time setup: submodules, JS dependencies, generated artifacts.
# that only exist after codegen.sh, which also builds the packages.
npm ci --ignore-scripts
./scripts/codegen.sh
$(MAKE) uniffi
cd $(PLAYGROUND) && yarn install --frozen-lockfile
cd $(DOTLI) && bun install --frozen-lockfile
$(MAKE) dotli-link
Expand All @@ -63,7 +65,7 @@ install: headless ## Install the truapi-host CLI into Cargo's bin dir; use as `m
cargo install --path rust/crates/truapi-host-cli --bin truapi-host --locked --force

codegen: ## Regenerate generated TS/Rust artifacts from the Rust crates.
./scripts/codegen.sh
TRUAPI_NIGHTLY_TOOLCHAIN="$(NIGHTLY_TOOLCHAIN)" ./scripts/codegen.sh
cd $(PLAYGROUND) && rm -rf node_modules/@parity && yarn install

wasm: ## Rebuild the truapi-server WASM artifacts under js/packages/truapi-host/dist/wasm/.
Expand Down Expand Up @@ -103,6 +105,77 @@ uniffi: ## Regenerate Swift bindings from truapi-server cdylib.
cp $(UNIFFI_SWIFT_TMP)/truapi_serverFFI.modulemap \
ios/truapi-host/Sources/truapi_serverFFI/include/module.modulemap

IOS_HOST := hosts/ios
IOS_DERIVED_DATA ?= $(IOS_HOST)/build/DerivedData
IOS_SIMULATOR_TARGET ?= aarch64-apple-ios-sim
IOS_CONFIGURATION ?= Debug
IOS_SWIFT_FLAGS ?= -DNIGHTLY -DW3S -DIOS_PASEO_E2E
IOS_BUNDLE ?= io.pcf.polkadotapp.develop
IOS_GOOGLE_SERVICE_PLIST ?= $(IOS_HOST)/polkadot-app/GoogleService/GoogleService-Info-Release.plist
IOS_PRODUCT_HOST ?= truapi-playground.dot
IOS_PRODUCT_URL ?= http://localhost:3100
IOS_CHAT_PRODUCT_DIR ?= playground
IOS_CHAT_PRODUCT_HOST ?= truapi-playground.dot
IOS_CHAT_PRODUCT_NAME ?= TrUAPI Playground
IOS_CHAT_PRODUCT_URL ?= http://127.0.0.1:3100
IOS_HOST_PLAYGROUND_DIR ?= ../host-playground
IOS_HOST_PLAYGROUND_HOST ?= host-playground.dot
IOS_HOST_PLAYGROUND_NAME ?= Host Playground
IOS_HOST_PLAYGROUND_URL ?= http://127.0.0.1:3101
IOS_APP := $(abspath $(IOS_DERIVED_DATA)/Build/Products/$(IOS_CONFIGURATION)-iphonesimulator/polkadot-app.app)

ios-build: uniffi ## Build matching Rust/Swift bindings and the TestFlight-configured iOS simulator app.
git submodule update --init --recursive $(IOS_HOST)
rustup target add $(IOS_SIMULATOR_TARGET)
$(CARGO) build -p truapi-server --release --features ws-bridge \
--target $(IOS_SIMULATOR_TARGET)
cd $(IOS_HOST) && RUN_IN_CI=true xcodebuild \
-project polkadot-app.xcodeproj \
-scheme polkadot-app \
-configuration $(IOS_CONFIGURATION) \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath $(abspath $(IOS_DERIVED_DATA)) \
ARCHS=arm64 \
ONLY_ACTIVE_ARCH=YES \
TRUAPI_SWIFT_FLAGS='$(IOS_SWIFT_FLAGS)' \
build
cp "$(IOS_GOOGLE_SERVICE_PLIST)" "$(IOS_APP)/GoogleService-Info.plist"
codesign --force --sign - "$(IOS_APP)"

ios-run: ios-build ## Build and launch the local TrUAPI playground in an iPhone simulator.
TRUAPI_IOS_E2E_APP="$(IOS_APP)" \
TRUAPI_IOS_E2E_BUNDLE="$(IOS_BUNDLE)" \
TRUAPI_IOS_E2E_PRODUCT_HOST="$(IOS_PRODUCT_HOST)" \
TRUAPI_IOS_E2E_PRODUCT_URL="$(IOS_PRODUCT_URL)" \
node scripts/launch-ios-playground.mjs

ios-chat-run: ios-build ## Run the TrUAPI Playground Chat diagnosis in an iPhone simulator.
TRUAPI_IOS_E2E_APP="$(IOS_APP)" \
TRUAPI_IOS_E2E_BUNDLE="$(IOS_BUNDLE)" \
TRUAPI_IOS_E2E_CHAT_PRODUCT_DIR="$(IOS_CHAT_PRODUCT_DIR)" \
TRUAPI_IOS_E2E_CHAT_PRODUCT_HOST="$(IOS_CHAT_PRODUCT_HOST)" \
TRUAPI_IOS_E2E_CHAT_PRODUCT_NAME="$(IOS_CHAT_PRODUCT_NAME)" \
TRUAPI_IOS_E2E_CHAT_PRODUCT_URL="$(IOS_CHAT_PRODUCT_URL)" \
node scripts/launch-ios-chat-playground.mjs

ios-chat-host-playground-run: ios-build ## Verify Host Playground Chat through the workspace-linked TrUAPI client.
TRUAPI_IOS_E2E_APP="$(IOS_APP)" \
TRUAPI_IOS_E2E_BUNDLE="$(IOS_BUNDLE)" \
TRUAPI_IOS_E2E_CHAT_PRODUCT_DIR="$(abspath $(IOS_HOST_PLAYGROUND_DIR))" \
TRUAPI_IOS_E2E_CHAT_PRODUCT_HOST="$(IOS_HOST_PLAYGROUND_HOST)" \
TRUAPI_IOS_E2E_CHAT_PRODUCT_NAME="$(IOS_HOST_PLAYGROUND_NAME)" \
TRUAPI_IOS_E2E_CHAT_PRODUCT_URL="$(IOS_HOST_PLAYGROUND_URL)" \
TRUAPI_IOS_E2E_CHAT_ROOM_ID="host-playground-room" \
TRUAPI_IOS_E2E_CHAT_MESSAGE="!echo hello" \
TRUAPI_IOS_E2E_CHAT_DIAGNOSIS="0" \
TRUAPI_IOS_E2E_CHAT_EXPECTED_STARTUP_MESSAGE="" \
TRUAPI_IOS_E2E_CHAT_EXPECT_CUSTOM_RENDERER="0" \
TRUAPI_IOS_E2E_CHAT_SCREENSHOT="artifacts/host-playground-chat.png" \
TRUAPI_IOS_E2E_CHAT_TRUAPI_DIR="$(abspath js/packages/truapi)" \
node scripts/launch-ios-chat-playground.mjs

ios-chat-all: ios-chat-run ios-chat-host-playground-run ## Run both local iOS Chat playground integrations.

UNIFFI_KOTLIN_OUT := android/truapi-host/src/main/kotlin/generated

uniffi-kotlin: ## Regenerate Kotlin UniFFI bindings from the truapi-server cdylib.
Expand Down Expand Up @@ -136,7 +209,7 @@ test: ## Run Rust + TypeScript client tests.
check: ## Full verification suite (build, fmt, clippy, test, TS tests, playground build + lint).
cargo build --workspace
cargo check --target wasm32-unknown-unknown -p truapi-server
cargo +nightly fmt --check
cargo +$(NIGHTLY_TOOLCHAIN) fmt --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features --all-targets
cd $(TRUAPI_PKG) && npm run build && npm test
Expand Down
61 changes: 59 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ controls, and examples.

`scripts/battery.sh` drives that CLI from source over every code-generated
example and writes both committed compatibility reports:
`explorer/diagnosis-reports/signing-host-cli.md` from a direct signing-host run,
and `pairing-host-cli.md` from a pairing host that the script pairs with a
`explorer/diagnosis-reports/spa/signing-host-cli.md` from a direct signing-host
run, and `spa/pairing-host-cli.md` from a pairing host that the script pairs with a
signing host it starts itself.

```bash
Expand All @@ -139,6 +139,63 @@ yarn dev

Open `https://dot.li/localhost:3000` inside the Polkadot Desktop Host. See [`playground/README.md`](playground/README.md) for deployment.

To build the iOS host and open the playground in Simulator:

```bash
make ios-run
```

The target regenerates the UniFFI Swift bindings, builds the matching Rust
simulator library, and builds the `hosts/ios` app with the Nightly feature
flags and release Firebase app used by the Nightly TestFlight build. Native
Chat and the Paseo chain catalog come from the same Nightly Remote Config as
TestFlight. The executable keeps the development bundle and app-group identity
so Simulator can reuse its already registered wallet; keychain data cannot be
transferred to the production bundle. The simulator also adds the
`IOS_PASEO_E2E` conveniences needed to start on the real `browse.dot` Browse tab
and activate the embedded signing host. Embedded product host sessions use the
same Paseo People and Bulletin chains selected by the Nightly app
configuration. The launcher starts the playground at `http://localhost:3100`
when needed and uses that local source only after Browse opens
`truapi-playground.dot`. It refuses to launch if the local URL belongs to a
different app. Override the product, URL, or simulator with `IOS_PRODUCT_HOST`,
`IOS_PRODUCT_URL`, or `TRUAPI_IOS_E2E_DEVICE`. The simulator launch reuses the
wallet and registered username already stored by the iOS app. Opening a product
activates the embedded `truapi-host` signing-host session from that wallet; it
does not provision or pair a signer-bot user.

To exercise the shared-core Chat path with the first-party TrUAPI Playground
worker, build and serve the local product, install its worker into the
simulator app's product storage, and open its native Chat application:

```bash
make ios-chat-run
```

The launcher verifies the Chat connection and runs a correlated Chat-only
diagnosis. The worker proves create-room idempotency, observes the new room on
the live list subscription, posts text and custom messages, receives
`!diagnose` through `chat_action_subscribe`, and serves live renderer trees.
The launcher also verifies that a renderer update reaches native code and that
the final Markdown report reaches CoreData. It writes the host-labelled report
to `playground/test-results/ios-chat/diagnosis-report.md`. The product builds
against the workspace-linked `@parity/truapi`. Override the product source,
identity, SPA URL, room, input, or report path with
`IOS_CHAT_PRODUCT_DIR`, `IOS_CHAT_PRODUCT_HOST`, `IOS_CHAT_PRODUCT_URL`,
`TRUAPI_IOS_E2E_CHAT_ROOM_ID`, `TRUAPI_IOS_E2E_CHAT_MESSAGE`, or
`TRUAPI_IOS_E2E_CHAT_REPORT`.

The same harness can run the legacy Product SDK worker from the sibling
`host-playground` checkout. This target builds the current TrUAPI client, links
it over Host Playground's transitive `@parity/truapi`, then builds and runs
that product:

```bash
make ios-chat-host-playground-run
```

Run both integrations with one iOS build using `make ios-chat-all`.

## Regenerate the TypeScript client

When the Rust trait surface changes:
Expand Down
Loading