From 88130dc62f86e1147d67d1f4444dc0e996d30ffa Mon Sep 17 00:00:00 2001 From: Elis Jackson Date: Wed, 16 Sep 2026 15:57:26 -0500 Subject: [PATCH 1/2] fix(wallet-toolbox): run SQLite migrations inside knex's per-file transaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With transactions disabled for SQLite, each DDL statement autocommits while knex records the migration in knex_migrations only after the whole file's up() resolves. A process killed mid-file — or between a file's last statement and the journal insert — leaves objects the journal never recorded, and every later migrate.latest() re-runs that file from its first statement and fails on "table ... already exists" or "duplicate column name", permanently, with no in-package recovery path. For a wallet with a local SQLite store, a force-quit during first-run bring-up is enough. SQLite DDL is transactional, so letting knex wrap each migration file rolls an interrupted migration back whole and keeps its journal insert atomic with its DDL. This also covers the alterTable and index-adding migrations, which statement-level idempotence guards cannot: knex exposes no portable hasIndex, and guards never close the journal-write window. PRAGMA foreign_keys is still issued outside migrate.latest(). SQLite's single-connection pool means the migration transaction inherits it, and knex's own alter-table rebuild deliberately leaves an ambient pragma alone while transacting (sqlite3/schema/ddl.js: enforceForeignCheck = transacting ? null : false), so the knex#4155 constraint the previous comment describes still holds. MySQL behaviour is unchanged: isSQLite was already false there, so disableTransactions was already false. dropAllData()'s own setting is deliberately untouched. Closes #538 --- .../wallet-toolbox/src/storage/StorageKnex.ts | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/wallet/wallet-toolbox/src/storage/StorageKnex.ts b/packages/wallet/wallet-toolbox/src/storage/StorageKnex.ts index 73b8c1ea4..186713710 100644 --- a/packages/wallet/wallet-toolbox/src/storage/StorageKnex.ts +++ b/packages/wallet/wallet-toolbox/src/storage/StorageKnex.ts @@ -1538,17 +1538,29 @@ export class StorageKnex extends StorageProvider implements WalletStorageProvide const clientName = (this.knex.client as { config?: { client?: string } }).config?.client ?? '' const isSQLite = clientName.includes('sqlite') - // For SQLite, disable transactions during migrations and turn off foreign keys. - // PRAGMA foreign_keys is silently ignored inside transactions, so we must - // disable transactions for the migration to allow the PRAGMA to take effect. - // See: https://github.com/knex/knex/issues/4155 + // For SQLite, turn foreign keys off for the duration of the migration. + // PRAGMA foreign_keys is silently ignored *when executed inside* a + // transaction (https://github.com/knex/knex/issues/4155), so it is issued + // here, outside migrate.latest(). SQLite's single-connection pool means + // knex's per-migration transaction runs on this same connection and + // inherits the setting, and knex's own SQLite alter-table rebuild leaves an + // ambient pragma alone while transacting (sqlite3/schema/ddl.js: alter() + // uses `enforceForeignCheck = this.client.transacting ? null : false`). if (isSQLite) { await this.knex.raw('PRAGMA foreign_keys = OFF;') } const config = { migrationSource: new KnexMigrations(this.chain, storageName, storageIdentityKey, 1024), - disableTransactions: isSQLite + // Let knex wrap each migration file in a transaction on every engine. + // SQLite DDL is transactional, so an interrupted migration rolls back + // whole and its knex_migrations insert stays atomic with its DDL. With + // transactions disabled, a process killed between two statements of one + // file — or between its last statement and the journal insert — left + // objects the journal never recorded, and every later migrate.latest() + // re-ran the file from its first statement and failed on + // "table ... already exists", permanently, with no recovery path. + disableTransactions: false } await this.knex.migrate.latest(config) const version = await this.knex.migrate.currentVersion(config) From 4c4127b2f6b1d1c90e1c180e584a68175ed40360 Mon Sep 17 00:00:00 2001 From: Ty J Everett Date: Tue, 22 Sep 2026 18:34:47 -0700 Subject: [PATCH 2/2] test(wallet): verify atomic SQLite migration recovery --- docs/reference/package-api-migrations.md | 76 +++---- governance/package-release-notes.json | 4 +- packages/wallet/wallet-toolbox/CHANGELOG.md | 6 + packages/wallet/wallet-toolbox/README.md | 14 ++ .../StorageKnexMigrationAtomicity.test.ts | 215 ++++++++++++++++++ ...orageKnexMigrationFailure.security.test.ts | 22 +- 6 files changed, 294 insertions(+), 43 deletions(-) create mode 100644 packages/wallet/wallet-toolbox/src/storage/__test/StorageKnexMigrationAtomicity.test.ts diff --git a/docs/reference/package-api-migrations.md b/docs/reference/package-api-migrations.md index cb1137120..d004a209c 100644 --- a/docs/reference/package-api-migrations.md +++ b/docs/reference/package-api-migrations.md @@ -23,42 +23,42 @@ and clean-consumer tests remain the executable type authority. ## Current release boundary -| Package | npm baseline | Source | Candidate | API | Migration | -| --------------------------------- | ------------ | -------- | --------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `@bsv/402-pay` | `0.2.5` | `0.3.2` | minor | [API and usage](../packages/middleware/402-pay.md) | Custom clients must send a strictly framed BRC-95 Atomic BEEF envelope whose subject is the payment transaction; plain BEEF is no longer accepted. Legacy Atomic BEEF containing unrelated branches remains compatible because the server reduces it to the declared subject and dependency closure. The default replay store is bounded and process-local; production services with more than one serving process or node must inject the same durable atomic PaymentReplayStore everywhere. Low-level validators should retain one wallet object or pass an explicit store. Applications that relied on implicit console diagnostics must supply the optional structured logger. Treat an unchallenged 503 after payment submission as ambiguous and reconcile the transaction before requesting another payment. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | -| `@bsv/air-gap` | `0.1.2` | `0.1.3` | patch | [API and usage](../packages/helpers/air-gap.md) | No migration is required for valid BRC-141 v1 senders or decoders. Malformed, oversized, duplicate-heavy, or adversarial high-degree sessions now fail closed or are evicted within the documented limits instead of consuming unbounded resources. | -| `@bsv/amountinator` | `2.1.5` | `2.1.6` | patch | [API and usage](../packages/helpers/amountinator.md) | Valid finite inputs retain the public API. Currency identifiers are trimmed and normalized to uppercase; callers that passed non-finite values, empty currencies, coercive options, invalid decimal precision, or non-finite converter results must normalize or reject them before calling because those values now fail closed. | -| `@bsv/auth` | `0.1.1` | `0.1.5` | patch | [API and usage](../packages/middleware/auth.md) | No valid proof bytes or public API shape changes. Client and server must use the same protocol tuple; security levels 0, 1, and 2 remain supported and select wallet consent policy while the explicit counterparty scopes derivation at every level. Proof and wallet adapters must supply plain own data fields and dense byte arrays; inherited, accessor-backed, sparse, or malformed runtime shapes fail closed. Structured bodies containing non-finite numbers or negative zero must be normalized to an unambiguous wire representation before signing. | -| `@bsv/auth-express-middleware` | `2.2.3` | `2.2.5` | patch | [API and usage](../packages/middleware/auth-express-middleware.md) | No valid BRC-103/104 wire bytes are changed. Upgrade to @bsv/auth-express-middleware 2.2.5 with @bsv/sdk 2.7.1 or later. When onCertificatesReceived is configured, the callback must call its approval function before returning; replicated deployments must inject a shared CertificateApprovalStore as well as shared session state. Parsed URL-encoded objects are limited to flat string fields, and unsupported nonempty parsed bodies now fail closed. BRC-104 v0.1 does not sign Host/authority, cookies, forwarding metadata, arbitrary standard request headers, or arbitrary standard response headers; pin authority at a trusted edge and do not authorize from omitted metadata. | -| `@bsv/authsocket` | `2.1.1` | `2.1.8` | patch | [API and usage](../packages/messaging/authsocket.md) | No API or wire migration is required for valid JSON events. Existing numeric-key objects under byte-like names are unchanged; typed payment protocols recover historical byte objects at their explicit fields. Outbound non-JSON or ambiguous runtime values, including negative zero, nested undefined, sparse arrays, accessors, hidden/extra properties, and serialization hooks, now fail closed before signing. requestedCertificates is an SDK allowlist, not an application authorization verdict. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | -| `@bsv/authsocket-client` | `2.1.1` | `2.1.7` | patch | [API and usage](../packages/messaging/authsocket-client.md) | No API or wire migration is required for valid JSON events. Existing numeric-key objects under byte-like names are unchanged; typed payment protocols recover historical byte objects at their explicit fields. Outbound non-JSON or ambiguous runtime values, including negative zero, nested undefined, sparse arrays, accessors, hidden/extra properties, and serialization hooks, now fail closed before signing. The connect event/connected property report Socket.IO transport state, not completed BRC-103 authentication; wait for verified application traffic when local behavior requires a known unpinned server. Distributors who copy the UMD file must keep THIRD_PARTY_NOTICES.md and LICENSES/ with it. | -| `@bsv/btms` | `1.1.1` | `1.2.3` | minor | [API and usage](../packages/wallet/btms.md) | Existing local, mainnet, testnet, and number-array behavior is unchanged. TTN consumers select networkPreset teratestnet; all consumers should upgrade to @bsv/sdk 2.4.1 or later for byte-boundary compatibility. Valid canonical token amounts remain compatible. Audit historical tokens for signed, exponent, leading-zero, non-positive, or greater-than-9007199254740991 amount fields before rebuilding wallet state; public number-based operations now fail closed when an aggregate cannot be represented exactly. | -| `@bsv/btms-permission-module` | `1.2.0` | `1.2.1` | patch | [API and usage](../packages/wallet/btms-permission-module.md) | Valid canonical BTMS flows remain compatible. Custom prompts must return literal true, authorization maps may contain at most 1,024 entries, and callers must provide dense signatures, safe exact totals, and canonical preimages; coercive or trailing-byte forms now fail closed. | -| `@bsv/chirp` | `0.0.0` | `0.1.2` | minor | [API and usage](../packages/network/chirp.md) | Valid BRC-167 objects, CHIRP/UHRP locators, host endpoints, and ordinary typed calls retain their wire and API behavior. Runtime configuration/options and number-array bytes must use plain own data properties and exact documented types; malformed, accessor-backed, oversized, expired, or non-canonical values now fail closed. Use fetchClient to customize network transport while retaining AuthFetch; the legacy fetch callback replaces the full authenticated request path and should be limited to tests or a caller-supplied authenticated client. Custom caches and sinks receive owned bytes and cannot mutate returned verified results. Pass signal to build, publish, download, stream, or closure validation when external adapters must be cancellable. CLI resume files are bounded, non-symlink, atomic mode-0600 capabilities, and retrieval refuses to follow or replace an existing output path. Existing @bsv/sdk storage and UHRP routes remain unchanged; BRC-167 remains authoritative. | -| `@bsv/did` | `0.2.1` | `0.2.6` | patch | [API and usage](../packages/helpers/did.md) | Valid compact encodings and public API signatures remain compatible. Non-did:key issuers must provide issuerPublicKey from local trust policy; authorization-sensitive verifiers should also set expectedIssuer and expectedVct. Key Binding verification now requires both expectedAudience and a transaction-specific expectedNonce and rejects stale or future proofs. Credential aud requires expectedCredentialAudience. Holder presentation verifies the credential first, requires the holder key to match cnf.jwk, and accepts trusted non-did:key issuer policy through verificationOptions. Nested disclosure selections use full dotted paths. Malformed, ambiguous, accessor-backed, oversized, noncanonical, duplicate, disconnected, or collision-bearing inputs now fail closed. Applications must still evaluate signed status claims under their own credential policy before granting access. | -| `@bsv/did-client` | `1.3.1` | `1.3.2` | patch | [API and usage](../packages/helpers/did-client.md) | New revocable DID tokens are locked to the issuer and bind the declared subject in their authenticated payload. Previously issued distinct-subject tokens used subject-owned locks and do not authenticate the issuer/subject relationship needed to reconstruct documented revocation; coordinate reissuance or an application-specific verified migration before relying on issuer revocation. Valid canonical same-party and newly issued flows remain compatible; malformed, oversized, ambiguous, or transaction-mutated results now fail closed. | -| `@bsv/ecpm-permission-module` | `0.1.0` | `0.1.1` | patch | [API and usage](../packages/wallet/ecpm-permission-module.md) | Valid ecpm requests remain compatible. Authorization callbacks must return literal true; inherited, accessor-backed, malformed, or oversized inputs now fail closed. Hosts must keep grants below 1,024 entries and avoid more than 64 concurrently pending new authorization prompts per module instance. | -| `@bsv/fund-wallet` | `1.5.1` | `1.5.2` | patch | [API and usage](../packages/helpers/fund-wallet.md) | No public API migration is required for conforming wallets. Custom WalletInterface adapters must return the literal accepted: true verdict after successful internalization; refusals and malformed or coercive results now throw instead of being reported as success. | -| `@bsv/gasp` | `1.3.1` | `1.3.7` | patch | [API and usage](../packages/overlays/gasp.md) | No API or wire migration is required. In bidirectional mode, the receiver must already have the parent of a pushed child, request it during a subsequent synchronization round, or reject the graph because GASP v1 submitNode does not transmit the spentBy parent outpoint. Pull-only operation avoids that assumption. Custom storage and remote adapters must preserve exact request/node binding and bounded responses. | -| `@bsv/lch` | `0.1.0` | `0.2.0` | minor | [API and usage](../packages/content/lch.md) | Replace buyer.quote(endpoint, request, issuer, keyGrants) with buyer.quote(verifiedSignedOffer, request, expectedSeller, keyGrants). JavaScript callers that still pass an endpoint string fail before transport I/O. Persist the returned plan and every partial settlement proof, retry with the same funded transaction, and configure a profile-aware agreementEvaluator before completion or recovery. Low-level unverified recovery results must not authorize key storage or content access. DNS endpoints require an address-pinning connector outside trusted browser environments. Distributors must retain THIRD_PARTY_NOTICES.md with the package; published BRC-170 remains authoritative if the implementation and standard differ. | -| `@bsv/message-box-client` | `2.4.0` | `2.5.2` | minor | [API and usage](../packages/messaging/message-box-client.md) | No valid Message Box wire format or existing method signature changes. socketOptions, serverIdentityKeysByHost, and maximumPayment are optional additive configuration. Move non-loopback HTTP Message Box deployments to HTTPS; ordinary AuthFetch fallback responses, malformed wallet identities, server identity changes within one client instance, malformed quote/send response shapes, and mismatched payment transactions now fail closed. Applications needing durable identity continuity should configure independently validated serverIdentityKeysByHost pins; keys are normalized per URL origin. Different overlay origins may retain different server identities. Custom WalletInterface implementations used for paid sends must return canonical Atomic BEEF plus its matching transaction ID and must preserve the requested output order when randomizeOutputs is false. Batch payment shape is unchanged, but its single server output must carry the quoted per-recipient delivery fee multiplied by the number of allowed recipients; older underpaying batch implementations are rejected. Client diagnostics, including errors, are now disabled unless enableLogging is true and emit only fixed lifecycle events; applications that need request correlation should add their own non-sensitive identifier rather than restoring raw wallet or message values. Upgrade @bsv/sdk and @bsv/message-box-client together; historical number-array wallets, current Uint8Array substrates, and already-pending numeric-key messages interoperate through the same portable transaction form. Distributors who copy the UMD file must keep THIRD_PARTY_NOTICES.md and LICENSES/ with it. No migration is required for the bundled payment optimization. Module consumers can use SDK 2.5.0 or later to enable the same optional extension; existing compatible SDK peer versions remain supported. Recipients must advertise already-validated transaction IDs through x-bsv-payment-known-txids, an optional SDK extension rather than a standardized BRC-105 header; services that omit it retain existing payment behavior. Existing return shapes and payment envelopes remain unchanged. Failed or incomplete notification payments stay queued. Resolve uncertain refund-send outcomes before retrying; this patch adds ordering checks, not an exactly-once refund journal. listMessages/listMessagesLite envelope behavior and basket-insertion semantics remain tracked in issue #503. Previously trimmed message-box, message-ID, device-token, and device-ID values must now be supplied in their exact canonical form; valid canonical protocol values are unchanged. | -| `@bsv/overlay` | `2.3.1` | `2.6.1` | minor | [API and usage](../packages/overlays/overlay.md) | Stop writes and take coordinated SQL and lookup-store backups before running every new Knex migration. Preflight and reconcile exact duplicate (txid, outputIndex, topic) output keys and (txid, topic) applied-transaction keys from transaction, topic, and lookup evidence; the uniqueness migration intentionally fails rather than deleting security state. Apply topical uniqueness before the additive spentBy column and verify both before serving writes. Prefer roll-forward: older versions do not know these migration names, so an image-only rollback can fail migration-list validation. To restore an older version, keep writes stopped and either use the new migration source to reverse spentBy and topical uniqueness in reverse order after exporting and reconciling every spent/spentBy association, or restore coordinated pre-migration SQL and lookup-store backups. Configure canonical header resolution and public HTTPS endpoints for production network paths; custom storage, topic, discovery, and transport adapters must satisfy the new validation and resource bounds. External topic indexes are not made globally atomic by SQL submission serialization, so retain idempotent compensation for side effects outside the overlay database. Valid canonical overlay wire shapes remain supported. | -| `@bsv/overlay-discovery-services` | `2.2.1` | `2.2.5` | patch | [API and usage](../packages/overlays/overlay-discovery-services.md) | Valid canonical advertisements and bounded queries remain compatible. Code that used findAllAdvertisements to enumerate advertisements owned by other identities must use the appropriate public lookup service instead; the wallet helper is now an authenticated owner view. Private-network or plaintext production URIs, malformed tokens, unbounded query shapes, and wallet results that mutate inspected actions now fail closed. | -| `@bsv/overlay-express` | `2.6.1` | `2.7.2` | minor | [API and usage](../packages/overlays/overlay-express.md) | Set an ARC callback token of at least 32 bytes before enabling the ARC ingestion route and update the sender to present it. Private-network or plaintext outbound destinations require the explicit allowPrivateHosts development policy; production endpoints must use public HTTPS. Detailed health data is opt-in. Existing canonical public overlay requests and default credential-free CORS remain supported, but operators must roll out callback credentials and egress policy together across every replica. | -| `@bsv/overlay-topics` | `1.8.0` | `1.8.4` | patch | [API and usage](../packages/overlays/overlay-topics.md) | Topic and lookup identifiers and valid canonical wire encodings remain unchanged. Audit historical rows for malformed amounts, noncanonical identifiers, incomplete ownership or admin evidence, and ambiguous outpoint linkage before replay or rebuild. Custom state and screening providers must return exact booleans, compare identity keys case-insensitively where documented, conserve exact safe-integer value, and honor bounded query and result contracts. | -| `@bsv/paymail` | `2.4.2` | `2.4.9` | patch | [API and usage](../packages/messaging/paymail.md) | Valid public APIs and deployed Paymail wire shapes remain supported; malformed, ambiguous, oversized, local-network, wrong-owner, mutation-backed, or request-mismatched input now fails closed. Production origins and capability endpoints must be credential-free public HTTPS, except exact localhost development; custom transports and resolver configuration must use the documented exact runtime types. Receive-route verifySignature defaults to false for legacy compatibility, so unsigned metadata must not authorize a sender. Even when enabled, the legacy P2P signature authenticates only the transaction ID, not recipient, reference, sender-handle context, endpoint, freshness, or replay state. The historical 6745385c3fc0 advertisement is this package's compatibility behavior and is not the upstream signed/timestamped Basic Address Resolution assurance; a complete correction needs a versioned wire migration. Transaction Negotiation v1 is unauthenticated public input. Handlers must validate outputs, references, thread/freshness policy, proofs, callbacks, and durable replay state before financial or authorization effects. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | -| `@bsv/payment-express-middleware` | `2.1.1` | `2.1.7` | patch | [API and usage](../packages/middleware/payment-express-middleware.md) | No wire or public API migration is required; legacy x-bsv-payment JSON behavior remains supported, and Express 4 and 5 applications use their own peer-provided Express installation. Wallet adapters must return accepted and optional isMerge as own data properties; inherited/accessor-backed verdicts now fail closed. Overinclusive Atomic BEEF receipts are normalized to the declared subject closure. Production replicas must share one durable atomic replay store, and operators must reconcile a replay-store failure after wallet acceptance before asking a payer to spend again. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | -| `@bsv/sdk` | `2.7.1` | `2.8.0` | minor | [API and usage](../packages/sdk/bsv-sdk.md) | Existing TOTP.generate and TOTP.validate calls retain their historical two-digit, unpadded behavior and require no wire migration. New authentication flows should use generateSecure and validateSecure and store or transmit the six-character code as a string so leading zeroes are preserved. Ordinary valid BEEF, BRC-103 v0.1 peers, and public APIs remain compatible; malformed, ambiguous, oversized, identity-mismatched, or value-creating results now fail closed. Validated wallet results retain ordinary object behavior but are returned as owned value snapshots, so callers must not rely on object, array, or byte-buffer identity with the wallet adapter's raw response. Historical numeric-key JSON objects are recovered as bytes only for documented HTTP wallet byte fields; opaque numeric-key metadata remains an object. Deferred signableTransaction results may remain partial, and completed createAction results may use source values from the caller's immutable inputBEEF. Custom wallets must include direct source transactions for every other completed createAction or signAction input; duplicate input outpoints and unresolved or zero-input value-creating completed results are rejected. Browser applications that require DNS rebinding resistance must use a trusted egress proxy. | -| `@bsv/simple` | `0.5.3` | `0.6.0` | minor | [API and usage](../packages/helpers/simple.md) | Replace createServerWalletHandler() deployments with createServerWalletHandler({ authorize: async ({ action, headers }) => authenticatedSessionCanUseAction(headers, action) }). The callback must return literal true for each status, create, request, receive, balance, outputs, or reset action; omission now returns HTTP 403 for every action. Roll out the authentication layer and callback with the package, update anonymous probes or automation, and apply the same policy to every replica. Do not emulate the old public behavior with an unconditional authorize: () => true callback. Valid recipient derivations and authenticated Message Box peers remain supported; malformed, wrong-owner, or transaction-mutated flows now fail closed. New DID, CredentialSchema, and Certifier records use canonical 32-byte types. Current SDK wallet methods reject historical short types, so do not put migration aliases in wallet list, acquire, prove, or relinquish calls. Export affected records through the storage version that created them, authenticate them offline against the exact locally configured identifier, and reissue/import canonical replacements; no legacy certificate is rewritten automatically. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/. | -| `@bsv/templates` | `1.9.1` | `1.10.2` | minor | [API and usage](../packages/helpers/templates.md) | No API migration is required for valid templates. MandalaToken now rejects locking or decoding amounts outside JavaScript's positive safe-integer range; audit any previously accepted non-exact amount scripts before replay. New R1K1Wallet consumers await lock(), retain each private 32-byte salt, and provide a PIV signer that signs the supplied digest directly without hashing it again. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | -| `@bsv/teranode-listener` | `1.1.1` | `1.1.6` | patch | [API and usage](../packages/network/teranode-listener.md) | No API migration is required for valid consumers: raw callbacks remain the default and decoding is opt-in with decodeMessages: true. Configuration arrays and callbacks are snapshotted at construction, boolean controls must be literal booleans, and malformed or duplicate topics, addresses, keys, and unsupported properties now fail closed. usePrivateDHT: false now actually omits the DHT service. The published mainnet PNET value is transport compatibility data, not a publisher credential; decoded sender and payload fields remain untrusted and security-critical claims require independent validation. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | -| `@bsv/verifast` | `0.3.5` | `0.3.6` | patch | [API and usage](../packages/sdk/verifast.md) | Valid typed verification calls and worker protocols remain compatible. Custom module factories and WASM adapters must return the exact documented binary and boolean shapes; coercive network, height, flag, byte, batch, verdict, lifecycle, or disposed-instance values now fail closed. Keep THIRD_PARTY_NOTICES.md and LICENSES/ with every JavaScript and WebAssembly distribution. | -| `@bsv/wallet-helper` | `0.1.7` | `0.1.8` | patch | [API and usage](../packages/helpers/wallet-helper.md) | getAddress now derives with forSelf: true and returns the caller-owned side of the bilateral relationship. Applications that stored or coordinated the previous peer-owned result must regenerate and exchange the corrected address before sending value. Amount must be an integer from 1 through 1,000 and counterparties must be valid public keys. Valid canonical transaction-builder flows remain supported; malformed, ambiguous, or wallet-mutated results now fail closed. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | -| `@bsv/wallet-relay` | `0.3.6` | `0.5.1` | minor | [API and usage](../packages/wallet/wallet-relay.md) | Enable signed QR codes and distribute only HTTPS pairing origins and relay API URLs. Root-relative API paths and loopback HTTP remain supported. Configure onApprovalRequired for every method not deliberately listed in autoApproveMethods; unsigned pairing URIs and implicit approval are no longer accepted. Existing relay sessions, custom RPC method names, and supported wallet RPC byte encodings remain valid, and host applications continue to provide their matching Express runtime and type graph. Version 0.5 defaults to two missed pongs; set maxMissedHeartbeats to 1 to retain the prior heartbeat policy. Heartbeat intervals must fit the Node timer range. Preserve Cache-Control: no-store at proxies, prefer the stable bsv-wallet-relay plus token WebSocket subprotocols over the legacy token query parameter, and configure edge rate limiting in addition to the bounded in-process defaults. The 24-hour connected-session lifetime no longer renews on reconnect, malformed wallet calls fail with code 400, and WalletRelayClient retains at most 100 log entries unless maxLogEntries is configured. The QR remains a short-lived bearer invitation: keep it private and require explicit operation approval. Relay envelopes, cryptographic framing, and wallet RPC encodings remain unchanged. | -| `@bsv/wallet-toolbox` | `2.12.0` | `2.13.2` | minor | [API and usage](../packages/wallet/wallet-toolbox.md) | No consumer, wire, or database migration is required for canonical proof validation and retry handling. Custom WalletServices implementations may omit getValidatedMerklePath; stale RPC proof recovery then uses one fully validated getMerklePath lookup. Sync transfer support requires the 2.13.0 candidate or a later release containing it on the client and each relevant provider; published 2.12.0 does not contain this extension. Providers must run additive migration 2026-09-09-001 for two bounded staging tables before advertising transfer version 1; syncTransfers: false supports a mixed-version rollout. For rollback, stop transfer traffic and use the new migration source to reverse only this staging migration and its ledger entry before restarting older code; preserve all current wallet records. Existing ordinary-page and export file contracts are retained. IndexedDB upgrades automatically to version 6 with a non-unique transaction-ID/user index, preserving existing data and duplicate transaction IDs. Older clients requesting schema version 6 cannot reopen the upgraded database; retain a compatible client for local backups. Compact checkpoints are advertised through runtime settings; older providers retain the full-state path. No ID-map data is removed and existing wire defaults remain compatible. Existing sync peers remain compatible; additive fields are optional. Run the normal Knex migration for source indexes. Only idempotent getSyncChunk reads retry after HTTP 413. Existing actions, ordinary noSend calls, permission modules, UMP v3 tokens, and active WAB accounts require no client migration. Deploy the additive WAB registration-status migration and WAB routes before relying on interrupted-signup recovery; older servers and clients retain their prior wire behavior. To use BRC-177, migrate every active Knex store before serving requests and run the default Wallet Toolbox monitor; IndexedDB upgrades automatically to schema version 6. Upgrade signer, active storage service, and remote monitor together to 2.11.0 or later; older remote storage is rejected before prefunding. The Knex migration also adds rebuildable prepared-BEEF and proof-epoch tables with every COOK control disabled. Validate the migration on MySQL before release and the cross-process epoch fence on non-production PXC before enabling writes. Roll out writes before reads, use backfill only after database review, and disable all three flags to roll back. Delete derived prepared rows before downgrading to code that cannot advance the epoch. Semantic modules may add handleRequest; hosts installing @bsv/ecpm-permission-module register it under the ecpm scheme. Argon2id tokens keep the same parameters and derived bytes across WebAssembly and JavaScript runtimes. Host registration is available from each package root; concurrent cold calls share one preload attempt, and hosts must make readiness/preload reentrant and cache permanent failures or back off retries. Native and JavaScript results share byte-type and exact-length validation; unrelated hash-wasm errors still propagate without WebAssembly. Canonical AtomicBEEF and number-array behavior are unchanged; use @bsv/sdk 2.4.2 or later, use docs/storage.md instead of the removed JSight export, and retain THIRD_PARTY_NOTICES.md and LICENSES/. Replace non-loopback HTTP storage and Arcade SSE endpoints with HTTPS. Snapshot APIs and formats are unchanged, but applications must store each complete snapshot in an OS Keychain, hardware-backed keystore, or comparably trusted secret store because possession of it grants wallet access. Apply every ChainTracks Knex migration before serving traffic. The MySQL repair clears only the rebuildable live-header cache while widening legacy truncating identifier columns to VARCHAR(64), retains authenticated bulk data, and adds the transaction lock row; allow the tracker to repopulate live headers before declaring it healthy. To downgrade, stop all ChainTracks writes, take and verify a database and authenticated-bulk-data backup, then use the current ChaintracksKnexMigrations source to roll down only the 2026-09-17 repair migration ledger entry. Its down step intentionally leaves the repaired VARCHAR(64) and LONGBLOB schema, chaintracks_state lock row, and authenticated bulk files intact. Start older code only after validating that retained schema and data in a non-production copy; never roll down the initial migration, recreate the tables, or restore truncating VARBINARY(32) identifier columns. Existing ChainTracks wire and public API contracts are unchanged. Existing logging integrations remain source compatible, but applications that relied on implicit console output must supply the optional logging callback explicitly. Configure durable download and cache lock timeouts deliberately; neither crash-abandoned lock is reclaimed automatically, so prove no writer remains before removing only the affected lock directory. Custom ChaintracksStorageBulkFileApi implementations must add atomic replaceBulkFiles support before multi-file reconciliation or replacement; older custom adapters now fail closed for those operations. MonitorOptions.maxQueuedDeactivatedHeaders is additive and defaults to 4096; lower it for constrained hosts. Arcade SSE event, pending-count, and pending-byte limits are additive and default to 262144 bytes, 64 events, and 4194304 bytes; lower them for constrained hosts. MonitorOptions.logging and ArcSSEClientOptions.log are additive and replace prior implicit library console output when observability is required. | -| `@bsv/wallet-toolbox-client` | `2.12.0` | `2.13.2` | minor | [API and usage](../packages/wallet/wallet-toolbox-client.md) | Custom WalletServices implementations may omit getValidatedMerklePath; stale RPC proof recovery then uses one fully validated getMerklePath lookup. Sync transfer support requires the 2.13.0 candidate or a later release containing it on the client and each relevant provider; published 2.12.0 does not contain this extension. Providers must run additive migration 2026-09-09-001 for two bounded staging tables before advertising transfer version 1; syncTransfers: false supports a mixed-version rollout. For rollback, stop transfer traffic and use the new migration source to reverse only this staging migration and its ledger entry before restarting older code; preserve all current wallet records. Existing ordinary-page and export file contracts are retained. IndexedDB upgrades automatically to version 6 with a non-unique transaction-ID/user index, preserving existing data and duplicate transaction IDs. Older clients requesting schema version 6 cannot reopen the upgraded database; retain a compatible client for local backups. Compact checkpoints are advertised through runtime settings; older providers retain the full-state path. No ID-map data is removed and existing wire defaults remain compatible. Existing sync peers remain compatible; additive fields are optional. Run the normal Knex migration for source indexes. Only idempotent getSyncChunk reads retry after HTTP 413. Existing browser actions, permission modules, UMP v3 tokens, and active WAB accounts require no client migration; IndexedDB upgrades automatically. Deploy the additive WAB registration-status migration and WAB routes before relying on interrupted-signup recovery; older servers and clients retain their prior wire behavior. To use BRC-177 with remote storage, upgrade the active storage service and its default monitor to Wallet Toolbox 2.11.0 or later before upgrading clients; an older server is rejected before prefunding. Prepared BEEF persistence and rollout controls apply only to the full package's Knex provider, so IndexedDB and remote clients require no COOK configuration. Semantic modules may add handleRequest; installing @bsv/ecpm-permission-module requires registration under the ecpm scheme. Argon2id tokens keep the same parameters and derived bytes across WebAssembly and JavaScript runtimes. Host registration is available from each package root; concurrent cold calls share a preload attempt, and hosts must make readiness/preload reentrant and cache permanent failures or back off retries. Native and JavaScript results share byte-type and exact-length validation; unrelated hash-wasm errors still propagate without WebAssembly. Browser exports, wire types, canonical AtomicBEEF behavior, and pagination contracts are unchanged; use @bsv/sdk 2.4.2 or later and retain THIRD_PARTY_NOTICES.md and LICENSES/. Replace non-loopback HTTP storage and Arcade SSE endpoints with HTTPS. Snapshot APIs and formats are unchanged, but store each complete snapshot only through browser or extension storage backed by an OS Keychain or comparably trusted secret store. | -| `@bsv/wallet-toolbox-mobile` | `2.12.0` | `2.13.2` | minor | [API and usage](../packages/wallet/wallet-toolbox-mobile.md) | Custom WalletServices implementations may omit getValidatedMerklePath; stale RPC proof recovery then uses one fully validated getMerklePath lookup. Sync transfer support requires the 2.13.0 candidate or a later release containing it on the client and each relevant provider; published 2.12.0 does not contain this extension. Providers must run additive migration 2026-09-09-001 for two bounded staging tables before advertising transfer version 1; syncTransfers: false supports a mixed-version rollout. For rollback, stop transfer traffic and use the new migration source to reverse only this staging migration and its ledger entry before restarting older code; preserve all current wallet records. Existing ordinary-page and export file contracts are retained. Compact checkpoints are advertised through runtime settings; older providers retain the full-state path. No ID-map data is removed and existing wire defaults remain compatible. Existing sync peers remain compatible; additive fields are optional. Run the normal Knex migration for source indexes. Only idempotent getSyncChunk reads retry after HTTP 413. Existing mobile actions, permission modules, UMP v3 tokens, and active WAB accounts require no client migration. Deploy the additive WAB registration-status migration and WAB routes before relying on interrupted-signup recovery; older servers and clients retain their prior wire behavior. To use BRC-177, migrate and upgrade the active remote storage service and its default monitor to Wallet Toolbox 2.11.0 or later before upgrading clients; an older server is rejected before prefunding. Prepared BEEF persistence and rollout controls apply only to the full package's Knex provider, so mobile remote clients require no COOK configuration. Semantic modules may add handleRequest without changing the Wallet interface. Argon2id tokens keep the same parameters and derived bytes across WebAssembly and JavaScript runtimes; no user device setting is required. Host registration is available from the mobile root; concurrent cold calls share a preload attempt, and hosts must make readiness/preload reentrant and cache permanent failures or back off retries. Native and JavaScript results share byte-type and exact-length validation; unrelated hash-wasm errors still propagate without WebAssembly. React Native exports, wire types, and canonical AtomicBEEF behavior are unchanged; use @bsv/sdk 2.4.2 or later and retain THIRD_PARTY_NOTICES.md and LICENSES/. Replace non-loopback HTTP storage and Arcade SSE endpoints with HTTPS. Snapshot APIs and formats are unchanged, but store each complete snapshot in the iOS Keychain, Android Keystore-backed encrypted storage, or a comparably trusted secret store. | -| `create-bsv-app` | `1.1.1` | `1.1.2` | patch | [API and usage](../packages/helpers/create-bsv-app.md) | Existing CLI flags, network choices, and generated project structure are unchanged. Regenerate or update dependencies after the patched packages are published. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | +| Package | npm baseline | Source | Candidate | API | Migration | +| --------------------------------- | ------------ | -------- | --------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `@bsv/402-pay` | `0.2.5` | `0.3.2` | minor | [API and usage](../packages/middleware/402-pay.md) | Custom clients must send a strictly framed BRC-95 Atomic BEEF envelope whose subject is the payment transaction; plain BEEF is no longer accepted. Legacy Atomic BEEF containing unrelated branches remains compatible because the server reduces it to the declared subject and dependency closure. The default replay store is bounded and process-local; production services with more than one serving process or node must inject the same durable atomic PaymentReplayStore everywhere. Low-level validators should retain one wallet object or pass an explicit store. Applications that relied on implicit console diagnostics must supply the optional structured logger. Treat an unchallenged 503 after payment submission as ambiguous and reconcile the transaction before requesting another payment. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | +| `@bsv/air-gap` | `0.1.2` | `0.1.3` | patch | [API and usage](../packages/helpers/air-gap.md) | No migration is required for valid BRC-141 v1 senders or decoders. Malformed, oversized, duplicate-heavy, or adversarial high-degree sessions now fail closed or are evicted within the documented limits instead of consuming unbounded resources. | +| `@bsv/amountinator` | `2.1.5` | `2.1.6` | patch | [API and usage](../packages/helpers/amountinator.md) | Valid finite inputs retain the public API. Currency identifiers are trimmed and normalized to uppercase; callers that passed non-finite values, empty currencies, coercive options, invalid decimal precision, or non-finite converter results must normalize or reject them before calling because those values now fail closed. | +| `@bsv/auth` | `0.1.1` | `0.1.5` | patch | [API and usage](../packages/middleware/auth.md) | No valid proof bytes or public API shape changes. Client and server must use the same protocol tuple; security levels 0, 1, and 2 remain supported and select wallet consent policy while the explicit counterparty scopes derivation at every level. Proof and wallet adapters must supply plain own data fields and dense byte arrays; inherited, accessor-backed, sparse, or malformed runtime shapes fail closed. Structured bodies containing non-finite numbers or negative zero must be normalized to an unambiguous wire representation before signing. | +| `@bsv/auth-express-middleware` | `2.2.3` | `2.2.5` | patch | [API and usage](../packages/middleware/auth-express-middleware.md) | No valid BRC-103/104 wire bytes are changed. Upgrade to @bsv/auth-express-middleware 2.2.5 with @bsv/sdk 2.7.1 or later. When onCertificatesReceived is configured, the callback must call its approval function before returning; replicated deployments must inject a shared CertificateApprovalStore as well as shared session state. Parsed URL-encoded objects are limited to flat string fields, and unsupported nonempty parsed bodies now fail closed. BRC-104 v0.1 does not sign Host/authority, cookies, forwarding metadata, arbitrary standard request headers, or arbitrary standard response headers; pin authority at a trusted edge and do not authorize from omitted metadata. | +| `@bsv/authsocket` | `2.1.1` | `2.1.8` | patch | [API and usage](../packages/messaging/authsocket.md) | No API or wire migration is required for valid JSON events. Existing numeric-key objects under byte-like names are unchanged; typed payment protocols recover historical byte objects at their explicit fields. Outbound non-JSON or ambiguous runtime values, including negative zero, nested undefined, sparse arrays, accessors, hidden/extra properties, and serialization hooks, now fail closed before signing. requestedCertificates is an SDK allowlist, not an application authorization verdict. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | +| `@bsv/authsocket-client` | `2.1.1` | `2.1.7` | patch | [API and usage](../packages/messaging/authsocket-client.md) | No API or wire migration is required for valid JSON events. Existing numeric-key objects under byte-like names are unchanged; typed payment protocols recover historical byte objects at their explicit fields. Outbound non-JSON or ambiguous runtime values, including negative zero, nested undefined, sparse arrays, accessors, hidden/extra properties, and serialization hooks, now fail closed before signing. The connect event/connected property report Socket.IO transport state, not completed BRC-103 authentication; wait for verified application traffic when local behavior requires a known unpinned server. Distributors who copy the UMD file must keep THIRD_PARTY_NOTICES.md and LICENSES/ with it. | +| `@bsv/btms` | `1.1.1` | `1.2.3` | minor | [API and usage](../packages/wallet/btms.md) | Existing local, mainnet, testnet, and number-array behavior is unchanged. TTN consumers select networkPreset teratestnet; all consumers should upgrade to @bsv/sdk 2.4.1 or later for byte-boundary compatibility. Valid canonical token amounts remain compatible. Audit historical tokens for signed, exponent, leading-zero, non-positive, or greater-than-9007199254740991 amount fields before rebuilding wallet state; public number-based operations now fail closed when an aggregate cannot be represented exactly. | +| `@bsv/btms-permission-module` | `1.2.0` | `1.2.1` | patch | [API and usage](../packages/wallet/btms-permission-module.md) | Valid canonical BTMS flows remain compatible. Custom prompts must return literal true, authorization maps may contain at most 1,024 entries, and callers must provide dense signatures, safe exact totals, and canonical preimages; coercive or trailing-byte forms now fail closed. | +| `@bsv/chirp` | `0.0.0` | `0.1.2` | minor | [API and usage](../packages/network/chirp.md) | Valid BRC-167 objects, CHIRP/UHRP locators, host endpoints, and ordinary typed calls retain their wire and API behavior. Runtime configuration/options and number-array bytes must use plain own data properties and exact documented types; malformed, accessor-backed, oversized, expired, or non-canonical values now fail closed. Use fetchClient to customize network transport while retaining AuthFetch; the legacy fetch callback replaces the full authenticated request path and should be limited to tests or a caller-supplied authenticated client. Custom caches and sinks receive owned bytes and cannot mutate returned verified results. Pass signal to build, publish, download, stream, or closure validation when external adapters must be cancellable. CLI resume files are bounded, non-symlink, atomic mode-0600 capabilities, and retrieval refuses to follow or replace an existing output path. Existing @bsv/sdk storage and UHRP routes remain unchanged; BRC-167 remains authoritative. | +| `@bsv/did` | `0.2.1` | `0.2.6` | patch | [API and usage](../packages/helpers/did.md) | Valid compact encodings and public API signatures remain compatible. Non-did:key issuers must provide issuerPublicKey from local trust policy; authorization-sensitive verifiers should also set expectedIssuer and expectedVct. Key Binding verification now requires both expectedAudience and a transaction-specific expectedNonce and rejects stale or future proofs. Credential aud requires expectedCredentialAudience. Holder presentation verifies the credential first, requires the holder key to match cnf.jwk, and accepts trusted non-did:key issuer policy through verificationOptions. Nested disclosure selections use full dotted paths. Malformed, ambiguous, accessor-backed, oversized, noncanonical, duplicate, disconnected, or collision-bearing inputs now fail closed. Applications must still evaluate signed status claims under their own credential policy before granting access. | +| `@bsv/did-client` | `1.3.1` | `1.3.2` | patch | [API and usage](../packages/helpers/did-client.md) | New revocable DID tokens are locked to the issuer and bind the declared subject in their authenticated payload. Previously issued distinct-subject tokens used subject-owned locks and do not authenticate the issuer/subject relationship needed to reconstruct documented revocation; coordinate reissuance or an application-specific verified migration before relying on issuer revocation. Valid canonical same-party and newly issued flows remain compatible; malformed, oversized, ambiguous, or transaction-mutated results now fail closed. | +| `@bsv/ecpm-permission-module` | `0.1.0` | `0.1.1` | patch | [API and usage](../packages/wallet/ecpm-permission-module.md) | Valid ecpm requests remain compatible. Authorization callbacks must return literal true; inherited, accessor-backed, malformed, or oversized inputs now fail closed. Hosts must keep grants below 1,024 entries and avoid more than 64 concurrently pending new authorization prompts per module instance. | +| `@bsv/fund-wallet` | `1.5.1` | `1.5.2` | patch | [API and usage](../packages/helpers/fund-wallet.md) | No public API migration is required for conforming wallets. Custom WalletInterface adapters must return the literal accepted: true verdict after successful internalization; refusals and malformed or coercive results now throw instead of being reported as success. | +| `@bsv/gasp` | `1.3.1` | `1.3.7` | patch | [API and usage](../packages/overlays/gasp.md) | No API or wire migration is required. In bidirectional mode, the receiver must already have the parent of a pushed child, request it during a subsequent synchronization round, or reject the graph because GASP v1 submitNode does not transmit the spentBy parent outpoint. Pull-only operation avoids that assumption. Custom storage and remote adapters must preserve exact request/node binding and bounded responses. | +| `@bsv/lch` | `0.1.0` | `0.2.0` | minor | [API and usage](../packages/content/lch.md) | Replace buyer.quote(endpoint, request, issuer, keyGrants) with buyer.quote(verifiedSignedOffer, request, expectedSeller, keyGrants). JavaScript callers that still pass an endpoint string fail before transport I/O. Persist the returned plan and every partial settlement proof, retry with the same funded transaction, and configure a profile-aware agreementEvaluator before completion or recovery. Low-level unverified recovery results must not authorize key storage or content access. DNS endpoints require an address-pinning connector outside trusted browser environments. Distributors must retain THIRD_PARTY_NOTICES.md with the package; published BRC-170 remains authoritative if the implementation and standard differ. | +| `@bsv/message-box-client` | `2.4.0` | `2.5.2` | minor | [API and usage](../packages/messaging/message-box-client.md) | No valid Message Box wire format or existing method signature changes. socketOptions, serverIdentityKeysByHost, and maximumPayment are optional additive configuration. Move non-loopback HTTP Message Box deployments to HTTPS; ordinary AuthFetch fallback responses, malformed wallet identities, server identity changes within one client instance, malformed quote/send response shapes, and mismatched payment transactions now fail closed. Applications needing durable identity continuity should configure independently validated serverIdentityKeysByHost pins; keys are normalized per URL origin. Different overlay origins may retain different server identities. Custom WalletInterface implementations used for paid sends must return canonical Atomic BEEF plus its matching transaction ID and must preserve the requested output order when randomizeOutputs is false. Batch payment shape is unchanged, but its single server output must carry the quoted per-recipient delivery fee multiplied by the number of allowed recipients; older underpaying batch implementations are rejected. Client diagnostics, including errors, are now disabled unless enableLogging is true and emit only fixed lifecycle events; applications that need request correlation should add their own non-sensitive identifier rather than restoring raw wallet or message values. Upgrade @bsv/sdk and @bsv/message-box-client together; historical number-array wallets, current Uint8Array substrates, and already-pending numeric-key messages interoperate through the same portable transaction form. Distributors who copy the UMD file must keep THIRD_PARTY_NOTICES.md and LICENSES/ with it. No migration is required for the bundled payment optimization. Module consumers can use SDK 2.5.0 or later to enable the same optional extension; existing compatible SDK peer versions remain supported. Recipients must advertise already-validated transaction IDs through x-bsv-payment-known-txids, an optional SDK extension rather than a standardized BRC-105 header; services that omit it retain existing payment behavior. Existing return shapes and payment envelopes remain unchanged. Failed or incomplete notification payments stay queued. Resolve uncertain refund-send outcomes before retrying; this patch adds ordering checks, not an exactly-once refund journal. listMessages/listMessagesLite envelope behavior and basket-insertion semantics remain tracked in issue #503. Previously trimmed message-box, message-ID, device-token, and device-ID values must now be supplied in their exact canonical form; valid canonical protocol values are unchanged. | +| `@bsv/overlay` | `2.3.1` | `2.6.1` | minor | [API and usage](../packages/overlays/overlay.md) | Stop writes and take coordinated SQL and lookup-store backups before running every new Knex migration. Preflight and reconcile exact duplicate (txid, outputIndex, topic) output keys and (txid, topic) applied-transaction keys from transaction, topic, and lookup evidence; the uniqueness migration intentionally fails rather than deleting security state. Apply topical uniqueness before the additive spentBy column and verify both before serving writes. Prefer roll-forward: older versions do not know these migration names, so an image-only rollback can fail migration-list validation. To restore an older version, keep writes stopped and either use the new migration source to reverse spentBy and topical uniqueness in reverse order after exporting and reconciling every spent/spentBy association, or restore coordinated pre-migration SQL and lookup-store backups. Configure canonical header resolution and public HTTPS endpoints for production network paths; custom storage, topic, discovery, and transport adapters must satisfy the new validation and resource bounds. External topic indexes are not made globally atomic by SQL submission serialization, so retain idempotent compensation for side effects outside the overlay database. Valid canonical overlay wire shapes remain supported. | +| `@bsv/overlay-discovery-services` | `2.2.1` | `2.2.5` | patch | [API and usage](../packages/overlays/overlay-discovery-services.md) | Valid canonical advertisements and bounded queries remain compatible. Code that used findAllAdvertisements to enumerate advertisements owned by other identities must use the appropriate public lookup service instead; the wallet helper is now an authenticated owner view. Private-network or plaintext production URIs, malformed tokens, unbounded query shapes, and wallet results that mutate inspected actions now fail closed. | +| `@bsv/overlay-express` | `2.6.1` | `2.7.2` | minor | [API and usage](../packages/overlays/overlay-express.md) | Set an ARC callback token of at least 32 bytes before enabling the ARC ingestion route and update the sender to present it. Private-network or plaintext outbound destinations require the explicit allowPrivateHosts development policy; production endpoints must use public HTTPS. Detailed health data is opt-in. Existing canonical public overlay requests and default credential-free CORS remain supported, but operators must roll out callback credentials and egress policy together across every replica. | +| `@bsv/overlay-topics` | `1.8.0` | `1.8.4` | patch | [API and usage](../packages/overlays/overlay-topics.md) | Topic and lookup identifiers and valid canonical wire encodings remain unchanged. Audit historical rows for malformed amounts, noncanonical identifiers, incomplete ownership or admin evidence, and ambiguous outpoint linkage before replay or rebuild. Custom state and screening providers must return exact booleans, compare identity keys case-insensitively where documented, conserve exact safe-integer value, and honor bounded query and result contracts. | +| `@bsv/paymail` | `2.4.2` | `2.4.9` | patch | [API and usage](../packages/messaging/paymail.md) | Valid public APIs and deployed Paymail wire shapes remain supported; malformed, ambiguous, oversized, local-network, wrong-owner, mutation-backed, or request-mismatched input now fails closed. Production origins and capability endpoints must be credential-free public HTTPS, except exact localhost development; custom transports and resolver configuration must use the documented exact runtime types. Receive-route verifySignature defaults to false for legacy compatibility, so unsigned metadata must not authorize a sender. Even when enabled, the legacy P2P signature authenticates only the transaction ID, not recipient, reference, sender-handle context, endpoint, freshness, or replay state. The historical 6745385c3fc0 advertisement is this package's compatibility behavior and is not the upstream signed/timestamped Basic Address Resolution assurance; a complete correction needs a versioned wire migration. Transaction Negotiation v1 is unauthenticated public input. Handlers must validate outputs, references, thread/freshness policy, proofs, callbacks, and durable replay state before financial or authorization effects. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | +| `@bsv/payment-express-middleware` | `2.1.1` | `2.1.7` | patch | [API and usage](../packages/middleware/payment-express-middleware.md) | No wire or public API migration is required; legacy x-bsv-payment JSON behavior remains supported, and Express 4 and 5 applications use their own peer-provided Express installation. Wallet adapters must return accepted and optional isMerge as own data properties; inherited/accessor-backed verdicts now fail closed. Overinclusive Atomic BEEF receipts are normalized to the declared subject closure. Production replicas must share one durable atomic replay store, and operators must reconcile a replay-store failure after wallet acceptance before asking a payer to spend again. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | +| `@bsv/sdk` | `2.7.1` | `2.8.0` | minor | [API and usage](../packages/sdk/bsv-sdk.md) | Existing TOTP.generate and TOTP.validate calls retain their historical two-digit, unpadded behavior and require no wire migration. New authentication flows should use generateSecure and validateSecure and store or transmit the six-character code as a string so leading zeroes are preserved. Ordinary valid BEEF, BRC-103 v0.1 peers, and public APIs remain compatible; malformed, ambiguous, oversized, identity-mismatched, or value-creating results now fail closed. Validated wallet results retain ordinary object behavior but are returned as owned value snapshots, so callers must not rely on object, array, or byte-buffer identity with the wallet adapter's raw response. Historical numeric-key JSON objects are recovered as bytes only for documented HTTP wallet byte fields; opaque numeric-key metadata remains an object. Deferred signableTransaction results may remain partial, and completed createAction results may use source values from the caller's immutable inputBEEF. Custom wallets must include direct source transactions for every other completed createAction or signAction input; duplicate input outpoints and unresolved or zero-input value-creating completed results are rejected. Browser applications that require DNS rebinding resistance must use a trusted egress proxy. | +| `@bsv/simple` | `0.5.3` | `0.6.0` | minor | [API and usage](../packages/helpers/simple.md) | Replace createServerWalletHandler() deployments with createServerWalletHandler({ authorize: async ({ action, headers }) => authenticatedSessionCanUseAction(headers, action) }). The callback must return literal true for each status, create, request, receive, balance, outputs, or reset action; omission now returns HTTP 403 for every action. Roll out the authentication layer and callback with the package, update anonymous probes or automation, and apply the same policy to every replica. Do not emulate the old public behavior with an unconditional authorize: () => true callback. Valid recipient derivations and authenticated Message Box peers remain supported; malformed, wrong-owner, or transaction-mutated flows now fail closed. New DID, CredentialSchema, and Certifier records use canonical 32-byte types. Current SDK wallet methods reject historical short types, so do not put migration aliases in wallet list, acquire, prove, or relinquish calls. Export affected records through the storage version that created them, authenticate them offline against the exact locally configured identifier, and reissue/import canonical replacements; no legacy certificate is rewritten automatically. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/. | +| `@bsv/templates` | `1.9.1` | `1.10.2` | minor | [API and usage](../packages/helpers/templates.md) | No API migration is required for valid templates. MandalaToken now rejects locking or decoding amounts outside JavaScript's positive safe-integer range; audit any previously accepted non-exact amount scripts before replay. New R1K1Wallet consumers await lock(), retain each private 32-byte salt, and provide a PIV signer that signs the supplied digest directly without hashing it again. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | +| `@bsv/teranode-listener` | `1.1.1` | `1.1.6` | patch | [API and usage](../packages/network/teranode-listener.md) | No API migration is required for valid consumers: raw callbacks remain the default and decoding is opt-in with decodeMessages: true. Configuration arrays and callbacks are snapshotted at construction, boolean controls must be literal booleans, and malformed or duplicate topics, addresses, keys, and unsupported properties now fail closed. usePrivateDHT: false now actually omits the DHT service. The published mainnet PNET value is transport compatibility data, not a publisher credential; decoded sender and payload fields remain untrusted and security-critical claims require independent validation. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | +| `@bsv/verifast` | `0.3.5` | `0.3.6` | patch | [API and usage](../packages/sdk/verifast.md) | Valid typed verification calls and worker protocols remain compatible. Custom module factories and WASM adapters must return the exact documented binary and boolean shapes; coercive network, height, flag, byte, batch, verdict, lifecycle, or disposed-instance values now fail closed. Keep THIRD_PARTY_NOTICES.md and LICENSES/ with every JavaScript and WebAssembly distribution. | +| `@bsv/wallet-helper` | `0.1.7` | `0.1.8` | patch | [API and usage](../packages/helpers/wallet-helper.md) | getAddress now derives with forSelf: true and returns the caller-owned side of the bilateral relationship. Applications that stored or coordinated the previous peer-owned result must regenerate and exchange the corrected address before sending value. Amount must be an integer from 1 through 1,000 and counterparties must be valid public keys. Valid canonical transaction-builder flows remain supported; malformed, ambiguous, or wallet-mutated results now fail closed. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | +| `@bsv/wallet-relay` | `0.3.6` | `0.5.1` | minor | [API and usage](../packages/wallet/wallet-relay.md) | Enable signed QR codes and distribute only HTTPS pairing origins and relay API URLs. Root-relative API paths and loopback HTTP remain supported. Configure onApprovalRequired for every method not deliberately listed in autoApproveMethods; unsigned pairing URIs and implicit approval are no longer accepted. Existing relay sessions, custom RPC method names, and supported wallet RPC byte encodings remain valid, and host applications continue to provide their matching Express runtime and type graph. Version 0.5 defaults to two missed pongs; set maxMissedHeartbeats to 1 to retain the prior heartbeat policy. Heartbeat intervals must fit the Node timer range. Preserve Cache-Control: no-store at proxies, prefer the stable bsv-wallet-relay plus token WebSocket subprotocols over the legacy token query parameter, and configure edge rate limiting in addition to the bounded in-process defaults. The 24-hour connected-session lifetime no longer renews on reconnect, malformed wallet calls fail with code 400, and WalletRelayClient retains at most 100 log entries unless maxLogEntries is configured. The QR remains a short-lived bearer invitation: keep it private and require explicit operation approval. Relay envelopes, cryptographic framing, and wallet RPC encodings remain unchanged. | +| `@bsv/wallet-toolbox` | `2.12.0` | `2.13.2` | minor | [API and usage](../packages/wallet/wallet-toolbox.md) | No consumer, wire, or database migration is required for canonical proof validation and retry handling. Custom WalletServices implementations may omit getValidatedMerklePath; stale RPC proof recovery then uses one fully validated getMerklePath lookup. Sync transfer support requires the 2.13.0 candidate or a later release containing it on the client and each relevant provider; published 2.12.0 does not contain this extension. Providers must run additive migration 2026-09-09-001 for two bounded staging tables before advertising transfer version 1; syncTransfers: false supports a mixed-version rollout. For rollback, stop transfer traffic and use the new migration source to reverse only this staging migration and its ledger entry before restarting older code; preserve all current wallet records. Existing ordinary-page and export file contracts are retained. IndexedDB upgrades automatically to version 6 with a non-unique transaction-ID/user index, preserving existing data and duplicate transaction IDs. Older clients requesting schema version 6 cannot reopen the upgraded database; retain a compatible client for local backups. Compact checkpoints are advertised through runtime settings; older providers retain the full-state path. No ID-map data is removed and existing wire defaults remain compatible. Existing sync peers remain compatible; additive fields are optional. Run the normal Knex migration for source indexes. Only idempotent getSyncChunk reads retry after HTTP 413. Existing actions, ordinary noSend calls, permission modules, UMP v3 tokens, and active WAB accounts require no client migration. Deploy the additive WAB registration-status migration and WAB routes before relying on interrupted-signup recovery; older servers and clients retain their prior wire behavior. To use BRC-177, migrate every active Knex store before serving requests and run the default Wallet Toolbox monitor; IndexedDB upgrades automatically to schema version 6. Upgrade signer, active storage service, and remote monitor together to 2.11.0 or later; older remote storage is rejected before prefunding. The Knex migration also adds rebuildable prepared-BEEF and proof-epoch tables with every COOK control disabled. Validate the migration on MySQL before release and the cross-process epoch fence on non-production PXC before enabling writes. Roll out writes before reads, use backfill only after database review, and disable all three flags to roll back. Delete derived prepared rows before downgrading to code that cannot advance the epoch. Semantic modules may add handleRequest; hosts installing @bsv/ecpm-permission-module register it under the ecpm scheme. Argon2id tokens keep the same parameters and derived bytes across WebAssembly and JavaScript runtimes. Host registration is available from each package root; concurrent cold calls share one preload attempt, and hosts must make readiness/preload reentrant and cache permanent failures or back off retries. Native and JavaScript results share byte-type and exact-length validation; unrelated hash-wasm errors still propagate without WebAssembly. Canonical AtomicBEEF and number-array behavior are unchanged; use @bsv/sdk 2.4.2 or later, use docs/storage.md instead of the removed JSight export, and retain THIRD_PARTY_NOTICES.md and LICENSES/. Replace non-loopback HTTP storage and Arcade SSE endpoints with HTTPS. Snapshot APIs and formats are unchanged, but applications must store each complete snapshot in an OS Keychain, hardware-backed keystore, or comparably trusted secret store because possession of it grants wallet access. Apply every ChainTracks Knex migration before serving traffic. The MySQL repair clears only the rebuildable live-header cache while widening legacy truncating identifier columns to VARCHAR(64), retains authenticated bulk data, and adds the transaction lock row; allow the tracker to repopulate live headers before declaring it healthy. To downgrade, stop all ChainTracks writes, take and verify a database and authenticated-bulk-data backup, then use the current ChaintracksKnexMigrations source to roll down only the 2026-09-17 repair migration ledger entry. Its down step intentionally leaves the repaired VARCHAR(64) and LONGBLOB schema, chaintracks_state lock row, and authenticated bulk files intact. Start older code only after validating that retained schema and data in a non-production copy; never roll down the initial migration, recreate the tables, or restore truncating VARBINARY(32) identifier columns. Existing ChainTracks wire and public API contracts are unchanged. Existing logging integrations remain source compatible, but applications that relied on implicit console output must supply the optional logging callback explicitly. Configure durable download and cache lock timeouts deliberately; neither crash-abandoned lock is reclaimed automatically, so prove no writer remains before removing only the affected lock directory. Custom ChaintracksStorageBulkFileApi implementations must add atomic replaceBulkFiles support before multi-file reconciliation or replacement; older custom adapters now fail closed for those operations. MonitorOptions.maxQueuedDeactivatedHeaders is additive and defaults to 4096; lower it for constrained hosts. Arcade SSE event, pending-count, and pending-byte limits are additive and default to 262144 bytes, 64 events, and 4194304 bytes; lower them for constrained hosts. MonitorOptions.logging and ArcSSEClientOptions.log are additive and replace prior implicit library console output when observability is required. The migration-atomicity fix is included in the existing unpublished 2.13.2 candidate and requires no new schema migration. It prevents future partial migrations; a database already left with unjournaled schema objects by an older version still needs operator-reviewed recovery from a verified backup or an exact schema/journal reconciliation. Never delete migration journal rows or wallet data blindly. | +| `@bsv/wallet-toolbox-client` | `2.12.0` | `2.13.2` | minor | [API and usage](../packages/wallet/wallet-toolbox-client.md) | Custom WalletServices implementations may omit getValidatedMerklePath; stale RPC proof recovery then uses one fully validated getMerklePath lookup. Sync transfer support requires the 2.13.0 candidate or a later release containing it on the client and each relevant provider; published 2.12.0 does not contain this extension. Providers must run additive migration 2026-09-09-001 for two bounded staging tables before advertising transfer version 1; syncTransfers: false supports a mixed-version rollout. For rollback, stop transfer traffic and use the new migration source to reverse only this staging migration and its ledger entry before restarting older code; preserve all current wallet records. Existing ordinary-page and export file contracts are retained. IndexedDB upgrades automatically to version 6 with a non-unique transaction-ID/user index, preserving existing data and duplicate transaction IDs. Older clients requesting schema version 6 cannot reopen the upgraded database; retain a compatible client for local backups. Compact checkpoints are advertised through runtime settings; older providers retain the full-state path. No ID-map data is removed and existing wire defaults remain compatible. Existing sync peers remain compatible; additive fields are optional. Run the normal Knex migration for source indexes. Only idempotent getSyncChunk reads retry after HTTP 413. Existing browser actions, permission modules, UMP v3 tokens, and active WAB accounts require no client migration; IndexedDB upgrades automatically. Deploy the additive WAB registration-status migration and WAB routes before relying on interrupted-signup recovery; older servers and clients retain their prior wire behavior. To use BRC-177 with remote storage, upgrade the active storage service and its default monitor to Wallet Toolbox 2.11.0 or later before upgrading clients; an older server is rejected before prefunding. Prepared BEEF persistence and rollout controls apply only to the full package's Knex provider, so IndexedDB and remote clients require no COOK configuration. Semantic modules may add handleRequest; installing @bsv/ecpm-permission-module requires registration under the ecpm scheme. Argon2id tokens keep the same parameters and derived bytes across WebAssembly and JavaScript runtimes. Host registration is available from each package root; concurrent cold calls share a preload attempt, and hosts must make readiness/preload reentrant and cache permanent failures or back off retries. Native and JavaScript results share byte-type and exact-length validation; unrelated hash-wasm errors still propagate without WebAssembly. Browser exports, wire types, canonical AtomicBEEF behavior, and pagination contracts are unchanged; use @bsv/sdk 2.4.2 or later and retain THIRD_PARTY_NOTICES.md and LICENSES/. Replace non-loopback HTTP storage and Arcade SSE endpoints with HTTPS. Snapshot APIs and formats are unchanged, but store each complete snapshot only through browser or extension storage backed by an OS Keychain or comparably trusted secret store. | +| `@bsv/wallet-toolbox-mobile` | `2.12.0` | `2.13.2` | minor | [API and usage](../packages/wallet/wallet-toolbox-mobile.md) | Custom WalletServices implementations may omit getValidatedMerklePath; stale RPC proof recovery then uses one fully validated getMerklePath lookup. Sync transfer support requires the 2.13.0 candidate or a later release containing it on the client and each relevant provider; published 2.12.0 does not contain this extension. Providers must run additive migration 2026-09-09-001 for two bounded staging tables before advertising transfer version 1; syncTransfers: false supports a mixed-version rollout. For rollback, stop transfer traffic and use the new migration source to reverse only this staging migration and its ledger entry before restarting older code; preserve all current wallet records. Existing ordinary-page and export file contracts are retained. Compact checkpoints are advertised through runtime settings; older providers retain the full-state path. No ID-map data is removed and existing wire defaults remain compatible. Existing sync peers remain compatible; additive fields are optional. Run the normal Knex migration for source indexes. Only idempotent getSyncChunk reads retry after HTTP 413. Existing mobile actions, permission modules, UMP v3 tokens, and active WAB accounts require no client migration. Deploy the additive WAB registration-status migration and WAB routes before relying on interrupted-signup recovery; older servers and clients retain their prior wire behavior. To use BRC-177, migrate and upgrade the active remote storage service and its default monitor to Wallet Toolbox 2.11.0 or later before upgrading clients; an older server is rejected before prefunding. Prepared BEEF persistence and rollout controls apply only to the full package's Knex provider, so mobile remote clients require no COOK configuration. Semantic modules may add handleRequest without changing the Wallet interface. Argon2id tokens keep the same parameters and derived bytes across WebAssembly and JavaScript runtimes; no user device setting is required. Host registration is available from the mobile root; concurrent cold calls share a preload attempt, and hosts must make readiness/preload reentrant and cache permanent failures or back off retries. Native and JavaScript results share byte-type and exact-length validation; unrelated hash-wasm errors still propagate without WebAssembly. React Native exports, wire types, and canonical AtomicBEEF behavior are unchanged; use @bsv/sdk 2.4.2 or later and retain THIRD_PARTY_NOTICES.md and LICENSES/. Replace non-loopback HTTP storage and Arcade SSE endpoints with HTTPS. Snapshot APIs and formats are unchanged, but store each complete snapshot in the iOS Keychain, Android Keystore-backed encrypted storage, or a comparably trusted secret store. | +| `create-bsv-app` | `1.1.1` | `1.1.2` | patch | [API and usage](../packages/helpers/create-bsv-app.md) | Existing CLI flags, network choices, and generated project structure are unchanged. Regenerate or update dependencies after the patched packages are published. Distributors must retain THIRD_PARTY_NOTICES.md and LICENSES/ with the package. | `none` means the source manifest matches the recorded npm baseline. Any other value is an unpublished candidate. Publication, tags, releases, registry @@ -523,8 +523,8 @@ CLI entry points: `{"wallet-relay":"./bin/init.mjs"}`. - Package documentation: [docs/packages/wallet/wallet-toolbox.md](../packages/wallet/wallet-toolbox.md) - Source: [packages/wallet/wallet-toolbox](https://github.com/bsv-blockchain/ts-stack/tree/main/packages/wallet/wallet-toolbox) -- Release note: Validates every acquired Merkle proof against the active ChainTracks root, fails over when a provider returns an orphan proof, and retries unresolved reorganization heights without stopping the forward review cursor. Accepts valid compound proofs with multiple marked transactions, rotates failed retries behind waiting heights across restarts, and preserves temporarily ineligible retries when the chain tip retreats. Reconciles stale RPC sync proofs only after full validation of current proof metadata, preserving raw transactions and wallet references. Adds optional getValidatedMerklePath provider failover with independent per-call provider traversal. Bounds concurrent authenticated proof validation to eight checks and drains failures before rejecting a page. Adds conservative initial sync pages and per-copy adaptation to committed page latency, with proof-work limits independent of byte budgets. Adds negotiated bounded sync transfers, full-frame integrity verification, resumable staged uploads and checkpoint replay protection for oversized records. Adds IndexedDB schema version 6 and bounded sync identity/relation and indexed proof-batch lookups to avoid full-wallet scans during large local restores. Uses negotiated binary JSON for large schema-defined sync response byte arrays while retaining legacy wire arrays. Adds negotiated compact committed sync checkpoints and faster binary JSON parsing without scalar-byte reviver callbacks. Adds adaptive wallet-storage sync reads, optional progress totals, disambiguated checkpoints, bounded HTTP 413 recovery, and MySQL/SQLite source indexes while retaining legacy peer compatibility. Adds the built-in BRC-177 noSend-expiry reference implementation with exact prefunding, durable pre-signed reclaim, atomic active-storage monitoring, backoff-controlled recovery, cross-device lifecycle synchronization, and proof-finalized race handling. Adds opt-in prepared BEEF storage for Knex-backed normal createAction funding: verified, checksummed proof closures are persisted after foreground completion and reused on later hits, while broad lookups, misses, and cache failures retain the canonical path. Reads, writes, bounded queueing, and gradual backfill default off; reorganizations stale derived rows and fence in-flight cross-process writes with a database proof epoch. Makes new WAB-to-UMP registrations interruption-safe through an explicit pending lifecycle and idempotent finalization while active and legacy account mismatches remain fail-closed. Also adds the optional semantic handleRequest hook for BRC-98/99/111 permission modules, an interoperable asynchronous JavaScript Argon2id fallback when WebAssembly is unavailable, and an optional proven-ready native Argon2id backend for host runtimes, retains BRC-95/BRC-100 compatibility and stable bounded pagination, removes the obsolete JSight application bundle, and preserves the earlier Open BSV grant. Prevents reuse or coalescing of spending approvals, verifies certificate signatures fail closed during direct acquisition, issuer acquisition, and overlay discovery, paginates complete spending history, requires HTTPS for credential-bearing transports, prevents credential logging, documents snapshots as wallet-equivalent secrets, validates every remote ChainTracks and WhatsOnChain header under bounded HTTP, stream, manifest, and WebSocket controls, requires public HTTPS for service-discovered CDN links, authenticates proof of work before chain-work selection, and caps/copy-isolates submitted and live-header queues. Isolates and authenticates local ChainTracks storage, serializes tip mutation, repairs MySQL header/blob types, strictly validates bulk manifests and stored metadata, and bounds legacy readers, filesystem I/O, exporters, and lock queues. Prevents rejected, stale, or caller-written Block Headers Service Merkle roots from becoming authoritative; validates and bounds its canonical header responses; validates ChainTracks construction limits; and makes library logging opt-in. Makes ChainTracks startup failures awaited and retryable, destruction complete, event subscriptions bounded and isolated, provider results and diagnostics safe, asynchronous validation inputs owned, durable download and cache mutations cross-process serialized, low-level header/work primitives canonical, and destructive migration failures visible. Commits built-in bulk-file replacements atomically, keeps memory behind durable writes, rejects remote local identities and inconsistent event topology, and bounds remote-client subscriptions, timers, metadata, and diagnostics. Independently validates and bounds wallet-monitor reorganization work, coalesces prepared-proof invalidation, cleans up partial subscriptions, and contains host callback failures. Serializes and bounds Arcade SSE admission and cursor commits, prevents successor dispatch after failure, and repairs monitor/daemon startup, task setup, diagnostic, and teardown lifecycle. Binds every user-scoped synchronization subquery and storage-identity migration value rather than interpolating runtime values into SQL. -- Migration: No consumer, wire, or database migration is required for canonical proof validation and retry handling. Custom WalletServices implementations may omit getValidatedMerklePath; stale RPC proof recovery then uses one fully validated getMerklePath lookup. Sync transfer support requires the 2.13.0 candidate or a later release containing it on the client and each relevant provider; published 2.12.0 does not contain this extension. Providers must run additive migration 2026-09-09-001 for two bounded staging tables before advertising transfer version 1; syncTransfers: false supports a mixed-version rollout. For rollback, stop transfer traffic and use the new migration source to reverse only this staging migration and its ledger entry before restarting older code; preserve all current wallet records. Existing ordinary-page and export file contracts are retained. IndexedDB upgrades automatically to version 6 with a non-unique transaction-ID/user index, preserving existing data and duplicate transaction IDs. Older clients requesting schema version 6 cannot reopen the upgraded database; retain a compatible client for local backups. Compact checkpoints are advertised through runtime settings; older providers retain the full-state path. No ID-map data is removed and existing wire defaults remain compatible. Existing sync peers remain compatible; additive fields are optional. Run the normal Knex migration for source indexes. Only idempotent getSyncChunk reads retry after HTTP 413. Existing actions, ordinary noSend calls, permission modules, UMP v3 tokens, and active WAB accounts require no client migration. Deploy the additive WAB registration-status migration and WAB routes before relying on interrupted-signup recovery; older servers and clients retain their prior wire behavior. To use BRC-177, migrate every active Knex store before serving requests and run the default Wallet Toolbox monitor; IndexedDB upgrades automatically to schema version 6. Upgrade signer, active storage service, and remote monitor together to 2.11.0 or later; older remote storage is rejected before prefunding. The Knex migration also adds rebuildable prepared-BEEF and proof-epoch tables with every COOK control disabled. Validate the migration on MySQL before release and the cross-process epoch fence on non-production PXC before enabling writes. Roll out writes before reads, use backfill only after database review, and disable all three flags to roll back. Delete derived prepared rows before downgrading to code that cannot advance the epoch. Semantic modules may add handleRequest; hosts installing @bsv/ecpm-permission-module register it under the ecpm scheme. Argon2id tokens keep the same parameters and derived bytes across WebAssembly and JavaScript runtimes. Host registration is available from each package root; concurrent cold calls share one preload attempt, and hosts must make readiness/preload reentrant and cache permanent failures or back off retries. Native and JavaScript results share byte-type and exact-length validation; unrelated hash-wasm errors still propagate without WebAssembly. Canonical AtomicBEEF and number-array behavior are unchanged; use @bsv/sdk 2.4.2 or later, use docs/storage.md instead of the removed JSight export, and retain THIRD_PARTY_NOTICES.md and LICENSES/. Replace non-loopback HTTP storage and Arcade SSE endpoints with HTTPS. Snapshot APIs and formats are unchanged, but applications must store each complete snapshot in an OS Keychain, hardware-backed keystore, or comparably trusted secret store because possession of it grants wallet access. Apply every ChainTracks Knex migration before serving traffic. The MySQL repair clears only the rebuildable live-header cache while widening legacy truncating identifier columns to VARCHAR(64), retains authenticated bulk data, and adds the transaction lock row; allow the tracker to repopulate live headers before declaring it healthy. To downgrade, stop all ChainTracks writes, take and verify a database and authenticated-bulk-data backup, then use the current ChaintracksKnexMigrations source to roll down only the 2026-09-17 repair migration ledger entry. Its down step intentionally leaves the repaired VARCHAR(64) and LONGBLOB schema, chaintracks_state lock row, and authenticated bulk files intact. Start older code only after validating that retained schema and data in a non-production copy; never roll down the initial migration, recreate the tables, or restore truncating VARBINARY(32) identifier columns. Existing ChainTracks wire and public API contracts are unchanged. Existing logging integrations remain source compatible, but applications that relied on implicit console output must supply the optional logging callback explicitly. Configure durable download and cache lock timeouts deliberately; neither crash-abandoned lock is reclaimed automatically, so prove no writer remains before removing only the affected lock directory. Custom ChaintracksStorageBulkFileApi implementations must add atomic replaceBulkFiles support before multi-file reconciliation or replacement; older custom adapters now fail closed for those operations. MonitorOptions.maxQueuedDeactivatedHeaders is additive and defaults to 4096; lower it for constrained hosts. Arcade SSE event, pending-count, and pending-byte limits are additive and default to 262144 bytes, 64 events, and 4194304 bytes; lower them for constrained hosts. MonitorOptions.logging and ArcSSEClientOptions.log are additive and replace prior implicit library console output when observability is required. +- Release note: Validates every acquired Merkle proof against the active ChainTracks root, fails over when a provider returns an orphan proof, and retries unresolved reorganization heights without stopping the forward review cursor. Accepts valid compound proofs with multiple marked transactions, rotates failed retries behind waiting heights across restarts, and preserves temporarily ineligible retries when the chain tip retreats. Reconciles stale RPC sync proofs only after full validation of current proof metadata, preserving raw transactions and wallet references. Adds optional getValidatedMerklePath provider failover with independent per-call provider traversal. Bounds concurrent authenticated proof validation to eight checks and drains failures before rejecting a page. Adds conservative initial sync pages and per-copy adaptation to committed page latency, with proof-work limits independent of byte budgets. Adds negotiated bounded sync transfers, full-frame integrity verification, resumable staged uploads and checkpoint replay protection for oversized records. Adds IndexedDB schema version 6 and bounded sync identity/relation and indexed proof-batch lookups to avoid full-wallet scans during large local restores. Uses negotiated binary JSON for large schema-defined sync response byte arrays while retaining legacy wire arrays. Adds negotiated compact committed sync checkpoints and faster binary JSON parsing without scalar-byte reviver callbacks. Adds adaptive wallet-storage sync reads, optional progress totals, disambiguated checkpoints, bounded HTTP 413 recovery, and MySQL/SQLite source indexes while retaining legacy peer compatibility. Adds the built-in BRC-177 noSend-expiry reference implementation with exact prefunding, durable pre-signed reclaim, atomic active-storage monitoring, backoff-controlled recovery, cross-device lifecycle synchronization, and proof-finalized race handling. Adds opt-in prepared BEEF storage for Knex-backed normal createAction funding: verified, checksummed proof closures are persisted after foreground completion and reused on later hits, while broad lookups, misses, and cache failures retain the canonical path. Reads, writes, bounded queueing, and gradual backfill default off; reorganizations stale derived rows and fence in-flight cross-process writes with a database proof epoch. Makes new WAB-to-UMP registrations interruption-safe through an explicit pending lifecycle and idempotent finalization while active and legacy account mismatches remain fail-closed. Also adds the optional semantic handleRequest hook for BRC-98/99/111 permission modules, an interoperable asynchronous JavaScript Argon2id fallback when WebAssembly is unavailable, and an optional proven-ready native Argon2id backend for host runtimes, retains BRC-95/BRC-100 compatibility and stable bounded pagination, removes the obsolete JSight application bundle, and preserves the earlier Open BSV grant. Prevents reuse or coalescing of spending approvals, verifies certificate signatures fail closed during direct acquisition, issuer acquisition, and overlay discovery, paginates complete spending history, requires HTTPS for credential-bearing transports, prevents credential logging, documents snapshots as wallet-equivalent secrets, validates every remote ChainTracks and WhatsOnChain header under bounded HTTP, stream, manifest, and WebSocket controls, requires public HTTPS for service-discovered CDN links, authenticates proof of work before chain-work selection, and caps/copy-isolates submitted and live-header queues. Isolates and authenticates local ChainTracks storage, serializes tip mutation, repairs MySQL header/blob types, strictly validates bulk manifests and stored metadata, and bounds legacy readers, filesystem I/O, exporters, and lock queues. Prevents rejected, stale, or caller-written Block Headers Service Merkle roots from becoming authoritative; validates and bounds its canonical header responses; validates ChainTracks construction limits; and makes library logging opt-in. Makes ChainTracks startup failures awaited and retryable, destruction complete, event subscriptions bounded and isolated, provider results and diagnostics safe, asynchronous validation inputs owned, durable download and cache mutations cross-process serialized, low-level header/work primitives canonical, and destructive migration failures visible. Commits built-in bulk-file replacements atomically, keeps memory behind durable writes, rejects remote local identities and inconsistent event topology, and bounds remote-client subscriptions, timers, metadata, and diagnostics. Independently validates and bounds wallet-monitor reorganization work, coalesces prepared-proof invalidation, cleans up partial subscriptions, and contains host callback failures. Serializes and bounds Arcade SSE admission and cursor commits, prevents successor dispatch after failure, and repairs monitor/daemon startup, task setup, diagnostic, and teardown lifecycle. Binds every user-scoped synchronization subquery and storage-identity migration value rather than interpolating runtime values into SQL. Restores transactional SQLite schema migrations so interrupted DDL and its migration journal entry roll back together, while restoring foreign-key enforcement on success or failure. +- Migration: No consumer, wire, or database migration is required for canonical proof validation and retry handling. Custom WalletServices implementations may omit getValidatedMerklePath; stale RPC proof recovery then uses one fully validated getMerklePath lookup. Sync transfer support requires the 2.13.0 candidate or a later release containing it on the client and each relevant provider; published 2.12.0 does not contain this extension. Providers must run additive migration 2026-09-09-001 for two bounded staging tables before advertising transfer version 1; syncTransfers: false supports a mixed-version rollout. For rollback, stop transfer traffic and use the new migration source to reverse only this staging migration and its ledger entry before restarting older code; preserve all current wallet records. Existing ordinary-page and export file contracts are retained. IndexedDB upgrades automatically to version 6 with a non-unique transaction-ID/user index, preserving existing data and duplicate transaction IDs. Older clients requesting schema version 6 cannot reopen the upgraded database; retain a compatible client for local backups. Compact checkpoints are advertised through runtime settings; older providers retain the full-state path. No ID-map data is removed and existing wire defaults remain compatible. Existing sync peers remain compatible; additive fields are optional. Run the normal Knex migration for source indexes. Only idempotent getSyncChunk reads retry after HTTP 413. Existing actions, ordinary noSend calls, permission modules, UMP v3 tokens, and active WAB accounts require no client migration. Deploy the additive WAB registration-status migration and WAB routes before relying on interrupted-signup recovery; older servers and clients retain their prior wire behavior. To use BRC-177, migrate every active Knex store before serving requests and run the default Wallet Toolbox monitor; IndexedDB upgrades automatically to schema version 6. Upgrade signer, active storage service, and remote monitor together to 2.11.0 or later; older remote storage is rejected before prefunding. The Knex migration also adds rebuildable prepared-BEEF and proof-epoch tables with every COOK control disabled. Validate the migration on MySQL before release and the cross-process epoch fence on non-production PXC before enabling writes. Roll out writes before reads, use backfill only after database review, and disable all three flags to roll back. Delete derived prepared rows before downgrading to code that cannot advance the epoch. Semantic modules may add handleRequest; hosts installing @bsv/ecpm-permission-module register it under the ecpm scheme. Argon2id tokens keep the same parameters and derived bytes across WebAssembly and JavaScript runtimes. Host registration is available from each package root; concurrent cold calls share one preload attempt, and hosts must make readiness/preload reentrant and cache permanent failures or back off retries. Native and JavaScript results share byte-type and exact-length validation; unrelated hash-wasm errors still propagate without WebAssembly. Canonical AtomicBEEF and number-array behavior are unchanged; use @bsv/sdk 2.4.2 or later, use docs/storage.md instead of the removed JSight export, and retain THIRD_PARTY_NOTICES.md and LICENSES/. Replace non-loopback HTTP storage and Arcade SSE endpoints with HTTPS. Snapshot APIs and formats are unchanged, but applications must store each complete snapshot in an OS Keychain, hardware-backed keystore, or comparably trusted secret store because possession of it grants wallet access. Apply every ChainTracks Knex migration before serving traffic. The MySQL repair clears only the rebuildable live-header cache while widening legacy truncating identifier columns to VARCHAR(64), retains authenticated bulk data, and adds the transaction lock row; allow the tracker to repopulate live headers before declaring it healthy. To downgrade, stop all ChainTracks writes, take and verify a database and authenticated-bulk-data backup, then use the current ChaintracksKnexMigrations source to roll down only the 2026-09-17 repair migration ledger entry. Its down step intentionally leaves the repaired VARCHAR(64) and LONGBLOB schema, chaintracks_state lock row, and authenticated bulk files intact. Start older code only after validating that retained schema and data in a non-production copy; never roll down the initial migration, recreate the tables, or restore truncating VARBINARY(32) identifier columns. Existing ChainTracks wire and public API contracts are unchanged. Existing logging integrations remain source compatible, but applications that relied on implicit console output must supply the optional logging callback explicitly. Configure durable download and cache lock timeouts deliberately; neither crash-abandoned lock is reclaimed automatically, so prove no writer remains before removing only the affected lock directory. Custom ChaintracksStorageBulkFileApi implementations must add atomic replaceBulkFiles support before multi-file reconciliation or replacement; older custom adapters now fail closed for those operations. MonitorOptions.maxQueuedDeactivatedHeaders is additive and defaults to 4096; lower it for constrained hosts. Arcade SSE event, pending-count, and pending-byte limits are additive and default to 262144 bytes, 64 events, and 4194304 bytes; lower them for constrained hosts. MonitorOptions.logging and ArcSSEClientOptions.log are additive and replace prior implicit library console output when observability is required. The migration-atomicity fix is included in the existing unpublished 2.13.2 candidate and requires no new schema migration. It prevents future partial migrations; a database already left with unjournaled schema objects by an older version still needs operator-reviewed recovery from a verified backup or an exact schema/journal reconciliation. Never delete migration journal rows or wallet data blindly. | Public subpath | Runtime target(s) | Declaration target(s) | | ---------------- | ---------------------------------------------------- | -------------------------- | diff --git a/governance/package-release-notes.json b/governance/package-release-notes.json index afc518636..b89de0644 100644 --- a/governance/package-release-notes.json +++ b/governance/package-release-notes.json @@ -217,8 +217,8 @@ "name": "@bsv/wallet-toolbox", "publishedVersion": "2.12.0", "releaseType": "minor", - "summary": "Validates every acquired Merkle proof against the active ChainTracks root, fails over when a provider returns an orphan proof, and retries unresolved reorganization heights without stopping the forward review cursor. Accepts valid compound proofs with multiple marked transactions, rotates failed retries behind waiting heights across restarts, and preserves temporarily ineligible retries when the chain tip retreats. Reconciles stale RPC sync proofs only after full validation of current proof metadata, preserving raw transactions and wallet references. Adds optional getValidatedMerklePath provider failover with independent per-call provider traversal. Bounds concurrent authenticated proof validation to eight checks and drains failures before rejecting a page. Adds conservative initial sync pages and per-copy adaptation to committed page latency, with proof-work limits independent of byte budgets. Adds negotiated bounded sync transfers, full-frame integrity verification, resumable staged uploads and checkpoint replay protection for oversized records. Adds IndexedDB schema version 6 and bounded sync identity/relation and indexed proof-batch lookups to avoid full-wallet scans during large local restores. Uses negotiated binary JSON for large schema-defined sync response byte arrays while retaining legacy wire arrays. Adds negotiated compact committed sync checkpoints and faster binary JSON parsing without scalar-byte reviver callbacks. Adds adaptive wallet-storage sync reads, optional progress totals, disambiguated checkpoints, bounded HTTP 413 recovery, and MySQL/SQLite source indexes while retaining legacy peer compatibility. Adds the built-in BRC-177 noSend-expiry reference implementation with exact prefunding, durable pre-signed reclaim, atomic active-storage monitoring, backoff-controlled recovery, cross-device lifecycle synchronization, and proof-finalized race handling. Adds opt-in prepared BEEF storage for Knex-backed normal createAction funding: verified, checksummed proof closures are persisted after foreground completion and reused on later hits, while broad lookups, misses, and cache failures retain the canonical path. Reads, writes, bounded queueing, and gradual backfill default off; reorganizations stale derived rows and fence in-flight cross-process writes with a database proof epoch. Makes new WAB-to-UMP registrations interruption-safe through an explicit pending lifecycle and idempotent finalization while active and legacy account mismatches remain fail-closed. Also adds the optional semantic handleRequest hook for BRC-98/99/111 permission modules, an interoperable asynchronous JavaScript Argon2id fallback when WebAssembly is unavailable, and an optional proven-ready native Argon2id backend for host runtimes, retains BRC-95/BRC-100 compatibility and stable bounded pagination, removes the obsolete JSight application bundle, and preserves the earlier Open BSV grant. Prevents reuse or coalescing of spending approvals, verifies certificate signatures fail closed during direct acquisition, issuer acquisition, and overlay discovery, paginates complete spending history, requires HTTPS for credential-bearing transports, prevents credential logging, documents snapshots as wallet-equivalent secrets, validates every remote ChainTracks and WhatsOnChain header under bounded HTTP, stream, manifest, and WebSocket controls, requires public HTTPS for service-discovered CDN links, authenticates proof of work before chain-work selection, and caps/copy-isolates submitted and live-header queues. Isolates and authenticates local ChainTracks storage, serializes tip mutation, repairs MySQL header/blob types, strictly validates bulk manifests and stored metadata, and bounds legacy readers, filesystem I/O, exporters, and lock queues. Prevents rejected, stale, or caller-written Block Headers Service Merkle roots from becoming authoritative; validates and bounds its canonical header responses; validates ChainTracks construction limits; and makes library logging opt-in. Makes ChainTracks startup failures awaited and retryable, destruction complete, event subscriptions bounded and isolated, provider results and diagnostics safe, asynchronous validation inputs owned, durable download and cache mutations cross-process serialized, low-level header/work primitives canonical, and destructive migration failures visible. Commits built-in bulk-file replacements atomically, keeps memory behind durable writes, rejects remote local identities and inconsistent event topology, and bounds remote-client subscriptions, timers, metadata, and diagnostics. Independently validates and bounds wallet-monitor reorganization work, coalesces prepared-proof invalidation, cleans up partial subscriptions, and contains host callback failures. Serializes and bounds Arcade SSE admission and cursor commits, prevents successor dispatch after failure, and repairs monitor/daemon startup, task setup, diagnostic, and teardown lifecycle. Binds every user-scoped synchronization subquery and storage-identity migration value rather than interpolating runtime values into SQL.", - "migration": "No consumer, wire, or database migration is required for canonical proof validation and retry handling. Custom WalletServices implementations may omit getValidatedMerklePath; stale RPC proof recovery then uses one fully validated getMerklePath lookup. Sync transfer support requires the 2.13.0 candidate or a later release containing it on the client and each relevant provider; published 2.12.0 does not contain this extension. Providers must run additive migration 2026-09-09-001 for two bounded staging tables before advertising transfer version 1; syncTransfers: false supports a mixed-version rollout. For rollback, stop transfer traffic and use the new migration source to reverse only this staging migration and its ledger entry before restarting older code; preserve all current wallet records. Existing ordinary-page and export file contracts are retained. IndexedDB upgrades automatically to version 6 with a non-unique transaction-ID/user index, preserving existing data and duplicate transaction IDs. Older clients requesting schema version 6 cannot reopen the upgraded database; retain a compatible client for local backups. Compact checkpoints are advertised through runtime settings; older providers retain the full-state path. No ID-map data is removed and existing wire defaults remain compatible. Existing sync peers remain compatible; additive fields are optional. Run the normal Knex migration for source indexes. Only idempotent getSyncChunk reads retry after HTTP 413. Existing actions, ordinary noSend calls, permission modules, UMP v3 tokens, and active WAB accounts require no client migration. Deploy the additive WAB registration-status migration and WAB routes before relying on interrupted-signup recovery; older servers and clients retain their prior wire behavior. To use BRC-177, migrate every active Knex store before serving requests and run the default Wallet Toolbox monitor; IndexedDB upgrades automatically to schema version 6. Upgrade signer, active storage service, and remote monitor together to 2.11.0 or later; older remote storage is rejected before prefunding. The Knex migration also adds rebuildable prepared-BEEF and proof-epoch tables with every COOK control disabled. Validate the migration on MySQL before release and the cross-process epoch fence on non-production PXC before enabling writes. Roll out writes before reads, use backfill only after database review, and disable all three flags to roll back. Delete derived prepared rows before downgrading to code that cannot advance the epoch. Semantic modules may add handleRequest; hosts installing @bsv/ecpm-permission-module register it under the ecpm scheme. Argon2id tokens keep the same parameters and derived bytes across WebAssembly and JavaScript runtimes. Host registration is available from each package root; concurrent cold calls share one preload attempt, and hosts must make readiness/preload reentrant and cache permanent failures or back off retries. Native and JavaScript results share byte-type and exact-length validation; unrelated hash-wasm errors still propagate without WebAssembly. Canonical AtomicBEEF and number-array behavior are unchanged; use @bsv/sdk 2.4.2 or later, use docs/storage.md instead of the removed JSight export, and retain THIRD_PARTY_NOTICES.md and LICENSES/. Replace non-loopback HTTP storage and Arcade SSE endpoints with HTTPS. Snapshot APIs and formats are unchanged, but applications must store each complete snapshot in an OS Keychain, hardware-backed keystore, or comparably trusted secret store because possession of it grants wallet access. Apply every ChainTracks Knex migration before serving traffic. The MySQL repair clears only the rebuildable live-header cache while widening legacy truncating identifier columns to VARCHAR(64), retains authenticated bulk data, and adds the transaction lock row; allow the tracker to repopulate live headers before declaring it healthy. To downgrade, stop all ChainTracks writes, take and verify a database and authenticated-bulk-data backup, then use the current ChaintracksKnexMigrations source to roll down only the 2026-09-17 repair migration ledger entry. Its down step intentionally leaves the repaired VARCHAR(64) and LONGBLOB schema, chaintracks_state lock row, and authenticated bulk files intact. Start older code only after validating that retained schema and data in a non-production copy; never roll down the initial migration, recreate the tables, or restore truncating VARBINARY(32) identifier columns. Existing ChainTracks wire and public API contracts are unchanged. Existing logging integrations remain source compatible, but applications that relied on implicit console output must supply the optional logging callback explicitly. Configure durable download and cache lock timeouts deliberately; neither crash-abandoned lock is reclaimed automatically, so prove no writer remains before removing only the affected lock directory. Custom ChaintracksStorageBulkFileApi implementations must add atomic replaceBulkFiles support before multi-file reconciliation or replacement; older custom adapters now fail closed for those operations. MonitorOptions.maxQueuedDeactivatedHeaders is additive and defaults to 4096; lower it for constrained hosts. Arcade SSE event, pending-count, and pending-byte limits are additive and default to 262144 bytes, 64 events, and 4194304 bytes; lower them for constrained hosts. MonitorOptions.logging and ArcSSEClientOptions.log are additive and replace prior implicit library console output when observability is required." + "summary": "Validates every acquired Merkle proof against the active ChainTracks root, fails over when a provider returns an orphan proof, and retries unresolved reorganization heights without stopping the forward review cursor. Accepts valid compound proofs with multiple marked transactions, rotates failed retries behind waiting heights across restarts, and preserves temporarily ineligible retries when the chain tip retreats. Reconciles stale RPC sync proofs only after full validation of current proof metadata, preserving raw transactions and wallet references. Adds optional getValidatedMerklePath provider failover with independent per-call provider traversal. Bounds concurrent authenticated proof validation to eight checks and drains failures before rejecting a page. Adds conservative initial sync pages and per-copy adaptation to committed page latency, with proof-work limits independent of byte budgets. Adds negotiated bounded sync transfers, full-frame integrity verification, resumable staged uploads and checkpoint replay protection for oversized records. Adds IndexedDB schema version 6 and bounded sync identity/relation and indexed proof-batch lookups to avoid full-wallet scans during large local restores. Uses negotiated binary JSON for large schema-defined sync response byte arrays while retaining legacy wire arrays. Adds negotiated compact committed sync checkpoints and faster binary JSON parsing without scalar-byte reviver callbacks. Adds adaptive wallet-storage sync reads, optional progress totals, disambiguated checkpoints, bounded HTTP 413 recovery, and MySQL/SQLite source indexes while retaining legacy peer compatibility. Adds the built-in BRC-177 noSend-expiry reference implementation with exact prefunding, durable pre-signed reclaim, atomic active-storage monitoring, backoff-controlled recovery, cross-device lifecycle synchronization, and proof-finalized race handling. Adds opt-in prepared BEEF storage for Knex-backed normal createAction funding: verified, checksummed proof closures are persisted after foreground completion and reused on later hits, while broad lookups, misses, and cache failures retain the canonical path. Reads, writes, bounded queueing, and gradual backfill default off; reorganizations stale derived rows and fence in-flight cross-process writes with a database proof epoch. Makes new WAB-to-UMP registrations interruption-safe through an explicit pending lifecycle and idempotent finalization while active and legacy account mismatches remain fail-closed. Also adds the optional semantic handleRequest hook for BRC-98/99/111 permission modules, an interoperable asynchronous JavaScript Argon2id fallback when WebAssembly is unavailable, and an optional proven-ready native Argon2id backend for host runtimes, retains BRC-95/BRC-100 compatibility and stable bounded pagination, removes the obsolete JSight application bundle, and preserves the earlier Open BSV grant. Prevents reuse or coalescing of spending approvals, verifies certificate signatures fail closed during direct acquisition, issuer acquisition, and overlay discovery, paginates complete spending history, requires HTTPS for credential-bearing transports, prevents credential logging, documents snapshots as wallet-equivalent secrets, validates every remote ChainTracks and WhatsOnChain header under bounded HTTP, stream, manifest, and WebSocket controls, requires public HTTPS for service-discovered CDN links, authenticates proof of work before chain-work selection, and caps/copy-isolates submitted and live-header queues. Isolates and authenticates local ChainTracks storage, serializes tip mutation, repairs MySQL header/blob types, strictly validates bulk manifests and stored metadata, and bounds legacy readers, filesystem I/O, exporters, and lock queues. Prevents rejected, stale, or caller-written Block Headers Service Merkle roots from becoming authoritative; validates and bounds its canonical header responses; validates ChainTracks construction limits; and makes library logging opt-in. Makes ChainTracks startup failures awaited and retryable, destruction complete, event subscriptions bounded and isolated, provider results and diagnostics safe, asynchronous validation inputs owned, durable download and cache mutations cross-process serialized, low-level header/work primitives canonical, and destructive migration failures visible. Commits built-in bulk-file replacements atomically, keeps memory behind durable writes, rejects remote local identities and inconsistent event topology, and bounds remote-client subscriptions, timers, metadata, and diagnostics. Independently validates and bounds wallet-monitor reorganization work, coalesces prepared-proof invalidation, cleans up partial subscriptions, and contains host callback failures. Serializes and bounds Arcade SSE admission and cursor commits, prevents successor dispatch after failure, and repairs monitor/daemon startup, task setup, diagnostic, and teardown lifecycle. Binds every user-scoped synchronization subquery and storage-identity migration value rather than interpolating runtime values into SQL. Restores transactional SQLite schema migrations so interrupted DDL and its migration journal entry roll back together, while restoring foreign-key enforcement on success or failure.", + "migration": "No consumer, wire, or database migration is required for canonical proof validation and retry handling. Custom WalletServices implementations may omit getValidatedMerklePath; stale RPC proof recovery then uses one fully validated getMerklePath lookup. Sync transfer support requires the 2.13.0 candidate or a later release containing it on the client and each relevant provider; published 2.12.0 does not contain this extension. Providers must run additive migration 2026-09-09-001 for two bounded staging tables before advertising transfer version 1; syncTransfers: false supports a mixed-version rollout. For rollback, stop transfer traffic and use the new migration source to reverse only this staging migration and its ledger entry before restarting older code; preserve all current wallet records. Existing ordinary-page and export file contracts are retained. IndexedDB upgrades automatically to version 6 with a non-unique transaction-ID/user index, preserving existing data and duplicate transaction IDs. Older clients requesting schema version 6 cannot reopen the upgraded database; retain a compatible client for local backups. Compact checkpoints are advertised through runtime settings; older providers retain the full-state path. No ID-map data is removed and existing wire defaults remain compatible. Existing sync peers remain compatible; additive fields are optional. Run the normal Knex migration for source indexes. Only idempotent getSyncChunk reads retry after HTTP 413. Existing actions, ordinary noSend calls, permission modules, UMP v3 tokens, and active WAB accounts require no client migration. Deploy the additive WAB registration-status migration and WAB routes before relying on interrupted-signup recovery; older servers and clients retain their prior wire behavior. To use BRC-177, migrate every active Knex store before serving requests and run the default Wallet Toolbox monitor; IndexedDB upgrades automatically to schema version 6. Upgrade signer, active storage service, and remote monitor together to 2.11.0 or later; older remote storage is rejected before prefunding. The Knex migration also adds rebuildable prepared-BEEF and proof-epoch tables with every COOK control disabled. Validate the migration on MySQL before release and the cross-process epoch fence on non-production PXC before enabling writes. Roll out writes before reads, use backfill only after database review, and disable all three flags to roll back. Delete derived prepared rows before downgrading to code that cannot advance the epoch. Semantic modules may add handleRequest; hosts installing @bsv/ecpm-permission-module register it under the ecpm scheme. Argon2id tokens keep the same parameters and derived bytes across WebAssembly and JavaScript runtimes. Host registration is available from each package root; concurrent cold calls share one preload attempt, and hosts must make readiness/preload reentrant and cache permanent failures or back off retries. Native and JavaScript results share byte-type and exact-length validation; unrelated hash-wasm errors still propagate without WebAssembly. Canonical AtomicBEEF and number-array behavior are unchanged; use @bsv/sdk 2.4.2 or later, use docs/storage.md instead of the removed JSight export, and retain THIRD_PARTY_NOTICES.md and LICENSES/. Replace non-loopback HTTP storage and Arcade SSE endpoints with HTTPS. Snapshot APIs and formats are unchanged, but applications must store each complete snapshot in an OS Keychain, hardware-backed keystore, or comparably trusted secret store because possession of it grants wallet access. Apply every ChainTracks Knex migration before serving traffic. The MySQL repair clears only the rebuildable live-header cache while widening legacy truncating identifier columns to VARCHAR(64), retains authenticated bulk data, and adds the transaction lock row; allow the tracker to repopulate live headers before declaring it healthy. To downgrade, stop all ChainTracks writes, take and verify a database and authenticated-bulk-data backup, then use the current ChaintracksKnexMigrations source to roll down only the 2026-09-17 repair migration ledger entry. Its down step intentionally leaves the repaired VARCHAR(64) and LONGBLOB schema, chaintracks_state lock row, and authenticated bulk files intact. Start older code only after validating that retained schema and data in a non-production copy; never roll down the initial migration, recreate the tables, or restore truncating VARBINARY(32) identifier columns. Existing ChainTracks wire and public API contracts are unchanged. Existing logging integrations remain source compatible, but applications that relied on implicit console output must supply the optional logging callback explicitly. Configure durable download and cache lock timeouts deliberately; neither crash-abandoned lock is reclaimed automatically, so prove no writer remains before removing only the affected lock directory. Custom ChaintracksStorageBulkFileApi implementations must add atomic replaceBulkFiles support before multi-file reconciliation or replacement; older custom adapters now fail closed for those operations. MonitorOptions.maxQueuedDeactivatedHeaders is additive and defaults to 4096; lower it for constrained hosts. Arcade SSE event, pending-count, and pending-byte limits are additive and default to 262144 bytes, 64 events, and 4194304 bytes; lower them for constrained hosts. MonitorOptions.logging and ArcSSEClientOptions.log are additive and replace prior implicit library console output when observability is required. The migration-atomicity fix is included in the existing unpublished 2.13.2 candidate and requires no new schema migration. It prevents future partial migrations; a database already left with unjournaled schema objects by an older version still needs operator-reviewed recovery from a verified backup or an exact schema/journal reconciliation. Never delete migration journal rows or wallet data blindly." }, { "name": "@bsv/wallet-toolbox-client", diff --git a/packages/wallet/wallet-toolbox/CHANGELOG.md b/packages/wallet/wallet-toolbox/CHANGELOG.md index f2851800c..d30106c4a 100644 --- a/packages/wallet/wallet-toolbox/CHANGELOG.md +++ b/packages/wallet/wallet-toolbox/CHANGELOG.md @@ -6,6 +6,12 @@ attention to changes that materially alter behavior or extend functionality. ## wallet-toolbox (unreleased) +- Restore transactional SQLite migrations in the unpublished 2.13.2 candidate. + DDL, migration journal and lock changes roll back after an interrupted attempt; + foreign-key enforcement is restored after success or failure. Existing stores + with unjournaled partial schema need operator-reviewed recovery; this change + does not delete or automatically reconcile historical wallet data. + - Implement `BHServiceClient.findChainTipHash()` by delegating to its existing `findChainTipHeader()` call against `/api/v1/chain/tip/longest`, instead of throwing `Not implemented`. `ChaintracksChainTracker.getVerificationContextToken()` diff --git a/packages/wallet/wallet-toolbox/README.md b/packages/wallet/wallet-toolbox/README.md index eeccb9ac8..9968da8cc 100644 --- a/packages/wallet/wallet-toolbox/README.md +++ b/packages/wallet/wallet-toolbox/README.md @@ -27,6 +27,20 @@ Timing compares successive candidates, not a controlled comparison against upstr `main`. Byte verification was sampled, not database-wide. See [test methods and limits](#sync-performance-and-recovery) for details. +### SQLite migration recovery + +The unpublished 2.13.2 candidate runs SQLite migration DDL and the migration +journal update transactionally. Foreign-key enforcement is disabled before the +migration transaction for table rebuilds and restored after success or failure. +Failed migrations can be retried after reopening the database without partial +schema objects from that attempt. MySQL's existing transaction configuration +is unchanged. + +This prevents future partial migrations. It does not automatically repair a +store already left with unjournaled schema objects by an older version. Preserve +the database and verified backups and reconcile the exact schema and migration +journal before recovery; do not delete journal rows or wallet data blindly. + ## Overview The Wallet Toolbox is the reference implementation of the BRC-100 wallet interface. It connects the BSV SDK's cryptographic primitives to real storage backends, network services, and signing flows so that application developers don't have to wire these layers together themselves. diff --git a/packages/wallet/wallet-toolbox/src/storage/__test/StorageKnexMigrationAtomicity.test.ts b/packages/wallet/wallet-toolbox/src/storage/__test/StorageKnexMigrationAtomicity.test.ts new file mode 100644 index 000000000..49af710f9 --- /dev/null +++ b/packages/wallet/wallet-toolbox/src/storage/__test/StorageKnexMigrationAtomicity.test.ts @@ -0,0 +1,215 @@ +import { mkdtemp, rm } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { spawn } from 'node:child_process' +import { once } from 'node:events' +import { knex as makeKnex, type Knex } from 'knex' +import { StorageKnex } from '../StorageKnex' +import { KnexMigrations } from '../schema/KnexMigrations' + +const initial = '2026-09-23-001 atomicity fixture' +const altered = '2026-09-23-002 populated alter fixture' + +function open(filename: string): StorageKnex { + return new StorageKnex({ + ...StorageKnex.defaultOptions(), + chain: 'test', + knex: makeKnex({ + client: 'better-sqlite3', + connection: { filename }, + useNullAsDefault: true, + pool: { min: 1, max: 1 } + }) + }) +} + +async function createParent(db: Knex): Promise { + await db.schema.createTable('migration_parent', table => { + table.integer('id').primary() + table.string('label').nullable() + }) +} + +async function createChildAndRows(db: Knex): Promise { + await db.schema.createTable('migration_child', table => { + table.integer('id').primary() + table.integer('parent').references('id').inTable('migration_parent') + }) + await db('migration_parent').insert({ id: 1, label: 'retained' }) + await db('migration_child').insert({ id: 2, parent: 1 }) +} + +afterEach(() => jest.restoreAllMocks()) + +describe('SQLite migration atomicity through StorageKnex', () => { + test('an abruptly killed migration leaves neither partial DDL nor a stuck migration lock', async () => { + const directory = await mkdtemp(join(tmpdir(), 'wallet-migration-crash-')) + const filename = join(directory, 'wallet.sqlite') + // Exercise the built artifact in a separate process so SIGKILL cannot run + // Knex catch/finally cleanup. The parent reopens the same durable database. + const child = spawn( + process.execPath, + [ + '-e', + ` + const { knex } = require('knex') + const { StorageKnex } = require('./out/src/storage/StorageKnex.js') + const { KnexMigrations } = require('./out/src/storage/schema/KnexMigrations.js') + KnexMigrations.prototype.getMigrations = async () => ['2026-09-23-001 atomicity fixture'] + KnexMigrations.prototype.getMigration = async () => ({ + down: async db => { await db.schema.dropTableIfExists('migration_parent') }, + up: async db => { + await db.schema.createTable('migration_parent', table => { table.integer('id').primary() }) + process.send('ddl-written') + await new Promise(() => {}) + } + }) + const storage = new StorageKnex({ ...StorageKnex.defaultOptions(), chain: 'test', + knex: knex({ client: 'better-sqlite3', connection: { filename: process.argv[1] }, + useNullAsDefault: true, pool: { min: 1, max: 1 } }) }) + storage.migrate('fixture', '1'.repeat(64)).catch(() => process.exit(1)) + `, + filename + ], + { stdio: ['ignore', 'ignore', 'pipe', 'ipc'] } + ) + let storage: StorageKnex | undefined + try { + await new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error('Child migration did not reach DDL')), 10000) + child.once('message', message => { + clearTimeout(timer) + if (message === 'ddl-written') resolve() + else reject(new Error('Unexpected migration checkpoint')) + }) + child.once('exit', () => { + clearTimeout(timer) + reject(new Error('Child exited before checkpoint')) + }) + child.once('error', error => { + clearTimeout(timer) + reject(error) + }) + }) + const exited = once(child, 'exit') + child.kill('SIGKILL') + await exited + storage = open(filename) + expect(await storage.knex.schema.hasTable('migration_parent')).toBe(false) + expect(await storage.knex('knex_migrations').select()).toEqual([]) + expect(await storage.knex('knex_migrations_lock').pluck('is_locked')).toEqual([0]) + jest.spyOn(KnexMigrations.prototype, 'getMigrations').mockResolvedValue([initial]) + jest.spyOn(KnexMigrations.prototype, 'getMigration').mockResolvedValue({ + up: createParent, + down: async db => { + await db.schema.dropTableIfExists('migration_parent') + } + }) + await storage.migrate('fixture', '1'.repeat(64)) + expect(await storage.knex('knex_migrations').pluck('name')).toEqual([initial]) + } finally { + if (child.exitCode === null && child.signalCode === null) { + const exited = once(child, 'exit') + child.kill('SIGKILL') + await exited + } + await storage?.destroy() + await rm(directory, { recursive: true, force: true }) + } + }) + + test.each(['mid-file', 'journal-write'] as const)( + 'rolls back a %s failure and migrates successfully after reopening', + async boundary => { + const directory = await mkdtemp(join(tmpdir(), 'wallet-migration-')) + const filename = join(directory, 'wallet.sqlite') + let storage = open(filename) + let interrupted = true + jest.spyOn(KnexMigrations.prototype, 'getMigrations').mockResolvedValue([initial]) + jest.spyOn(KnexMigrations.prototype, 'getMigration').mockResolvedValue({ + down: async db => { + await db.schema.dropTableIfExists('migration_child') + await db.schema.dropTableIfExists('migration_parent') + }, + up: async db => { + expect((await db.raw('PRAGMA foreign_keys'))[0].foreign_keys).toBe(0) + await createParent(db) + if (interrupted && boundary === 'mid-file') throw new Error('injected interruption') + await createChildAndRows(db) + } + }) + try { + if (boundary === 'journal-write') { + await storage.knex.migrate.list({ + migrationSource: new KnexMigrations('test', 'fixture', '1'.repeat(64), 1024) + }) + await storage.knex.raw( + "CREATE TRIGGER reject_journal BEFORE INSERT ON knex_migrations BEGIN SELECT RAISE(ABORT, 'injected journal interruption'); END" + ) + } + await expect(storage.migrate('fixture', '1'.repeat(64))).rejects.toThrow(/interruption/) + expect(await storage.knex.schema.hasTable('migration_parent')).toBe(false) + expect(await storage.knex.schema.hasTable('migration_child')).toBe(false) + expect(await storage.knex('knex_migrations').select()).toEqual([]) + expect((await storage.knex.raw('PRAGMA foreign_keys'))[0].foreign_keys).toBe(1) + await storage.destroy() + storage = open(filename) + interrupted = false + await storage.knex.raw('DROP TRIGGER IF EXISTS reject_journal') + await storage.migrate('fixture', '1'.repeat(64)) + expect(await storage.knex('migration_parent').select()).toEqual([{ id: 1, label: 'retained' }]) + expect(await storage.knex('migration_child').select()).toEqual([{ id: 2, parent: 1 }]) + expect(await storage.knex('knex_migrations').pluck('name')).toEqual([initial]) + expect(await storage.knex.raw('PRAGMA foreign_key_check')).toEqual([]) + expect((await storage.knex.raw('PRAGMA integrity_check'))[0].integrity_check).toBe('ok') + await storage.migrate('fixture', '1'.repeat(64)) + expect(await storage.knex('knex_migrations').pluck('name')).toEqual([initial]) + } finally { + await storage.destroy() + await rm(directory, { recursive: true, force: true }) + } + } + ) + + test('preserves populated referenced rows during an alter-table rebuild and restores enforcement', async () => { + const directory = await mkdtemp(join(tmpdir(), 'wallet-migration-')) + const storage = open(join(directory, 'wallet.sqlite')) + const migrations = jest.spyOn(KnexMigrations.prototype, 'getMigrations').mockResolvedValue([initial]) + jest.spyOn(KnexMigrations.prototype, 'getMigration').mockImplementation(async name => ({ + down: async db => { + if (name === initial) { + await db.schema.dropTableIfExists('migration_child') + await db.schema.dropTableIfExists('migration_parent') + } else { + await db.schema.alterTable('migration_parent', table => { + table.string('label').nullable().alter() + }) + } + }, + up: async db => { + if (name === initial) { + await createParent(db) + await createChildAndRows(db) + } else { + await db.schema.alterTable('migration_parent', table => { + table.string('label', 128).notNullable().alter() + }) + } + } + })) + try { + await storage.migrate('fixture', '1'.repeat(64)) + migrations.mockResolvedValue([initial, altered]) + await storage.migrate('fixture', '1'.repeat(64)) + expect(await storage.knex('migration_parent').select()).toEqual([{ id: 1, label: 'retained' }]) + expect(await storage.knex('migration_child').select()).toEqual([{ id: 2, parent: 1 }]) + expect(await storage.knex('knex_migrations').pluck('name')).toEqual([initial, altered]) + expect(await storage.knex.raw('PRAGMA foreign_key_check')).toEqual([]) + await expect(storage.knex('migration_child').insert({ id: 3, parent: 99 })).rejects.toThrow(/FOREIGN KEY/) + await expect(storage.knex('migration_parent').insert({ id: 4, label: null })).rejects.toThrow(/NOT NULL/) + } finally { + await storage.destroy() + await rm(directory, { recursive: true, force: true }) + } + }) +}) diff --git a/packages/wallet/wallet-toolbox/src/storage/__test/StorageKnexMigrationFailure.security.test.ts b/packages/wallet/wallet-toolbox/src/storage/__test/StorageKnexMigrationFailure.security.test.ts index 7b1594ced..913e69db0 100644 --- a/packages/wallet/wallet-toolbox/src/storage/__test/StorageKnexMigrationFailure.security.test.ts +++ b/packages/wallet/wallet-toolbox/src/storage/__test/StorageKnexMigrationFailure.security.test.ts @@ -5,6 +5,22 @@ function storageWithKnex(knex: object): StorageKnex { } describe('StorageKnex migration failure boundaries', () => { + test('retains MySQL transaction settings without SQLite pragmas', async () => { + const knex = { + client: { config: { client: 'mysql2' } }, + raw: jest.fn(), + migrate: { + latest: jest.fn().mockResolvedValue([1, ['fixture']]), + currentVersion: jest.fn().mockResolvedValue('fixture') + } + } + await expect(StorageKnex.prototype.migrate.call(storageWithKnex(knex), 'wallet', '1'.repeat(64))).resolves.toBe( + 'fixture' + ) + expect(knex.migrate.latest).toHaveBeenCalledWith(expect.objectContaining({ disableTransactions: false })) + expect(knex.raw).not.toHaveBeenCalled() + }) + test('dropAllData stops only at the explicit empty-schema state', async () => { const knex = { client: { config: { client: 'better-sqlite3' } }, @@ -51,9 +67,9 @@ describe('StorageKnex migration failure boundaries', () => { } } - await expect( - StorageKnex.prototype.migrate.call(storageWithKnex(knex), 'wallet', '1'.repeat(64)) - ).rejects.toThrow('migration failed') + await expect(StorageKnex.prototype.migrate.call(storageWithKnex(knex), 'wallet', '1'.repeat(64))).rejects.toThrow( + 'migration failed' + ) expect(knex.raw).toHaveBeenLastCalledWith('PRAGMA foreign_keys = ON;') }) })