Conversation
Adds TrUAPI trait 21, PeerTransport: host-terminated JAMNP-S streams to
JAM peers (dial/open/send/recv/reset/close/events). The host terminates
QUIC or WebTransport, builds the jamnp-s ALPN from the declared genesis
and pins the peer's certificate identity; the guest verifies every byte
it receives.
A host may grant it only to an execution whose App manifest v2 declares
capabilities.network.jam = { genesis }, for that genesis only, with
bounded connections, streams and message sizes. The default
implementation, including the Rust product runtime, returns NotGranted.
Ships the browser WebTransport session and the deterministic PolkaJAM
certificate-hash derivation under @parity/truapi/peer-transport, with
SCALE codec vectors pinned against the Rust types.
|
CI Status: 24 required jobs green, 23 passed and 1 skipped by path filter. All job results
Signing credentials: failure as of 2026-10-02, a release may fail Commit |
iOS simulator previewBuilt from gh run download 36816443890 --name simulator-preview-87868067f
unzip polkadot-app-*.app.zip
xcrun simctl install booted polkadot-app.app
xcrun simctl launch booted io.parity.polkadotapp.developOr download it in a browser, which arrives as a zip wrapping An arm64 simulator slice, so it needs an Apple Silicon Mac and does not |
Peer access is now a runtime permission rather than an App-manifest
capability, matching the rest of TrUAPI (Remote, WebRtc, ChainSubmit).
RemotePermission gains JamPeers { genesis }, appended as variant 5 so
every earlier index is unchanged; its decision is stored per product and
genesis like any other remote permission.
ProductRuntimeHost::require_jam_peers checks the stored decision,
prompts while it is undetermined and persists the answer, keeping a
one-use grant for the execution so a light client dialing several
validators is asked once per genesis. The product runtime's
PeerTransport still answers NotGranted. The manifest grant and its
parser are gone; the genesis parser and ALPN helper stay.
The browser session takes an authorize(genesis) callback instead of a
fixed genesis, asks once per genesis per session and shares a pending
answer between concurrent dials. The Android and iOS product bridges
deny JamPeers without a prompt, as neither ships a JAM transport.
Brings #1010's switch from the manifest grant to the RemotePermission::JamPeers runtime permission into the Seity layer. Kept both sides of the permission storage-key test (Chat authority and JamPeers keys); regenerated the client catalog wire hash with codegen.
A dial waiting on the JamPeers prompt could outlast the guest's request timeout; the guest retried and the original dial later opened a connection nobody knew about, holding a slot until the session closed. Dials now answer within 10 s (prompt + handshake), CANCEL withdraws an in-flight dial with Cancelled, and anything opened after withdrawal is closed without holding a slot. The permission decision is still remembered.
Follows paritytech/trinity-user-agents#1010 at 50284f97, which renames the TrUAPI service to JamPeerTransport because it is JAM-specific (JamPeers permission, jamnp-s ALPN, JAMNP-S identity and framing). Wire-identical: trait 23, methods 0..6, same SCALE layout, same JamPeers permission. The sandbox now imports @parity/truapi/jam-peer-transport (createJamPeerTransportSession, JamPeerTransportSession, JAM_PEER_TRANSPORT_*), and the vendored @parity/truapi and @parity/truapi-host are rebuilt from that commit.
Trait ids on main run 1-19 and are handed out in merge order: open PRs already claim 20 (Contacts, Game) and 22 (Profile), and the next ones are likely to take 21-24. JamPeerTransport is unmerged, so moving it clear of that range now costs no deployed compatibility. Methods 0..6 and every SCALE payload are unchanged; only the trait byte moves from 23 to 111.
… 111 # Conflicts: # rust/crates/truapi-client/src/generated.rs
Native product runtimes (iOS, Android, CLI) now implement JamPeerTransport themselves instead of answering NotGranted. The JAMNP-S QUIC client is ported from jam-explore's tested jam-peer-transport-native crate (quinn + rustls/ring, self-signed Ed25519 identity pinned by the peer's JAMNP-S alternative name, u32-LE framing, per-execution caps) into truapi_server::jam_peer_transport, native targets only. Each product connection owns one session. A dial runs require_jam_peers once per genesis on the runtime spawner, so concurrent dials share one prompt, a refusal is remembered, and an answer given after the dial gave up is still persisted. A dial answers within 10 s including the prompt, honours CANCEL, and never holds a slot past its deadline. The endpoint is created by the first granted dial, so a refused product binds no socket; dispose closes all peer connections and flushes their close frames. recv reports Closed once the end of a stream has been consumed, and undrained events are capped at 1024, matching the browser session. The iOS and Android product bridges route RemotePermission::JamPeers through their existing remote-permission prompt and storage, per product and genesis. Adds tests/live_jam_public_devnet.rs: an ignored test that dials all six public-devnet validators through ProductRuntime frames, completes UP 0 and waits for block announcements, and a Linux test proving a refused product opens no UDP socket.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Bundle size reportCompared with
WebAssembly modules
Changed files (21)
Commit: c923399 |
|
This pull request touches an app, which is not built by default. Add a label for each build you want:
Each starts as soon as it is added and follows the branch from then on. |
# Conflicts: # rust/crates/truapi/Cargo.toml
# Conflicts: # rust/crates/truapi-client/src/generated.rs
# Conflicts: # rust/crates/truapi-client/src/generated.rs
Current repair qualification — 2026-10-02
ab46811b1e02469119a94706d7b4a01c9fc57839. Canonical peer artifact-generation and Wasm revisions are this same commit. Prior main-integration baselines are retained: nativebda6ac518c8cc59319491b12e4e23b96777375fd, frontendf4012c50c3400d1186c332ad2ae50298cefd153d.19a9c5aeb0f0f6ef33151ca66da3bb147efa5936. Complete matching SDK/host packages remain 0.23.0; codegen and Wasm were rebuilt canonically. Generated client bytes are unchanged. Production features:wasm-signing-host; separate testing bundle:wasm-signing-host,test-host.5efd803fff8452ecb1fbc9d23e58a28298d1ec256e7cf66b55ea567b67703077dist/generated/client.js5fe2e73ed9f9210045a1ad456e29632cff8fa3acbb2cc2054f8774a6633f57a51a99b8c8ab0f9e22ccc24d78273cc2c42d621e368fc31587f93699dc999a8a36b80462f2f938ccb3aaef6035bb2dccd71159588cc9d8a887f00450c558bc111356b0027e01fc1d08bb547a4489bbc08ef778f913eea08f68d56f79d114657a91Native correction and causal limits
Bulletin mortal signatures now anchor to a finalized checkpoint, while nonce/runtime state comes from the freshest available signing snapshot. A checkpoint at least 64 blocks behind is rejected before broadcast. Regression coverage verifies the actual signature with a newer nonce and older finalized checkpoint, and the expiry boundary. Existing transaction retries/deadlines are unchanged; no chain error is suppressed. The affected test fixture uses
parking_lot::Mutex.The former Seity
Extrinsic marked as invalidrun used noncanonical best-block anchors, but retained evidence lacks the signed bytes and typed pool reason. Fork-sensitive mortality was a real correctness hazard; it is not claimed as the conclusively proved cause of that historical failure. Prior hosted Factory/Genesis timeout causes also remain unproved. No broker/readiness workaround, prewarming, extra retry, or timeout increase was added.Executed verification
-D warnings, CLI build, and canonical browser/testing Wasm packaging. Base full library: 1,246 passed.0a24db4aa189adad7b93f9ff508c7949e39196ee301a235ba13248ef4281e9e3d859919fb6cfbe06d3dc8a41807c36b8bcdf001c47eccb9735305499The Chat/Seity tested heads above precede documentation-only formatting merges; their native/Wasm pins and runtime code are unchanged. Final hosted frontend results are recorded on the linked consumer PRs, separately from these exact-head local results.
Final hosted retest: failures remain
error; it is not evidence of a chain rejection. Retained traces include People-chain/local development sockets but do not expose the Asset Hub gateway WebSocket exchange, so these new failures do not establish an upstream or broker cause.paseo-bulletin-next-ipfs.polkadot.io. The zero-failure host-settings gate correctly failed.Doom: criterion corrected, backend matrix still unqualified
The user explicitly approved 35 FPS sustained over 30 seconds, with one frame of sampling-boundary tolerance:
frames + 1 >= elapsedMs * 35 / 1000. This replaces the instantaneousFPS >= 35sample; it is an acceptance-criterion change, not a runtime speedup. Runtime and displayed FPS are unchanged; raw samples are not rounded to force a pass. Update p95 <28.6ms, cold/warm first-frame limits <3,000/<1,000ms, audio and translation-cache checks remain. The revised official RPC benchmark passed once before the vendor replacement.The later isolated matrix used the rebuilt base pair at frontend
238ab5fa739788b2eb948ba1f807a1876edbacad, with fresh owned Chrome profiles and no concurrent builds/E2E:Distinct diagnostics found no presentation loss in a later instrumented 24-second shared-worker sample and observed a successful hidden RPC new-document reload. Neither diagnostic supersedes the failures or qualifies performance. No evidence-proved runtime fix, guest rebuild, speculative tuning, or further unchanged gate rerun was made. The original strict RPC failure and every new failure remain retained. Next causal capture must instrument the original early 30-second window and distinguish warm lifecycle states before proposing a runtime fix.
Retention and rollout boundary
Local verification used
paseo-next-v2, the pinned host-playground fixturef56294cea4430163bf16ec068844b1327441073c, and existing private QA identities. All three corrected sequences paired on their first existing setup attempt. A prior local launch failure and a misconfigured Previewnet-product run (34 passed, 20 skipped, 8 Chain/Contract failures) are retained separately, not presented as reproductions of the hosted Genesis timeout. Private traces/auth/signers were not published.The first repair heads also exposed a README formatting failure (corrected by documentation-only commits) and a PVM cache-unit-test 5,000ms timeout. No cache runtime/test change, limit increase, or causal claim was made for that isolated timeout. Older in-flight runs superseded by the formatting correction remain recorded as cancelled, not passed.
No PR merge, force push, deployment, environment approval, SDK/npm/product/guest publication, or rollback was performed by this repair. JAM remains JAM-TEST-INSTANCE, never JAM-PUBLIC-DEVNET. The user-owned
deploy: paseo.fyilabel is retained; repair-triggered deployment runs 36970790399 and 36971286469 were cancelled before deployment. The earlier rollout audit is retained below: an older workflow deployed56cea5d37577bf82684fb5c6b6e4ba81d2142938; this record does not claim live remained unchanged historically.Historical integration qualification (superseded; retained verbatim)
Current main refresh and qualification — 2026-10-01
3cc0e5c44850a5f87606bf065a7603a17c342ed8. Native mainbda6ac518c8cc59319491b12e4e23b96777375fdis integrated; the frontend stack includes dotli mainf4012c50c3400d1186c332ad2ae50298cefd153d(merged chore(deps): bump postcss from 8.5.15 to 8.5.23 in /explorer #313). Histories and worktrees were preserved; pushes used fetched-head ancestry guards, never force.79cd7de8a7ccb4ef27e81ae158f6e7cf9aedffd6. Later native changes are test/docs/iOS-only, not SDK/Wasm inputs, so the artifact pin intentionally differs from the current head. Complete matching client and host packages remain 0.23.0, generated rather than hand-edited. Browser signing Wasm usesweb-wasm-signing-hostwithouttest-host; testing Wasm is separate.44ed4f830a2c8f86990e02fb061c802aa389b0bc.5efd803fff8452ecb1fbc9d23e58a28298d1ec256e7cf66b55ea567b67703077dist/generated/client.js(notdist/index.js)5fe2e73ed9f9210045a1ad456e29632cff8fa3acbb2cc2054f8774a6633f57a5a17d04963c096d859699e88f41c473e899a84fa90a5dc3f05ce2018f57752459cddf79e80de21e517b97ad2156611f09bb77c4c55016994384efb7bb593cbdb2Qualification
Frontend #290 exact-head Tests: functional 89 passed, 3 skipped; E2E 42 passed, 20 skipped. No flaky case in this final hosted Tests run.
Current-head Core CI: 24 required jobs green — 22 passed, 2 path-filter skips. This includes core Swift/Android coverage, not a claim that full iOS application CI ran at this head. The separate existing release-signing-credentials advisory failure remains distinct from Core CI.
Local: Rust workspace: 1,849 passed across 29 suites, 22 ignored. Client/host SDK: 320/285 passed. Live JAM test passed (1 test, 1.11s). Canonical codegen and complete feature-specific package construction were qualified locally.
Browser/native proof: Actual installed PVM host-frame-request path passed with the canonical SDK: trusted product/full-genesis consent, deny/dismiss without dialing, real certificate-verified WebTransport and UP0 open/send/receive, 73-byte handshake and 333-byte block announcement, independent genesis grants, close/reset, stale-frame rejection, pagehide disposal, and replacement-execution re-consent. Target: JAM-TEST-INSTANCE, genesis
0x10c123f02eb6df4c01397d797a112055be691883baa2e82f83b618ed6ce45e46. No TLS bypass or new published guest. Evidence:main-refresh-peer-pvm-live.jsonand screenshots.Retained cross-stack limits
Extrinsic marked as invalid). Two existing native broadcast attempts were validated then invalidated; none was found included in the inspected canonical range. [INFERENCE] Noncanonical mortality anchors may explain invalidation; the exact cause is not established and signed extrinsic bytes were not retained. The earlier Factory deadline failure is also retained: inclusion took 47,516ms against the unchanged 30s product deadline. A separate unchanged manual Factory attempt passed in 10.918s. These failures are not erased by other passing checks..authcontents are included here.This refresh authorizes no deployment, environment approval, PR merge, SDK/npm publication, or guest/product publication. Its QA writes were testnet-only.
All three refresh-triggered JAM Deploy runs were cancelled before rollout; final-head cancellation proof is for frontend
dcdef40499119183aed82a39f784ef740d494341. No refreshed source head was deployed.The live environment changed during qualification: older Deploy run 36945647296, attempt 3 was approved under GitHub account
replghost, explicitly checked out baseline56cea5d37577bf82684fb5c6b6e4ba81d2142938, and recorded deployment success at 2026-10-02 01:19:01 UTC. Its later published-product smoke failed. The operator/client/session behind that account is unproven; this qualification granted no approval and performed no rollback. The observed live content hash changed from4a7caf047fb7f350c1833039b93ba634698a47c56f051b8c7fc0525d4c59a5c8to5882695ee20df5d24a6ed2feb9e997f176fe27ecf4eafe742fabb39eba92885f; these are content hashes, not Git SHAs, and their exact byte-level mapping to a source commit is unproven. Earlier revision/deployment records below remain historical evidence and do not override this current section.Execution callback ownership
createProvider(product, callbacks)binds platform callbacks to one execution of a shared native host. Retired executions cannot route later callbacks through the core's default callback scope; wallet authentication and storage retain core ownership. Native contacts installed after core construction remain available unless the execution supplies its own contacts adapter.Current head:
87868067f1277491723ee5fd454d5ff43fb42a8a. Canonical codegen, default web/testing Wasm, signing-only browser Wasm, host SDK tests, and harness typing are qualified. Hosted CI is green, including Rust workspace, Wasm bridge, Android compilation and iOS Swift/WebKit checks. No package publication.Earlier source and qualification (superseded)
Head
f18d4c8ea676c034704026de7644fbba0d12e0d7includes mainaa6ae62ca038bf4a6356edae8eb78e595d52ce24through history-preserving merge commits. Client and host package manifests remain 0.23.0 with pending Changesets.The branch includes the integrated main changes and preserves its feature boundary. Root and combined-stack canonical codegen and TypeScript qualification pass. This branch’s current-head Codegen CI job passes; its downloaded canonical output matches all 45 tracked generated files byte-for-byte. Full workspace/native qualification remains tracked by current-head CI.
Current-head core CI passes, including Rust workspace, default WASM bridge, Android compile/unit checks, and iOS Swift + WebKit. Full local workspace/native qualification was interrupted by workstation disk exhaustion; the full current-head core CI gate completes that qualification. Full iOS application CI also passes, including the in-tree core, application build, simulator preview, and tests. No PR was merged or approved, and no npm package was published. Deployment evidence below belongs to the explicitly named earlier revisions, not this source refresh.
Summary
Adds the
JamPeerTransportTrUAPI host service (wire trait 111) so a PolkaVM app can observe a JAM network directly over JAMNP-S, the way a light client does, instead of through an application-specific proof relay. The service is JAM-specific (theJamPeerspermission, thejamnp-sALPN, JAMNP-S peer identity and framing), hence the name.dial,open,send,recv,reset,close,events. The host terminates QUIC (native) or WebTransport (browser), builds thejamnp-s/1/<genesis8>ALPN from the dialed genesis, pins the peer's certificate identity, and frames/unframesu32-LE JAMNP-S messages. It never interprets JAM messages; the guest verifies everything it receives.Remote { domains },WebRtc,ChainSubmit, ...).RemotePermissiongainsJamPeers { genesis: [u8; 32] }, appended last (SCALE index 5, earlier indices unchanged), displayed as "connections to JAM network 0x<first 8 hex>…". Before adialconnects, the host requiresJamPeers { genesis }exactly likeChainSubmit: it reads the product's stored decision, prompts when it is undetermined and persists the answer per product and genesis (the storage key is the canonical request, so each genesis is its own slot). The decision is cached for the execution, so a light client dialing six validators is asked at most once per genesis. Denied or unsupported isNotGranted; the other methods act only on connections a granted dial opened. App manifests declare nothing and there is no host-side allowlist.dialfirst runsProductRuntimeHost::require_jam_peers(genesis), which reads the stored decision, prompts while it is undetermined and persists the answer; a one-use answer is held for the execution rather than spent by the first dial. The browser (wasm) core keeps the trait'sNotGranteddefaults, since its JavaScript session answers trait 111 before frames reach the core.@parity/truapi/jam-peer-transportimplements the service over the WebTransport API.createJamPeerTransportSession({ authorize, connect?, now? })callsauthorize(genesisHex)at most once per genesis per session (concurrent dials share the pending answer;falseor a rejection isNotGranted). A host with the Rust core implementsauthorizethroughpermissions.authorizeRemotePermission({ permission: { tag: "JamPeers", value: { genesis } } }). It also ships the deterministic PolkaJAM certificate-hash derivation needed forserverCertificateHashes(PolkaJAM nodes serve WebTransport on their JAMNP-S port with an unsigned P-256 certificate per fixed validity period).JamPeers { genesis }through their existing remote-permission prompt and storage, keyed per product and genesis (" would like to connect to JAM network 0x10c123f0…", "Read-only peer access to this network's validators, with no accounts or signing."). The CLI host prompts through its approval policy with the permission's display text.WebTransport certificate serials. Stock PolkaJAM gives every P-256 WebTransport certificate issuer
CN=jamand serial 0, and Firefox's NSS rejects a second, different certificate with the same issuer and serial (SEC_ERROR_REUSED_ISSUER_AND_SERIAL), so Firefox reached one validator of six. jam-explore'spolkajam-webtransport-serial.patchderives the serial from the key and validity period (first 8 bytes of SHA-256(compressed key ‖ period as big-endian u64), top bit cleared, 1 if zero). The browser adapter now pins both variants for each of the three periods (six hashes), so stock nodes keep working and every patched node is reachable in Firefox. Vectors are certificates real patched and stock nodes served, byte for byte. The native QUIC path pins the Ed25519 key, not the serial, and is unchanged.Native implementation
Native product runtimes (iOS, Android, CLI) serve
JamPeerTransportthemselves:truapi::jam_peer_transportholds a JAMNP-S QUIC client (quinn + rustls/ring, self-signed Ed25519 identity,u32-LE framing, the 8/16/1 MiB/4 MiB caps), andProductRuntimeHostimplements the trait over one session per product connection. The code is the testedjam-peer-transport-nativecrate from the JAM light-client work, ported into the core. The wasm build is unchanged: the browser core keeps the trait'sNotGranteddefaults, and the new dependencies are native-only.dialrunsrequire_jam_peers(genesis)once per genesis per connection. Concurrent dials share the pending answer, a refusal staysNotGrantedwithout asking again, and the check runs on the runtime spawner, so an answer given after the dial gave up is still persisted.Unreachableafter that,Cancelledon CANCEL), and what it would have opened is dropped without holding a connection slot.jam_peer_transport::alpn(genesis). The peer certificate must carry the dialed Ed25519 key under its JAMNP-S alternative name, and the TLS 1.3 signature is checked against that key. The P-256 key is for WebTransport hosts and is ignored.ProductRuntime::disposecloses every peer connection, the close frames are sent before the endpoint's runtime stops, and later calls areDenied.Closed. At most 1024 undrained events are kept.JamPeers { genesis }through their existing remote-permission prompt and storage, keyed per product and genesis (" would like to connect to JAM network 0x10c123f0…", "Read-only peer access to this network's validators, with no accounts or signing."). The CLI host already prompts through its approval policy with the permission's display text.Stacking
This is a separate layer on top of #540, not part of the generic PolkaVM app runtime. It must not be merged before #540, and #540 does not depend on it. The browser consumer is paritytech/dotli-community#290, the matching layer on top of paritytech/dotli-community#185.
Security
This is the first outbound-network capability offered to PolkaVM apps. It is scoped to one user-approved genesis per decision, to endpoints the app names, and to the peer identity the app pins (Ed25519 key for QUIC, P-256-derived certificate hashes for WebTransport). Caps: 8 connections, 16 streams per connection, 1 MiB messages, 4 MiB buffered per connection. No HTTP, DNS, arbitrary hosts, accounts, signing, submission or storage authority is implied. Bytes are untrusted until the app verifies them. Like every
RemotePermission, products onREMOTE_PERMISSION_TRUSTED_LABELShold it without a prompt unless a stored decision says otherwise.Artifacts and qualification
Main is integrated through
c5158448f3c4575f40350017d466053d6b19dacb. The consumer paritytech/dotli-community#290 pins matching TrUAPI client/host 0.23.0 and wallet WASM fromc73e072c96cff7d363af9a61e378ee22621e9f5b, with exact archive/client/WASM digests in its vendor lock. Later CI/tooling integration does not change that runtime artifact.Local native checks, wallet/testing WASM builds, 11 JAM-peer tests, and three transport tests pass. At
4a9a390055d5b97cf3d564df9c1ac51a55e4ee1d, all PR checks pass, including core CI, full iOS CI, and Android APK packaging.The iOS workflow always preserves xcresult, raw build/simulator logs, and available crash reports, and prints the test summary/list. Diagnostic capture was smoke-tested with real Xcode result fixtures and exercised successfully by the hosted run. No tests, retries, or parallelism settings were changed. An earlier iOS test failure discarded its diagnostics, so its cause is not established; the current full test run passes.
The consumer checks pass; the combined deployment is qualified separately in paritytech/dotli-community#291. No new live JAM-validator trial was performed for this refresh.