Skip to content
Merged
23 changes: 21 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
## 0.8.7 (2026-06-06)

### 🚀 Features

- **statement-store:** sign-and-submit primitives are now exported from the package index: `createExpiryAllocator` (a strictly-increasing expiry/priority source for one signing account, with a floor that adopts chain-reported minimums), `submitStatementOnce` / `signAndSubmitStatement` (allocate an expiry, prove and submit a statement, resyncing the allocator on a priority rejection), and `submitWithRetry` / `isPriorityTooLow` (a retry policy with separate budgets for transient failures and priority rejections). The session consumes the same primitives internally, and `createSession` accepts an optional shared `allocator` so several writers signing with the same account cannot tie on same-second priorities.

### 🩹 Fixes

- **statement-store:** reworked the session to match the iOS/Android implementations. Concurrent incoming requests are now tracked independently (an older request stays answerable after a newer one arrives), transient submit/query failures are retried with a short backoff, and message batches are sized against the full encoded request payload instead of the raw bytes. Statement expiry is pinned to a non-expiring max with a wall-clock priority so channel supersession is deterministic, and a request id is best-effort recovered from a corrupt payload so the sender can be NACKed. Adds an in-memory adapter for tests.
- **statement-store:** the statement priority (the expiry's low word) is now counted from the spec's priority epoch (2025-11-15, exported as `PRIORITY_EPOCH_OFFSET`), matching iOS and Android. Previously it was the raw Unix timestamp, which made every TS-written statement outrank mobile-written ones in cross-client priority comparisons.
- **statement-store:** priority rejections (`ExpiryTooLowError` and `AccountFullError`) no longer surface to session callers: while a submission is live the session resyncs its expiry above the chain-reported minimum and keeps retrying beyond the transient-failure cap; once the submission is superseded on its channel, the rejection is absorbed as success — it merely lost the race to a newer statement, and re-answering would clobber it.
- **statement-store:** a disposed session now rejects `submitRequestMessage` / `submitResponseMessage` immediately instead of hanging, and no longer re-activates (or submits queued work) when `dispose()` lands while initialization is still in flight.
- **statement-store / host-papp:** the SSO allowance service now builds its statement-store prover from the mobile slot-account secret (`privateKey || nonce`) via the new `createSlotAccountProver`, instead of treating it as a raw sr25519 secret — proofs now sign and verify against the correct slot-account public key.
- **host-papp:** the authorising device's encryption public key (`deviceEncPubKey`) from the V2 handshake response is now persisted on `StoredUserSession` and exposed on the session, so the host can ECDH-address the paired device (e.g. for device-sync). It was previously decoded but dropped on persistence, leaving consumers to mis-read the 32-byte SSO shared secret in `remoteAccount.publicKey` as a public key.
- **host-papp:** the SSO request size limit was raised from 254 KiB to 500 KiB, tracking the mobile statement allowance.
- bumped `polkadot-api` to 2.1.6, which fixes a double-notification bug.

### ⚠️ Breaking Changes
### 🏡 Chore

- every published package now ships a `LICENSE` file, and a repo-wide `THIRD_PARTY_NOTICES.md` records dependency licenses.
- added a `SECURITY.md` policy and hardened `.env` handling in `.gitignore`.

- **host-papp:** `StoredUserSession` gained a required `deviceEncPubKey` field (65-byte uncompressed P-256). Sessions persisted before this change do not decode and are not migrated — a fresh SSO handshake (re-pairing / reset app data) is required after upgrading.
### ❤️ Thank You

- Ilya Kalinin
- PG Herveou @pgherveou
- Sergey Zhuravlev @johnthecat
- Yanaty

## 0.8.6 (2026-06-05)

Expand Down
1,218 changes: 858 additions & 360 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"@nx/js": "22.7.5",
"@nx/storybook": "22.7.5",
"@nx/vite": "22.7.5",
"@storybook/react-vite": "10.4.1",
"@types/node": "25.9.1",
"@storybook/react-vite": "10.4.4",
"@types/node": "25.9.3",
"@types/ws": "8.18.1",
"@vitejs/plugin-react": "5.2.0",
"babel-plugin-react-compiler": "1.0.0",
Expand All @@ -39,15 +39,15 @@
"globals": "17.6.0",
"husky": "9.1.7",
"jiti": "2.7.0",
"knip": "6.14.2",
"knip": "6.16.1",
"nanoevents": "9.1.0",
"nx": "22.7.5",
"prettier": "3.8.3",
"storybook": "10.4.1",
"prettier": "3.8.4",
"storybook": "10.4.4",
"ts-node": "10.9.2",
"tslib": "2.8.1",
"typescript": "6.0.3",
"typescript-eslint": "8.60.0",
"typescript-eslint": "8.61.0",
"vite": "7.3.2",
"vite-plugin-dts": "5.0.2",
"vite-plugin-externalize-deps": "0.10.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/handoff-service/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/handoff-service",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "HOP (Handoff Pool) file transfer service for P2P chat",
"license": "Apache-2.0",
"repository": {
Expand Down
4 changes: 2 additions & 2 deletions packages/host-api-wrapper/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/host-api-wrapper",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "Host API wrapper: integrate and run your product inside Polkadot browser.",
"license": "Apache-2.0",
"repository": {
Expand All @@ -28,7 +28,7 @@
"@polkadot/extension-inject": "^0.63.1",
"@polkadot-api/json-rpc-provider-proxy": "^0.4.0",
"@polkadot-api/substrate-bindings": "^0.20.3",
"@novasamatech/host-api": "0.8.7-0",
"@novasamatech/host-api": "0.8.7",
"polkadot-api": ">=2",
"neverthrow": "^8.2.0"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/host-api/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/host-api",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "Host API: transport implementation for host - product integration.",
"license": "Apache-2.0",
"repository": {
Expand All @@ -22,7 +22,7 @@
"README.md"
],
"dependencies": {
"@novasamatech/scale": "0.8.7-0",
"@novasamatech/scale": "0.8.7",
"nanoevents": "9.1.0",
"nanoid": "5.1.11",
"neverthrow": "^8.2.0",
Expand Down
8 changes: 4 additions & 4 deletions packages/host-chat/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/host-chat",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "Host statement store chat integration",
"license": "Apache-2.0",
"repository": {
Expand Down Expand Up @@ -40,9 +40,9 @@
"README.md"
],
"dependencies": {
"@novasamatech/scale": "0.8.7-0",
"@novasamatech/statement-store": "0.8.7-0",
"@novasamatech/storage-adapter": "0.8.7-0",
"@novasamatech/scale": "0.8.7",
"@novasamatech/statement-store": "0.8.7",
"@novasamatech/storage-adapter": "0.8.7",
"nanoid": "5.1.11",
"neverthrow": "^8.2.0"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/host-container/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/host-container",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "Host container for hosting and managing products within the Polkadot ecosystem.",
"license": "Apache-2.0",
"repository": {
Expand All @@ -28,7 +28,7 @@
"@noble/hashes": "2.2.0",
"polkadot-api": ">=2",
"@polkadot-api/substrate-client": "^0.7.0",
"@novasamatech/host-api": "0.8.7-0",
"@novasamatech/host-api": "0.8.7",
"nanoevents": "9.1.0",
"nanoid": "5.1.11",
"neverthrow": "^8.2.0"
Expand Down
18 changes: 9 additions & 9 deletions packages/host-papp-react-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/host-papp-react-ui",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "Polkadot app UI Flow",
"license": "Apache-2.0",
"repository": {
Expand Down Expand Up @@ -33,21 +33,21 @@
"react-dom": ">=18"
},
"dependencies": {
"@novasamatech/host-papp": "0.8.7-0",
"@novasamatech/statement-store": "0.8.7-0",
"@novasamatech/host-papp": "0.8.7",
"@novasamatech/statement-store": "0.8.7",
"@polkadot-api/utils": "^0.4.0",
"@radix-ui/react-dialog": "1.1.15",
"@radix-ui/react-popover": "1.1.15",
"@radix-ui/react-dialog": "1.1.16",
"@radix-ui/react-popover": "1.1.16",
"qrcode": "^1.5.4",
"@novasamatech/tr-ui": "0.2.9"
"@novasamatech/tr-ui": "^0.2.11"
},
"devDependencies": {
"@types/qrcode": "^1.5.6",
"@types/react": "19.2.15",
"@types/react": "19.2.17",
"@types/react-dom": "19.2.3",
"@polkadot-api/substrate-bindings": "^0.20.3",
"react": "19.2.6",
"react-dom": "19.2.6",
"react": "19.2.7",
"react-dom": "19.2.7",
"typescript-plugin-css-modules": "5.2.0",
"vite-plugin-wasm": "3.6.0"
},
Expand Down
10 changes: 5 additions & 5 deletions packages/host-papp/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/host-papp",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "Polkadot app integration",
"license": "Apache-2.0",
"repository": {
Expand Down Expand Up @@ -34,10 +34,10 @@
"@noble/ciphers": "2.2.0",
"@noble/curves": "2.2.0",
"@noble/hashes": "2.2.0",
"@novasamatech/host-api": "0.8.7-0",
"@novasamatech/scale": "0.8.7-0",
"@novasamatech/statement-store": "0.8.7-0",
"@novasamatech/storage-adapter": "0.8.7-0",
"@novasamatech/host-api": "0.8.7",
"@novasamatech/scale": "0.8.7",
"@novasamatech/statement-store": "0.8.7",
"@novasamatech/storage-adapter": "0.8.7",
"@polkadot-api/utils": "^0.4.0",
"@polkadot-labs/hdkd-helpers": "^0.0.30",
"nanoevents": "9.1.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/host-papp/src/sso/sessionManager/userSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import type { SigningPayloadRequest, SigningPayloadResponseData, SigningRawReque
// payload is for an SDK version the mobile app doesn't support yet. After
// this timeout the queue task fails, freeing the pool for the next request.
const QUEUE_TASK_TIMEOUT_MS = 180_000;
// Mobile SSO statements allow 256 KiB total; keep headroom for statement/session overhead.
const MAX_SSO_REQUEST_SIZE = 254 * 1024;
// Mobile SSO statements allow 500 KiB total; keep headroom for statement/session overhead.
const MAX_SSO_REQUEST_SIZE = 498 * 1024;

function withQueueTimeout<T>(resultAsync: ResultAsync<T, Error>, label: string): ResultAsync<T, Error> {
const timeoutPromise = new Promise<Result<T, Error>>(resolve =>
Expand Down
4 changes: 2 additions & 2 deletions packages/host-substrate-chain-connection/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/host-substrate-chain-connection",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "Chain connection pool with ref counting and provider branching for Polkadot API",
"license": "Apache-2.0",
"repository": {
Expand All @@ -25,7 +25,7 @@
"README.md"
],
"dependencies": {
"@novasamatech/storage-adapter": "0.8.7-0",
"@novasamatech/storage-adapter": "0.8.7",
"@polkadot-api/ws-provider": "^0.9.0",
"@polkadot-api/json-rpc-provider": "^0.2.0",
"@polkadot-api/json-rpc-provider-proxy": "^0.4.0",
Expand Down
6 changes: 3 additions & 3 deletions packages/host-worker-sandbox/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/host-worker-sandbox",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "QuickJS-based sandbox for running product worker code with Triangle Host API.",
"license": "Apache-2.0",
"repository": {
Expand All @@ -25,8 +25,8 @@
"README.md"
],
"dependencies": {
"@novasamatech/host-api": "0.8.7-0",
"@novasamatech/host-container": "0.8.7-0",
"@novasamatech/host-api": "0.8.7",
"@novasamatech/host-container": "0.8.7",
"quickjs-emscripten": "0.32.0"
},
"publishConfig": {
Expand Down
4 changes: 2 additions & 2 deletions packages/product-bulletin/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/product-bulletin",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "Bulletin Chain client adapter for Polkadot product applications",
"license": "Apache-2.0",
"repository": {
Expand All @@ -27,7 +27,7 @@
"README.md"
],
"dependencies": {
"@novasamatech/host-api-wrapper": "0.8.7-0",
"@novasamatech/host-api-wrapper": "0.8.7",
"@parity/bulletin-sdk": "^0.3.0",
"polkadot-api": ">=2"
},
Expand Down
6 changes: 3 additions & 3 deletions packages/product-react-renderer/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/product-react-renderer",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "React wrapper for custom renderer format from host-api-wrapper",
"license": "Apache-2.0",
"repository": {
Expand All @@ -24,8 +24,8 @@
"README.md"
],
"dependencies": {
"@novasamatech/host-api": "0.8.7-0",
"@novasamatech/host-api-wrapper": "0.8.7-0",
"@novasamatech/host-api": "0.8.7",
"@novasamatech/host-api-wrapper": "0.8.7",
"scale-ts": "1.6.1",
"react-reconciler": "0.33.0"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/scale/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/scale",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "additional scale-ts bindings",
"license": "Apache-2.0",
"repository": {
Expand Down
8 changes: 4 additions & 4 deletions packages/statement-store/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@novasamatech/statement-store",
"type": "module",
"version": "0.8.7-0",
"version": "0.8.7",
"description": "Statement store integration",
"license": "Apache-2.0",
"repository": {
Expand All @@ -25,13 +25,13 @@
"README.md"
],
"dependencies": {
"@novasamatech/scale": "0.8.7-0",
"@novasamatech/scale": "0.8.7",
"@novasamatech/sdk-statement": "^0.6.0",
"@novasamatech/substrate-slot-sr25519-wasm": "0.8.7-0",
"@novasamatech/substrate-slot-sr25519-wasm": "0.8.7",
"@polkadot-api/substrate-bindings": "^0.20.3",
"@polkadot-api/substrate-client": "^0.7.0",
"@polkadot-labs/hdkd-helpers": "^0.0.30",
"@polkadot-labs/schnorrkel-wasm": "0.0.8",
"@polkadot-labs/schnorrkel-wasm": "0.0.9",
"@noble/hashes": "2.2.0",
"@noble/ciphers": "2.2.0",
"@scure/sr25519": "2.2.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/statement-store/src/adapter/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ export function createPapiStatementStoreAdapter(lazyClient: LazyClient): Stateme
case 'dataTooLarge':
return errAsync(new DataTooLargeError(result.submitted_size, result.available_size));
case 'channelPriorityTooLow':
return errAsync(new ExpiryTooLowError(result.submitted_expiry, result.min_expiry));
return errAsync(new ExpiryTooLowError(BigInt(result.submitted_expiry), BigInt(result.min_expiry)));
case 'accountFull':
return errAsync(new AccountFullError(result.submitted_expiry, result.min_expiry));
return errAsync(new AccountFullError(BigInt(result.submitted_expiry), BigInt(result.min_expiry)));
case 'storeFull':
return errAsync(new StorageFullError());
case 'noAllowance':
Expand Down
7 changes: 7 additions & 0 deletions packages/statement-store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ export {
} from './adapter/types.js';
export { createPapiStatementStoreAdapter } from './adapter/rpc.js';

export type { ExpiryAllocator } from './submit/allocator.js';
export { PRIORITY_EPOCH_OFFSET, createExpiryAllocator } from './submit/allocator.js';
export type { SubmitRetryOptions } from './submit/retry.js';
export { isPriorityTooLow, submitWithRetry } from './submit/retry.js';
export type { SubmitStatementParams } from './submit/submitStatement.js';
export { signAndSubmitStatement, submitStatementOnce } from './submit/submitStatement.js';

export {
createSr25519Derivation,
createSr25519Secret,
Expand Down
Loading