Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
97 changes: 97 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 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;
- adapts typed dotli callbacks with `createWasmRawCallbacks(...)`;
- calls `createWebWorkerProvider(new HostWorker(), rawCallbacks, { runtimeConfig })`;
- supplies callbacks from `packages/ui/src/host-callbacks/handlers.ts`;
- calls `createIframeHost(...)` with the product URL, sandbox policy, allowed
origin, and the worker-backed provider.

Product frames enter the Rust core through the iframe `MessageChannel`.
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.4.0",
"@parity/truapi-host": "0.1.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 @@ -59,17 +59,30 @@ RATE_LIMITED_ENVS := paseo dev-test
# 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
34 changes: 26 additions & 8 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 |
| `signPayload` / `signRaw` | Shows signing modals and routes signing through the active session |
| `chainConnection` | Returns a smoldot-backed JsonRpcProvider for supported chains |
| `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,6 +154,26 @@ bun install
bun run preview # Build + serve both apps on localhost:5173
```

The TrUAPI packages are installed from temporary npm aliases while the Rust core
port is in review. 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
Expand All @@ -174,7 +192,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