Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
5b9f22e
refactor(host): adopt Rust TrUAPI runtime
pgherveou Jul 15, 2026
9b29b7b
test(ui): wait for bridge lifecycle events
pgherveou Jul 15, 2026
2864c01
test(ui): allow cold topbar import
pgherveou Jul 15, 2026
95b810c
fix(ui): preserve notification requests
pgherveou Jul 15, 2026
e8915f0
docs(shared): remove stale auth reference
pgherveou Jul 15, 2026
44b880c
refactor(protocol): unify auth storage keys
pgherveou Jul 15, 2026
6e8dc55
refactor(protocol): defer chainSend flush error responses to a follow-up
pgherveou Jul 15, 2026
d2c57a1
refactor(resolver): defer dependency pruning and coverage tests to a …
pgherveou Jul 15, 2026
984f073
refactor(debug): defer panel fixes unrelated to the port to a follow-up
pgherveou Jul 15, 2026
98690c0
refactor(ui): defer permission-prompt rate limiting to a follow-up
pgherveou Jul 15, 2026
6c01473
refactor(ui): defer allowance-key at-rest encryption to a follow-up
pgherveou Jul 15, 2026
c6a1d94
refactor(bridge): defer the per-frame wire debug tap to a follow-up
pgherveou Jul 15, 2026
cf99551
refactor(topbar): defer login-failure copy and popover hardening to a…
pgherveou Jul 15, 2026
963ac83
build(ui): depend on published @parity/truapi 0.4.0 and @parity/truap…
pgherveou Jul 15, 2026
0bf87e5
build: link local TrUAPI packages
pgherveou Jul 15, 2026
95e8bac
refactor(config): keep base formatting for unchanged gateway helpers
pgherveou Jul 15, 2026
d1640a1
fix(ui): translate legacy Nova follow ids
pgherveou Jul 16, 2026
9ec38d0
fix(ui): isolate opened navigation tabs
pgherveou Jul 17, 2026
6dea057
fix(ui): avoid duplicate permission rows
pgherveou Jul 17, 2026
c35f944
fix(ui): reject login when core closes
pgherveou Jul 17, 2026
26a93be
test(ui): cover confirmation variants
pgherveou Jul 17, 2026
8f0c9cd
fix(ui): pin legacy bridge origin
pgherveou Jul 17, 2026
4493ea3
fix(ui): verify cached preimages
pgherveou Jul 17, 2026
29d89b3
fix(ui): handle ring proof reviews
pgherveou Jul 17, 2026
b93d9a0
chore: merge main into Rust core port
pgherveou Jul 17, 2026
f7d372f
fix(ui): support published confirmation types
pgherveou Jul 17, 2026
ed783fc
fix(ui): retain legacy alias shape
pgherveou Jul 17, 2026
37a6f54
build(ui): update TruAPI to 0.4.1
pgherveou Jul 17, 2026
9648cf6
test(e2e): support local host playground
pgherveou Jul 20, 2026
7c27048
update with temporary packages
pgherveou Jul 20, 2026
be0092a
merge
pgherveou Jul 20, 2026
b570793
fix bridge
pgherveou Jul 20, 2026
b407af4
update package deps
pgherveou Jul 20, 2026
0fb0672
fix modal
pgherveou Jul 20, 2026
9667cbf
fix(metrics): keep event callbacks intact
pgherveou Jul 20, 2026
2840b45
fix(ui): normalize local legacy accounts
pgherveou Jul 20, 2026
7921ce4
fix(ui): address core migration regressions
pgherveou Jul 20, 2026
3d26792
update lock files
pgherveou Jul 21, 2026
66ace10
remove nova-removal.test.ts
pgherveou Jul 21, 2026
4730658
refactor tests
pgherveou Jul 21, 2026
90d4974
fix(deps): update vulnerable transitive deps
pgherveou Jul 21, 2026
5de0ee5
fix(ui): broker smoldot core connections
pgherveou Jul 22, 2026
0258973
fix(deps): update fast-uri
pgherveou Jul 22, 2026
8638264
Merge remote-tracking branch 'origin/main' into codex/bulletin-preima…
pgherveou Jul 22, 2026
1edd74e
fix
pgherveou Jul 23, 2026
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
4 changes: 0 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ VITE_METRICS=
# build time (see packages/sandbox-checker). Leave unset for normal builds.
VITE_SANDBOX_CHECKER=

# Set to "true" to use the smoldot light client for the statement store
# chain. Defaults to false; enable in development for testing and feedback.
VITE_SS_USE_SMOLDOT=

# Optional relay chain spec override for the statement store people chain.
# Chain-spec file name without `.json`, e.g. "westend-local". When unset,
# the default Paseo relay chain is reused.
Expand Down
6 changes: 0 additions & 6 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,6 @@ github-actions:
- 'packages/content/**'
- '!packages/content/package.json'

"pkg: auth":
- changed-files:
- all-globs-to-any-file:
- 'packages/auth/**'
- '!packages/auth/package.json'

"pkg: ui":
- changed-files:
- all-globs-to-any-file:
Expand Down
99 changes: 99 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# dotli host layer migration to TrUAPI

This document records the current dotli host cutover state. The old
`@novasamatech/*` host-container path is no longer part of the runtime design:
dotli launches products through the Rust-backed TrUAPI host bridge and keeps
only its own `.dot` resolution/smoldot protocol outside that boundary.

## Current integration

dotli still has two protocol layers:

1. Host-product API frames, handled by TrUAPI.
2. dotli's internal host-smoldot protocol in `packages/protocol/`, which owns
`.dot` resolution and is not replaced by TrUAPI.

The active launch path is `packages/ui/src/bridge.ts`:

- imports `@parity/truapi-host/web` and
`@parity/truapi-host/worker-runtime?worker`;
- starts a Web Worker that owns the `truapi-server` WASM core;
- creates the worker runtime with `createWebWorkerPairingHostRuntime(...)`;
- supplies typed callbacks from `packages/ui/src/host-callbacks/handlers.ts`;
- creates a product-scoped provider with `runtime.createProvider({ productId })`;
- calls `createIframeHost(...)` with the product URL, sandbox policy, allowed
origin, and the worker-backed provider.

Modern product frames enter the Rust core through the iframe `MessageChannel`.
The temporary Nova compatibility shim forwards legacy `window.postMessage`
frames into the same product-scoped provider.
Account, signing, statement-store, SSO pairing, restore, and logout are
core-owned and do not cross the JS host callback boundary as Nova-specific
routes.

## dotli callback boundary

dotli still supplies platform primitives that are host policy or browser UI:

| Area | Files |
| ------------------------- | -------------------------------------------------------------------------------------- |
| navigation | `packages/ui/src/host-callbacks/OpenUrl.ts` |
| notifications | `packages/ui/src/host-callbacks/PushNotification.ts` |
| device/remote permissions | `packages/ui/src/host-callbacks/PromptPermission.ts`, `packages/ui/src/permissions.ts` |
| feature support | `packages/ui/src/host-callbacks/FeatureSupported.ts` |
| product storage | `packages/ui/src/host-callbacks/LocalStorage.ts` |
| session persistence | `packages/ui/src/host-callbacks/SessionStore.ts` |
| pairing presentation | `packages/ui/src/host-callbacks/AuthState.ts` |
| user confirmations | `packages/ui/src/host-callbacks/UserConfirmation.ts` |
| preimage lookup UI | `packages/ui/src/host-callbacks/Preimage.ts` |
| theme subscription | `packages/ui/src/host-callbacks/Theme.ts` |
| chain backend connection | `packages/ui/src/host-callbacks/Chain.ts` |

These callbacks must remain generic host primitives. They should not re-add
Nova package imports, wallet-specific JS channels, or statement-store request
routing that belongs inside the Rust core.

## Dependencies

`packages/ui/package.json` depends on the published TrUAPI packages:

```jsonc
"@parity/truapi": "0.5.0",
"@parity/truapi-host": "0.2.0"
```

Standalone dotli installs and CI exercise these npm artifacts. TrUAPI's
development and E2E Make targets run `bun run link:truapi` after building the
local packages, so parent-repo work always exercises the current checkout
instead of the npm artifacts.

The runtime manifests and source must not depend on or import the removed
`@novasamatech/*` runtime packages.

## Nested dApps

The old `setupNestedBridgeDetector` path created extra JS bridges for nested
iframe sources. TrUAPI v1 does not create separate nested Rust runtimes,
sessions, product identities, or storage namespaces. If nested traffic is
forwarded later, it must use the shared top-level Rust core/provider context.

The future usefulness of independent nested-product semantics is tracked in the
parent TrUAPI design note `docs/design/host-contract-and-core-impl/I -
nested-dapps.md`.

## Non-TrUAPI dotli layers

These areas remain dotli-owned and are intentionally outside the host-product
replacement:

- `apps/host/src/main.ts`: landing shell, `.dot` resolution, iframe mounting.
- `apps/sandbox/src/main.ts`: content renderer.
- `packages/protocol/`: smoldot protocol client/broker/session plumbing.
- `packages/resolver/`: `.dot` name resolution.
- `packages/content/` and `packages/storage/`: archive fetch and cache.
- `packages/config/`, `packages/metrics/`, and `packages/shared/`: support
libraries.

When auditing the migration, treat Nova runtime imports/dependencies as a
regression, but do not confuse dotli's internal `packages/protocol/` with the
removed host-product protocol.
17 changes: 15 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,30 @@ SENTRY_PROJECT := $(lastword $(subst /, ,$(_sentry_hostpath)))
# Default env when none is passed on the command line.
ENV ?= paseo

# When this repo is checked out as truapi/hosts/dotli, local make builds should
# consume the checked-out TrUAPI packages instead of the temporary npm aliases.
TRUAPI_REPO ?= $(abspath ../..)
TRUAPI_LOCAL_PACKAGE := $(TRUAPI_REPO)/js/packages/truapi/package.json
TRUAPI_HOST_LOCAL_PACKAGE := $(TRUAPI_REPO)/js/packages/truapi-host/package.json

# Packages required on a fresh Ubuntu 22.04+ box. The brotli module is split
# across two packages on noble (filter + static) and both ship a drop-in in
# /etc/nginx/modules-enabled/ so they auto-load. curl and ca-certificates
# back certbot's API calls.
APT_PACKAGES := nginx libnginx-mod-http-brotli-filter libnginx-mod-http-brotli-static certbot python3-certbot-dns-cloudflare rsync ufw curl ca-certificates

.PHONY: build provision provision-prereqs provision-firewall provision-cloudflare-creds provision-cert provision-renewal deploy ci-deploy deploy-nginx render-nginx _require-env _require-env-name
.PHONY: build link-truapi-local provision provision-prereqs provision-firewall provision-cloudflare-creds provision-cert provision-renewal deploy ci-deploy deploy-nginx render-nginx _require-env _require-env-name

build:
build: link-truapi-local
bun run build

link-truapi-local:
@if [ -f "$(TRUAPI_LOCAL_PACKAGE)" ] && [ -f "$(TRUAPI_HOST_LOCAL_PACKAGE)" ]; then \
TRUAPI_REPO="$(TRUAPI_REPO)" bun run link:truapi; \
else \
echo "No local TrUAPI checkout found at $(TRUAPI_REPO); using package manager dependencies."; \
fi

# ====================================================================
# Fresh-server provisioning. Idempotent; safe to re-run.
#
Expand Down
70 changes: 61 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Each product gets its own `<label>.app.paseo.li` origin, so versions of the same

1. **Resolves** `.dot` names via an in-browser [smoldot](https://github.com/paritytech/smoldot) light client connected to Asset Hub Paseo, querying dotNS contracts.
2. **Fetches** content from the [Bulletin Chain](https://github.com/paritytech/polkadot-bulletin-chain) via smoldot `bitswap_v1_get` JSON-RPC or an IPFS gateway.
3. **Renders** the content in a sandboxed iframe with a full host-container bridge, so loaded SPAs can request accounts, sign transactions, connect to chains, and use scoped storage.
3. **Renders** the content in a sandboxed iframe with the Rust-backed TrUAPI bridge, so loaded SPAs can request accounts, sign transactions, connect to chains, and use scoped storage.

```
host-playground.paseo.li
Expand Down Expand Up @@ -116,26 +116,24 @@ On repeat visits, content renders instantly from the cache while it is resolved

If a background re-resolution finds the on-chain CID has changed, dotli shows a **New version available** notification with a **Reload** action rather than swapping content silently.

## Host-container bridge
## TrUAPI bridge

Loaded SPAs communicate with dotli through a postMessage-based protocol. The bridge exposes:

| Handler | What it does |
| ------------------------------ | ---------------------------------------------------------------------- |
| `accountGet` | Derives a per-app public key via HDKD soft derivation |
| `getLegacyAccounts` | Returns non-derived (imported) accounts — always empty on the web host |
| `signPayload` / `signRaw` | Shows signing modals, delegates to host-papp session |
| `chainConnection` | Returns a smoldot-backed JsonRpcProvider for supported chains |
| `signPayload` / `signRaw` | Shows signing modals and routes signing through the active session |
| `chainConnection` | Returns an isolated broker connection over the selected chain backend |
| `localStorageRead/Write/Clear` | Scoped `localStorage` per `.dot` domain |
| `navigateTo` | Opens URLs in new tabs |
| `featureSupported` | Reports whether a feature is supported (e.g. a chain's genesis hash) |
| `connectionStatus` | Streams auth state changes to the SPA |

### Nested dApp support
### App iframe model

dApps can embed other dApps via iframes (e.g. a marketplace app embedding a payments app). The host automatically detects nested dApps and creates separate bridges for each one, regardless of nesting depth.

When the host receives a protocol message from an unknown iframe window, it dynamically creates a new container bridge targeting that window. The dApp SDK always sends to `window.top`, so all nested dApps communicate directly with the host — no relay needed.
The host creates one TrUAPI bridge for the rendered product iframe. dApp-in-dApp iframes are opaque to the host and must use the top-level product's shared Rust core/provider context rather than separate host-created bridges.

The app context uses `document.write()` to eliminate extra iframe nesting: when loaded inside a host iframe, the app replaces its own document with the dApp content so the dApp occupies the iframe directly.

Expand All @@ -156,10 +154,64 @@ bun install
bun run preview # Build + serve both apps on localhost:5173
```

The TrUAPI packages are installed from their published `@parity` packages. To
iterate against a local truapi checkout instead, run:

```bash
bun run link:truapi
```

When dotli is not checked out under `truapi/hosts/dotli`, point the script at
the truapi repo:

```bash
TRUAPI_REPO=/path/to/truapi bun run link:truapi
```

Return to the package versions recorded in `bun.lock` with:

```bash
bun run unlink:truapi
```

Local development uses wildcard subdomains:

- `host-playground.localhost:5173` — resolves `host-playground.dot` via the host

### Running the host-playground E2E locally

The product E2E suite can load the source checkout directly through dotli's
localhost proxy instead of resolving the published `host-playground.dot` CID.
By default it expects the product at `../../../host-playground` relative to this
repository and the local signing bot at `http://localhost:3737/`:

```bash
bun run test:e2e:local
```

Override either checkout or server when needed:

```bash
E2E_PRODUCT_REPO=/path/to/host-playground \
E2E_PRODUCT_URL=http://localhost:5199 \
bun run test:e2e:local
```

The suite defaults to `rpc-gateway`. Set `E2E_CHAIN_BACKEND` to run the same
flow through either light-client backend:

```bash
E2E_CHAIN_BACKEND=smoldot-shared-worker bun run test:e2e:local
```

The signer can run natively or in Docker; set `SIGNER_BOT_BASE_URL`,
`SIGNER_BOT_SVC_TOKEN`, and `SIGNER_BOT_NETWORK` when its published port or
credentials differ from the defaults.

The command builds dotli with its debug-only localhost proxy enabled, starts
both preview servers through Playwright, pairs once through the signer bot, and
runs the same host-product suite used in CI.

### Running an approved build

Releases are published as GitHub Releases tagged `vX.Y.Z` (the latest published tag is what the hosted dotli deployment runs). To reproduce a specific approved version from a fresh checkout:
Expand All @@ -174,7 +226,7 @@ The published tag on the [Releases page](https://github.com/paritytech/dotli/rel

## Debug panel

dotli ships a TrUAPI debug panel that aggregates host-side activity (boot/resolve/render/bridge events, TrUAPI host↔product messages, host-papp SSO/session events) into one time-aligned inspector. The panel chunk is dynamically imported, so users who never see it pay no download cost.
dot.li ships a TrUAPI debug panel that aggregates host-side activity (boot/resolve/render/bridge events, TrUAPI host↔product messages, SSO/session events) into one time-aligned inspector. The panel chunk is dynamically imported, so users who never see it pay no download cost.

In builds compiled with `VITE_APP_DEBUG=true` (local `bun run preview:debug`, and the staging dev deploys at `paseoli.dev` / `dotli.dev`) the panel auto-mounts collapsed. In staging/production it's off until you click **Open in debug mode** in the host Settings menu (or append `?debug=true` to any URL). The choice is sessionStorage-scoped — closing the tab clears it. Use `?debug=off` to silence it explicitly within the same session.

Expand Down
Loading
Loading