Centralize chain provider ownership#97
Draft
pgherveou wants to merge 47 commits into
Draft
Conversation
Replace the Nova host-container, auth, and signing path with the worker-backed Rust core. Keep dotli responsible only for browser policy, persistence, UI, and physical chain transports.
Notification grants do not alter iframe Permissions Policy. Reloading disposed the in-flight Rust request before its response reached the product.
The pending-message flush drops send failures silently, matching the pre-port behavior. Surfacing them as JSON-RPC errors moves to a follow-up PR with test coverage.
…follow-up
Keep @polkadot-api/signer and @polkadot-labs/hdkd{,-helpers} declared
(unused since before the port) and limit rpc-chain tests to the new
core gateway provider surface. Removing the dead deps and adding
coverage for pre-existing chain checks moves to a follow-up PR.
Keep the base panel top offset and suffix-based terminator matching (minus the removed host-papp events). The offset correction, the exact-match terminator set, and the @dotli/config dependency declaration move to a follow-up PR.
Prompts always reach the modal, matching the pre-port behavior. The sliding-window limiter returns in a follow-up PR together with the denied-path semantics and a test that trips the window.
AllowanceKeys persist as plain hex through the same path as every other core storage key. The at-rest cipher moves to a follow-up PR where the scheme (per-write nonce, plain-hex read fallback) can get a focused security review.
The bridge emits only the first_inbound/first_outbound lifecycle events. The per-frame TrUAPI tap that feeds the debug panel timeline returns in a follow-up PR; the panel's truapi event handling stays in place and receives no events until then.
… follow-up Login failures fall back to the raw reason except for the base OriginPersonProviderError mapping, and the permissions popover renders last-write-wins without a staleness guard or unavailable-state hint. The failure-message pack and popover hardening move to a follow-up PR.
…i-host 0.1.0 The published packages replace the temporary personal-scope npm aliases used while the port was in review.
bun run link:truapi replaces the two installed TrUAPI npm packages with symlinks to a sibling TrUAPI checkout, and bun run unlink:truapi runs a forced frozen install to restore the declared packages. Package resolution is validated after linking so local E2E cannot silently exercise a stale npm build. The parent truapi repo's make e2e-dotli target depends on this script via make dotli-link.
getActiveGatewayChains and its doc comment match the pre-port source; only the core gateway set is new.
Nova exposes synthetic follow ids to legacy products, while the Rust core requires the subscription wire request id. Translate only at the legacy window boundary so existing product-sdk apps keep working without weakening core validation.
All OpenUrl requests are normalized and scheme-validated by the Rust core before they reach this browser callback, so duplicating that policy in TypeScript would create a second allowlist that can drift. Add the noopener window feature to every routing branch instead. This prevents an allowed destination from retaining window.opener access to the dotli host while preserving the existing dot-domain, localhost, and external URL routing behavior. Cover all three branches with a regression test that verifies each new tab is opened with noopener.
Changing a permission dispatched a synchronous permission-changed event whose listener started rendering the open popover, then the dropdown success path started a second render directly. Both asynchronous renders appended their rows after the same clear, doubling every entry. Remove only the redundant success-path render and keep the catch-path render for recovery. The permission event remains the single successful refresh trigger for both device and non-device permissions. Add a regression test that changes a dropdown and asserts the popover still contains exactly one row per permission.
The topbar login promise previously listened only for response frames. If the core provider closed during pairing, no response could arrive and the pending promise retained its message subscription indefinitely. Subscribe to provider close events and reject with the transport error. Centralize settlement and cleanup so message responses, malformed frames, send failures, and provider closure all unsubscribe both listeners exactly once. The cleanup also handles the provider contract where an already-closed provider invokes the close callback during subscription. Add a regression test that closes a pending provider and verifies rejection plus listener cleanup.
The confirmation adapter already rendered and resolved the newer review variants, but its suite exercised only legacy payload signing and omitted transaction creation and the dedicated preimage flow. Add product-account payload and transaction assertions so account formatting and transaction fields are locked down. Add both allow and cancel cases for preimage submission to verify that the dedicated modal maps user intent to the boolean host response. This intentionally changes tests only: the existing production behavior satisfies the review request once these untested branches are covered.
The legacy window transport accepted inbound frames based only on Window identity and sent outbound frames with a wildcard target. The iframe keeps its real origin because its sandbox includes allow-same-origin; credentialless mode does not make that origin opaque. Require the resolved product origin for inbound provider frames, use it as the outbound postMessage target, and enforce it on the initial modern-versus-legacy probe before replaying the first frame. This closes the wildcard path without changing the legacy Nova wire compatibility layer. Add coverage for the exact outbound target and for rejecting both wrong-origin and wrong-source inbound messages.
Preimage lookup trusted non-empty bytes returned by both the IPFS gateway and Bitswap and cached them under the requested key. A faulty or hostile backend could therefore poison the process-wide cache and serve mismatched bytes to later subscribers. Recompute the requested Blake2b-256 content hash with the existing content verification utility before either cache write. Verification failures follow the existing backend-error stream path and never populate the cache. Cover successful verified caching and corrupt responses from both backend modes. The corruption cases subscribe twice to prove that rejected bytes are fetched again rather than retained.
Link one TrUAPI client across dotli and the product so local runs exercise the same runtime end to end. Surface authentication progress while pairing finalizes.
PAPI registers Function.prototype as a WebSocket no-op. Sentry's BrowserApiErrors wrapper then marks the prototype and makes later event listeners resolve to that no-op.
Resolve the dependency audit advisories by locking patched fast-uri and brace-expansion releases within existing dependency ranges.
Independent providers drained the same smoldot response queue, allowing concurrent Statement Store calls to steal replies.
Override AJV's transitive fast-uri to 3.1.4 to address GHSA-v2hh-gcrm-f6hx.
Route Rust Core and host RPC through logical protocol connections so each runtime has one upstream response consumer per genesis hash. Keep People in the protocol iframe under SharedWorker mode because statement discovery requires browser WebRTC unavailable in SharedWorkerGlobalScope.
Contributor
⚡ Performance Report
|
Contributor
Bundle Size ReportChunks over 500 KB:
All files
Commit: ace4b5e |
@dotli/protocol/errors now imports @dotli/shared/errors, which put a @dotli/shared module into the functional specs' import chain for the first time (loading.spec.ts -> apps/host/src/errors.ts -> @dotli/protocol/errors). The extensionless exports target (./src/*) resolves under Vite/Vitest but not under Playwright-on-Bun, so every spec failed to load and the Functional job collected 0 tests. Match the .ts-qualified pattern the other workspace packages use.
isChainConnectionErrorCode had no callers, and no producer emits a chain-halt message, so validate-on-arrival and the halt reason field were dead wire surface. Re-add them with the halt emitter follow-up.
Moving the host's statusToPhase import off @dotli/resolver/resolve means a blocked resolve chunk no longer fails host-side first. It now surfaces through the protocol iframe's vite:preloadError relay, which reports kind: fatal, and describeError mapped every ProtocolFatalError to the smoldot-crash copy before looking at the message. Match the chunk-load message first so users get the reload prompt instead of a light-client crash report (caught by loading.spec.ts:371).
Contributor
|
❌ E2E Product suite failed on Failed tests:
Logs: https://github.com/paritytech/dotli-community/actions/runs/29993014529 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #70.
Summary
Why
Rust Core chain calls bypassed the protocol broker and could create competing smoldot wrappers over the same destructive response queue. Responses and subscription acknowledgements could be consumed by the wrong wrapper, leaving Statement Store calls waiting indefinitely.
This change makes the protocol runtime the only physical provider owner and gives each runtime one upstream response consumer per genesis hash.
Follow-up
Connection, subscription acknowledgement, and submit acknowledgement deadlines plus cancellation propagation during subscription setup remain out of scope.