Skip to content

Port the dot.li web host to the shared TrUAPI Rust core#54

Draft
pgherveou wants to merge 85 commits into
mainfrom
worktree-issue-96-rust-core-port
Draft

Port the dot.li web host to the shared TrUAPI Rust core#54
pgherveou wants to merge 85 commits into
mainfrom
worktree-issue-96-rust-core-port

Conversation

@pgherveou

@pgherveou pgherveou commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Port the dot.li web host to the shared TrUAPI Rust core

This moves dot.li off the @novasamatech/host-api stack and onto the shared TrUAPI Rust core. Protocol logic that dot.li used to hand-maintain in TypeScript now lives once in truapi-server (Rust → WASM), and the web host is reduced to a thin transport bridge plus a small set of typed platform callbacks.

Architecture

A product iframe speaks TrUAPI over a MessageChannel. The host page forwards those SCALE frames to a Web Worker running the truapi-server WASM core; it never decodes or interprets the protocol. The core executes all protocol logic and, when it needs an OS primitive, calls back out through typed truapi-platform callbacks the host implements.

Product iframe ──MessageChannel──▶ host bridge (byte pipe) ──▶ Web Worker (truapi-server WASM)
                                                                      │ typed callbacks
                                                                      ▼
                                                        dot.li platform capabilities
  • Core owns: wire framing, dispatch, subscriptions, permission gating, the auth/SSO state machine, session interpretation, signing orchestration, and the chainHead runtime.
  • Host owns: storage, navigation, permission/confirmation UI, chain RPC transport, theme, preimage backend, and product identity/pairing config.

This is the same core that the native (iOS/Android) hosts embed over UniFFI, so protocol behavior stays identical across web and native and wire formats cannot drift between hosts.

What changes in @dotli/ui

  • Removed: container.ts and statement-store-mapping.ts (the hand-written protocol dispatch, codecs, subscriptions, permission policy, statement mapping, and DOTNS parsing), plus the @novasamatech/host-api, host-container, sdk-statement, and statement-store dependencies.
  • Added: a host-callbacks/ module with one file per capability trait (LocalStorage, OpenUrl, PushNotification, PromptPermission, Chain, Theme, SessionStore, UserConfirmation, Preimage, …), backed by @parity/truapi-host-wasm and @parity/truapi.
  • Rewritten: bridge.ts now starts the worker-backed core and wires the product iframe to it.

The host callbacks are typed: the host implements the generated HostCallbacks interface directly, with no hand-rolled bytes↔types adapter. SCALE stays on the product↔core wire (the sandboxed-dApp contract); the core↔host callback boundary carries native typed values.

Permission decisions are core-owned state. The dot.li topbar admin screen reads and writes grants through the core rather than a parallel localStorage store; dot.li keeps only the browser-specific projection (iframe allow attribute and reload behavior).

Scope

In scope: the protocol-layer swap described above (delete the JS protocol stack, add the typed host-callbacks/, rewrite the bridge, swap dependencies).

Deferred to follow-up issues: the SharedWorker topology (today the core runs in per-tab dedicated Web Workers, one per core instance); eliminating the per-CID *.app.dot.li sandbox iframe; and moving content fetching into Rust, including the host-side preimage backend behind the PreimageHost callback.

Related

Inventory of the host-API boundary and the slim Phase-A callback surface
the dotli refactor lands on, plus a status section describing the
current bridge.ts + host-callbacks/ layout for onboarding.
Adds the three TrUAPI host packages (@truapi/client, @truapi/host-shared,
@truapi/host-web) as file: workspace deps, alongside the existing
novasamatech ones (those are dropped in a follow-up). Vite needs an
explicit server.fs.allow entry rooted at the truapi monorepo so the
file: dependency resolution can serve sources from outside the dotli
package tree.
Concrete WasmHostCallbacks impls grouped per RFC domain. Each file
owns the bridge between TrUAPI's wire types (Uint8Array Hex, tagged
unions) and the dotli primitives the syscall lands on:

- Account / Signing  → @novasamatech/host-papp session ops (D1 retires)
- Chain              → smoldot or RPC providers (E1 retires)
- LocalStorage       → window.localStorage
- OpenUrl            → window.open
- Preimage           → Helia (D2 retires)
- PromptPermission   → permission-modal + permissions.ts cache
- PushNotification   → Notification API
- StatementStore     → sdk-statement / statement-store via @dotli/auth
- handlers.ts composes them into a single CreateHostCallbacks impl

@dotli/auth re-exports the novasamatech statement types so consumers
don't take direct deps on those SDKs.
Replaces the @novasamatech/host-container setupContainer call with
@truapi/host-web's createIframeHost. The new bridge:

- dynamically imports @truapi/host-web and the worker entrypoint
  @truapi/host-shared/dist/worker-runtime.js?worker
- spawns a Web Worker that owns the truapi-server WASM core, so
  smoldot's CPU and JSON-RPC handling no longer block the page main
  thread (was a steady stream of '[Violation] message handler took
  150ms+' warnings)
- calls createWebWorkerHostRuntime(new HostWorker(), callbacks)
- supplies the dotli callback set from packages/ui/src/host-callbacks/
- calls createIframeHost(...) with the product URL, sandbox policy,
  and allowed origin

Nested dApp-in-dApp bridging (the old setupNestedBridgeDetector) is
dropped; createIframeHost owns its own MessageChannel and does not
yet expose an attachNested API. Tracked as a known regression in the
parent refactor plan; the workaround lives in C1.
Both files implemented the host side of @novasamatech/host-container
+ @novasamatech/sdk-statement. Their responsibilities now live in
the Rust core (truapi-server: routing, codecs, subscriptions,
permissions service, statement <-> sub_store mapping) and the
host-callbacks/ syscall layer for the irreducible OS bits.

No callers remain in dotli — bridge.ts and render.ts went through
host-callbacks/ in the previous commit.
…tore}

The host-API protocol stack moved into the TrUAPI Rust core through
@truapi/host-shared + @truapi/host-web in the previous commits. The
four novasamatech packages are no longer imported anywhere in dotli
and can come out of the dependency tree.

@novasamatech/host-papp is kept — it still owns wallet pairing and
session-bound signing, both retired by D1 (Rust-native wallet pairing)
in a follow-up.
Four files imported types from it after B5: `toHex`, `SigningErr`,
`CodecType`, and the `DevicePermission` codec. All four have local or
@truapi/client equivalents now.

- @dotli/shared/hex.ts (new): pure-JS toHex / fromHex, no @noble/hashes.
- @dotli/auth/{auth,signing}.ts: swap toHex for the new helper.
- @dotli/auth/signing.ts: replace SigningErr.{Rejected,Unknown} with
  plain Error. The wasm bridge stringifies rejected callbacks via
  js_to_string regardless of class identity, so nothing is lost on
  the wire.
- @dotli/ui/permissions.ts: use DevicePermission["tag"] from
  @truapi/client instead of CodecType<typeof DevicePermissionCodec>.
- @dotli/ui/host-callbacks/PromptPermission.ts: delete the
  toDevicePermissionName casing shim — the Rust trait was renamed
  Nfc -> NFC upstream and the regenerated codec emits matching
  strings.

Wave 1 of host-papp retirement; pgherveou/truapi#20.
@socket-security

socket-security Bot commented Jul 1, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant