Releases: paritytech/host-api-test-sdk
Release list
v0.13.1
Three parts of the host were declared to the core as absent and could not be configured otherwise, so three product-facing features were dead on arrival in 0.13.0. Each is now wired, and each has a test that fails without it.
All three surfaced when a real product suite migrated to 0.13.0. They shipped because account.getUserId() and preimage.submit() had no coverage here.
Fixed
account.getUserId() failed for every product with Unknown: No primary username for this session. The session was minted with both username fields empty, on the assumption that the core resolves them itself. It does — but only from the dotNS contracts on Asset Hub, and it gives up at once when the host declares no Asset Hub, which this one did. Sessions now carry "<name>.01" for the active account (alice.01, bob.01), following switchAccount.
A chain: 'Bulletin' network never reached the core, so preimage.submit() could not work at all — the core asked to connect to the all-zero genesis and got bulletin chain unavailable: … no chain configured for genesis 0x0000…. The core routes its own Bulletin and Asset Hub traffic by the genesis hashes handed to it at boot, not by anything supportedChains() reports, and both were hard-coded to all-zero. They now come from whichever configured network declares that chain role. Preimage lookup was unaffected.
The product's dotNS identifier was fixed at test-product.dot. The core refuses any call acting as a product account whose dotNsIdentifier is not the id the host declared the product under — signRaw, signPayload and createTransaction with PermissionDenied, statementStore.createProofAuthorized with UnknownAccount. A product signing under its own name got a blanket PermissionDenied with nothing in any log to explain it, and no option existed to change the id.
Added
productId on createTestHostServer and the Playwright fixture (default 'test-product.dot') — the dotNS identifier the host declares the product under, and the namespace the core scopes product storage and permissions to.
createTestHostFixture({
productUrl: "http://localhost:3000",
productId: "myapp.dot",
});It is not what productAccounts is keyed by — that is the dotNsIdentifier in the request, which the gate normally forces to the same value. Note that 'localhost' and 'localhost:<port>' are development wildcards the core admits for any dotNsIdentifier, so setting one turns the gate off entirely.
accounts[].username, overriding the derived "<name>.01":
accounts: [{ name: "Alice", uri: "//Alice", username: "zaphod.07" }]Changed
A network with chain: 'AssetHub' now has an effect on the core, where before it was declared absent. The core reads dotNS from it — product manifests, and the trustedProducts grants carrying cross-product access — so a grant that was refused instantly now costs a real round trip to that network's rpcUrl. Unchanged for a network with no chain role. Signing is untouched: still the in-page People loopback, still no network.
Upgrading
Nothing to change. Set productId if your product signs under its own dotNS name, and add a chain: 'Bulletin' network if you test preimage submission.
Full notes: CHANGELOG.md · README
v0.13.0
A rewrite of everything below the public API. The host no longer speaks the @novasamatech/host-container protocol — it runs the TrUAPI core itself, truapi-server compiled to WebAssembly in a Web Worker. Both sides of the wire move together, so a product must be on @parity/truapi 0.17 and boot through @parity/truapi/sandbox. A product on the old protocol will not connect at all.
Still no network, no node, no Docker: the People chain is an in-page loopback statement store, and the host mints both halves of the SSO session at boot.
Upgrading
Grep your tests for these — most are silent if you miss them.
productAccountsis keyed by the bare product id.{ 'myapp.dot/0': 'bob' }→{ 'myapp.dot': 'bob' }. A per-index key is now rejected with an error naming the replacement, and one entry moves every indexed account of that product together.- Any test pinning a product-account address must re-read it. The core derives index
nas a soft child of the product subtree, not the hard junction//Selected//dotnsId/n. Root dev accounts are unchanged —//Aliceis still5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY. - Signing is an SSO round trip, not a synchronous callback. Tests that await a signature already work; tests that read
getSigningLog()immediately after triggering an action must now await the product's own promise first. - Chat requires
executionKind: 'Worker'. The core denies every Chat entry point forAppandWidget. accounts[].uriaccepts only hard junctions, and the roster's first entry is the one active signing identity.switchAccountnow resolves names against the roster instead of synthesising//Name.- Removed controls: the statement-store, login and payment groups, plus
setEnforcePermissions. Removed types:LoginBehavior,PaymentLogEntry,PaymentTopUpBehavior,StatementSubmissionLogEntry. - Renamed:
clearChatState()→clearChat(). injectChatActionis async and takesChatActionInput.
Overriding host conditions
The headline addition: a test can now set what the host reports and how it decides, live or before the product's first frame.
Two families, named consistently — get<Thing> / set<Thing> / seed<Thing> / clear<Thing> for ambient data, and set<Thing>Behavior plus get<Thing>Log / clear<Thing>Log for decisions. Ambient data covers theme, locale, feature support, the supported chain set, product storage, chat rooms and bots, and device-permission status; decisions cover permissions, user confirmation, navigation and notifications. Passing undefined to a per-key setter restores what the host would otherwise report.
initialState and behaviors on createTestHostServer and the Playwright fixture apply the same overrides before the product loads, so a product that reads theme or locale during startup can be tested on that path. An unknown status or behaviour mode throws at boot, naming the value.
Two limitations, both documented: product-storage keys must be replayed exactly as getProductStorage() reports them (the core namespaces them per product), and the function form of a behavior works in-page only — the Playwright fixture's setters take 'approve-all' | 'reject-all', because a function cannot cross page.evaluate.
See Overriding host conditions for the full table.
Also added
executionKind('App' | 'Widget' | 'Worker', default'App') on the server and the fixture.getChainStatus()alongsidegetConnectionStatus()— the host's own session, as distinct from the product's connection. Signing travels over the session, so a switch that leaves it'disconnected'means no signature is coming.NetworkConfig.chain— a network's protocol role, reported throughsupportedChains(). A network that omits it is left out rather than labelled by guesswork.
Fixed
Four of these blocked signing outright, and each was invisible from the test suite that passed over it:
statement_submitwas rejected by the core, which blocked ALL signing. The loopback store replied with the bare string"new"; the core reads a field, so every SSO request died before a signing request ever reached the responder.- An indexed product account was reported under one key and signed for under another — a product's own signature did not verify against its own address. Now verified against schnorrkel's own pinned vector, not just against itself.
- The AutoSigning capability was refused as an invalid subtree secret.
@scure/sr25519hands out the cofactor-multiplied encoding; schnorrkel'sSecretKey::from_bytestakes only the canonical one. - Topic filters were parsed under the wrong key spelling, so a real filter matched every statement and
matchAnywas silently narrowed tomatchAll. featureSupporteddenied the one chain the host always serves — the in-page People loopback, which every signature travels over.- Switching to a custom account signed with the wrong key, silently ignoring the configured URI.
v0.12.1
host-api-test-sdk 0.12.1
One fix. Drop-in upgrade from 0.12.0.
Fixed
-
All three built-in network genesis hashes refreshed after chain resets.
PASEO_ASSET_HUB,PREVIEWNET, andPREVIEWNET_ASSET_HUBwere all pinned to genesis values from earlier deployments of those chains, verified dead against live RPC:Constant Was Now PASEO_ASSET_HUB0xbf0488…ef19f0x23e730eb1c6fecae09c917439a5038cb6122d0d48980e8b9bbf0ff56f94a2ca6PREVIEWNET0x477dd8…125250x8c27ddf678c2ae9bef0efebfc485a9309f3d735c6d3fbb8d947afc3ace0e80f4PREVIEWNET_ASSET_HUB0x860d75…c7867c0x4d11c803cc6921429e3876638977ad006ea1bba8cd3976a0bca2f164e7026210
The host routes each connection request to a network by genesis hash, so a stale pin means the host does not recognise the chain your product is asking for. The symptom is the product stuck at connection-status: "connecting" until the test times out, with a reachable RPC endpoint and nothing obviously wrong in the logs.
One thing worth calling out: before this release the pins were wrong but consistent. A test host and a set of descriptors both generated against the same dead chain agree with each other perfectly, so a suite can be green while connected to a chain that no longer exists. If your e2e went green across one of these resets without you touching anything, that is what happened.
What you need to do
- Upgrade to
0.12.1. If you use the built-in network configs, that is the whole change — the correct hashes come with the upgrade. - If you hard-coded any of the three old hashes in your own
NetworkConfigor in test assertions, update them. Better still, read them off the exported config (PASEO_ASSET_HUB.genesisHash) so the next reset costs you nothing. - These chains reset periodically. Treat a genesis literal in your own repo as something that will go stale, not as a constant.
v0.12.0
host-api-test-sdk 0.12.0
Tracks upstream @novasamatech/*@^0.9.1 (triangle-js-sdks#239) and serves products on @parity/truapi@^0.6.0. v0.9 is wire-incompatible with v0.8: RFC-0022 changes DerivationIndex from a bare u32 to Enum{Index(u32), Raw([u8; 32])}, so your product side has to move in the same commit.
Against 0.11.0 a truapi-0.6 product fails quietly: the wrong account for any index other than 0, signRaw over a different payload than the one requested, and createTransaction dropped without a reply. product-sdk's E2E suite goes from 2 failed to 54 passed on this release.
What changed on our side
Product accounts take a selector, not a number (breaking)
Index(n) resolves exactly as the plain n did, so productAccounts keys and derived addresses are unchanged. Raw 32-byte selectors are new, keyed by hex ("myapp.dot/0x1234…"). The wrapper takes number | Uint8Array and normalises for you; hand-built requests wrap the index:
-hostApi.signRaw(enumValue("v1", { account: [dotnsId, 0], payload }));
+hostApi.signRaw(enumValue("v1", { account: [dotnsId, derivationIndexOf(0)], payload }));accountCreateProof returns a struct (breaking)
-const proofHex = u8aToHex(result.value);
+const proofHex = u8aToHex(result.value.proof);Plus contextualAlias, ringIndex, ringRevision.
accountGetAlias rejects with GetAliasErr (breaking)
Alias errors moved out of RequestCredentialsErr into their own enum (RingNotFound / NotMember / Rejected / Unknown).
SmartContractAllowance carries a selector (breaking)
-{ tag: "SmartContractAllowance", value: 0 }
+{ tag: "SmartContractAllowance", value: { tag: "Index", value: 0 } }handleAccountSignVrf (RFC-0023, new upstream) is not implemented — the container answers SignVrfErr.Unknown.
What you need to do
- Upgrade to
0.12.0and move your product side to@parity/truapi@^0.6.0or@novasamatech/host-api-wrapper@^0.9.1in the same commit. - Through the wrapper, nothing else. Hand-built requests: wrap indices in
derivationIndexOf()and adjust the three shapes above. - If your fixtures still pass
chain:, see 0.10.0 — it is silently ignored, so anyrpcUrloverride you set is being dropped.
v0.11.0
@parity/truapi 0.4 products connect out of the box
Products that upgraded to @parity/truapi 0.4 (including everything built on recent @parity/product-sdk) change how the iframe channel is opened: instead of exchanging frames directly over window postMessage, the product posts { type: "truapi-ready" } and expects the host to answer with { type: "truapi-init" } carrying a transferred MessagePort. Against older test-sdk releases, that handshake went unanswered, the product waited 20 seconds for a port that never arrived and waitForConnection() timed out.
The test host now answers the handshake and serves all traffic over the transferred port. No test changes are needed:
const bobFixture = createTestHostFixture({
productUrl: "http://localhost:5260",
accounts: ["bob"],
networks: [PASEO_ASSET_HUB],
});
// waitForConnection() now resolves for truapi-0.4 products tooProducts on the 0.3 bootstrap (@novasamatech/host-api-wrapper) are unaffected, the direct window postMessage channel is still served, and both kinds of product talk to the same container with the same handlers, logs, and permission model.
v0.10.0
v0.9.2
Changed
- Upstream
@novasamatech/*→^0.8.8. Tracks 0.8.7 (triangle-js-sdks release/0.8.7) and 0.8.8 (release/0.8.8). The diff against 0.8.6 inhost-api,host-container, andhost-api-wrapperis LICENSE files and version bumps only — no source changes. The 0.8.7 statement-store rework (priority epoch, expiry retries,AccountFullhandling) and the 0.8.8 legacy sign requests both live inhost-papp(the SSO peer / authorising-device path), which this SDK does not simulate. No handler surface changes; no test changes beyond re-running the suite.
v0.9.1
Changed
- Upstream
@novasamatech/*→^0.8.6. Pulls in RFC-0021 coin top-ups (triangle-js-sdks#194), thePaymentTopUpSourcecodec fix (#198 —PrivateKey/Coinskeys are now 64-byte sr25519 secrets, not 32-byte ed25519), thederiveProductEntropyFromSourceexport for RFC-0007 Option 1 hosts (#205), and host-chat / host-papp internals. All existing handlers (theme, payments, signing, statement-store) continue to work without code changes.
Added
PaymentTopUpSource.Coins(Vector<Sr25519SecretKey>)flows through topaymentLog.source.handlePaymentTopUpalready forwardedparams.sourceas-is, so the new variant lands in the log entry as{ tag: 'Coins', value: Uint8Array[] }. Each key in the vector is 64 bytes after the upstream codec fix.setPaymentTopUpBehavior(behavior)test control for driving products through the RFC-0021PartialPaymenterror path. Behavior is'ok'(default),{ type: 'partial', credited }(creditcreditedand reject withPaymentTopUpErr.PartialPayment({ credited }), mirroring how a real host reports that only some coins could be claimed), or{ type: 'reject', reason: 'InvalidSource' | 'InsufficientFunds' }. ThepaymentLogentry always records the attemptedamountandsourceregardless of outcome.PaymentTopUpBehaviortype exported from the package root so tests can type the argument.
Internal
- Integration tests for the coins round-trip and the partial-payment behavior. The partial-payment test asserts both the rejected promise's
payload.creditedand that the balance was bumped by exactly that amount. - Test product gained
paymentTopUpCoins(amount, keysHex[])helper.
v0.9.0
Changed
- Upstream
@novasamatech/*→^0.8.0(triangle-js-sdks#179). v0.8 is wire-incompatible with v0.7 — a test host built on this release will only talk to products on@novasamatech/host-api@^0.8.0. Upgrade your product side in lockstep. Most products don't need code changes if they usecreatePapiProviderfor chain access and@novasamatech/product-react-rendererfor custom chat. See the v0.8 migration guide for the product-side breaking points.
Breaking changes
- Theme subscription delivers the new
{ name, variant }struct instead of a flat'light' | 'dark'.setTheme('light' | 'dark')keeps working as a shorthand (mapped to{ name: { tag: 'Default', value: undefined }, variant: 'Light' | 'Dark' }) and now also accepts the full struct so tests can drive custom-named themes.getTheme()returns the struct — readtheme.variantfor the previous light/dark value. AllocatableResourcevariant rename:BulletInAllowance→BulletinAllowance. Affects tests that hand-build resource-allocation requests.
Added
PaymentLogEntry.purserecords the optional purse selector from RFC-0017 —intoon top-ups,fromon payment requests. Undefined means the product targeted the main purse.ThemeandThemeInputtypes exported from the package root..github/CODEOWNERSadded with* @mordamax.
v0.8.6
One fix. Drop-in upgrade from 0.8.5.
Fixed
PASEO_ASSET_HUB.genesisHashrefreshed after the Paseo Asset Hub chain reset (#32). Thepaseo-asset-hub-nextchain was reset, changing its genesis from0x173cea…to0xbf0488dbe9daa1de1c08c5f743e26fdc2a4ecd74cf87dd1b4b1eeb99ae4ef19f. The built-inPASEO_ASSET_HUBconfig carried the old hash, so the host's chain-feature handshake rejected product-sdk descriptors regenerated against the new chain — the product app would sitdisconnectedin E2E even though the RPC was reachable. The new value is verified against the live chain viachain_getBlockHash(0).
What you need to do
- Upgrade to
0.8.6. If you only use the built-inPASEO_ASSET_HUBconfig, no code changes are needed — the correct genesis comes with the upgrade. - If you hardcoded the old genesis (
0x173cea…) in your ownChainConfigor test assertions, update it to0xbf0488….