Skip to content

feat(sdk,wallet-toolbox): recovery path for certificates with non-BRC-52 types - #1100

Open
sirdeggen wants to merge 9 commits into
mainfrom
worktree-sdk-legacy-certificate-types
Open

sirdeggen wants to merge 9 commits into
mainfrom
worktree-sdk-legacy-certificate-types

Conversation

@sirdeggen

@sirdeggen sirdeggen commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Keep this pull request in draft until local validation is complete. After
every push, wait for all applicable checks on the exact head to finish and
fix every failure before requesting review or calling the work complete.

Program and scope

  • Tracker or issue: fix: sdk 2.8.0 32-byte certificate type check breaks legacy certificates on every certificate call, undocumented #1085
  • Program gate(s) advanced: none; migration path for certificates issued before @bsv/sdk 2.8.0 under non-BRC-52 types
  • Why this change is needed: the 2.8.0 security merge (b3155fa2e) made every wallet certificate validator, the list/discovery result validator, BRC-103 requested sets and Certificate.toBinary() require the BRC-52 32-byte type without recording a breaking change. Nothing bounded the type before 2.8.0 and @bsv/simple before 0.6 derived it from a schema identifier, so wallets hold certificates issued under non-compliant types. Today one such stored record makes an unfiltered listCertificates fail as a whole for apps that never used those types, and there is no way to find or retire the record. Reviewers (ty-everett, BraydenLangley, kjartan221) agreed on direction: keep the normal APIs strict, omit broken records from listings, and provide an explicit recovery path so issuers reissue valid types and holders swap the old record out.
  • Explicitly out of scope: any transport- or API-level acceptance of non-32-byte types (the earlier commits on this branch did that and are superseded by this head; the diff against main is what counts); changing the BRC-52 binary layout or the binary wallet wire; rewriting stored records; regenerating the stale ts2md reference under packages/sdk/docs/reference.
  • Exact head SHA reviewed: 7cc959e76 (b8de96fc9 rework, 9143944f6 ledger follow-up, 4d2c0fd94 Sonar refactors, merges of main at 9efb82756 and da58fb725, 7cc959e76 coverage tests; earlier commits on the branch are superseded)

What changes

  • @bsv/sdk 4.0.0 candidate (main already carries the 4.0.0 major candidate from feat: @bsv/sdk 4.0 module balances and BRC-207 color permission module #1243; this change is additive within it and is recorded under its own "Legacy certificate recovery (4.0.0 candidate)" changelog section)
    • validationHelpers.ts: an exact-length base64 failure reads exactly 32 bytes instead of at least 32 bytes. No validator accepts a non-32-byte type.
    • WalletResultValidation.ts: listCertificates, discoverByIdentityKey and discoverByAttributes results leave out a certificate whose type is canonical base64 that does not decode to 32 bytes (up to 128, matching @bsv/simple's legacy tolerance) and that validates in every other respect; totalCertificates is reduced by the omitted count, never below the entries kept. Any other malformed certificate still rejects the whole result; acquisition and proof results are unchanged. WalletWireProcessor serializes the same validated result, so JSON and binary substrates behave identically (no transport asymmetry).
    • Certificate.ts: toBinary() and verification share one #serialize; verify(), sign(), toBinary() and fromBinary() are unchanged. New recovery-only verifyHistoricalSignature() rebuilds the pre-2.8.0 serialization (decoded type bytes written unpadded) for a 1–128-byte canonical-base64 type and verifies the certifier signature, whose key is still derived from the exact type and serialNumber; a certificate passing it is not thereby compliant.
  • @bsv/wallet-toolbox 2.15.0 (rides the existing minor candidate; client/mobile already 2.15.0)
    • FindCertificatesArgs.legacyTypes?: 'exclude' | 'only': Knex length(type) = 44 / <> 44, the same test on IndexedDB; countCertificates inherits it. listCertificates passes 'exclude', so non-compliant records are omitted at the storage query.
    • Wallet.listLegacyCertificates({ limit?, offset? }) returns the non-compliant records with fields and master keyring in the listCertificates shape (filtered again locally for a storage server that predates legacyTypes); Wallet.relinquishLegacyCertificate({ type, serialNumber, certifier }) retires one. Both are outside BRC-100 and only reachable on the Wallet instance (WalletPermissionsManager does not forward them).
    • Storage-level relinquishCertificate validates the reference with a legacy-tolerant type (1–128 bytes) so the recovery path works over existing remoting (findCertificatesAuth, relinquishCertificate); the BRC-100 Wallet.relinquishCertificate and WalletStorageManager.relinquishCertificate keep the strict SDK validator. A remote storage server must be upgraded together with the wallet.
  • Docs: SDK changelog (own section under the 4.0.0 candidate) with the retroactive 2.8.0 note and the hash-or-random type-ID guidance (BRC-184 metadata separate); toolbox 2.15.0 changelog; release-notes entries (sdk patch → minor, toolbox and simple rows); docs/packages/sdk/bsv-sdk.md "Legacy certificate types since 4.0.0"; docs/packages/wallet/wallet-toolbox.md "Legacy certificate recovery"; corrected @bsv/simple guide/api-reference passages; regenerated migration ledger and stack facts; repository-health baseline.

Impact

  • No public package source or manifest changed
  • Public package source or manifest changed; affected packages are listed below
  • Infrastructure source, dependency, image, or deployment configuration changed
  • Public API, exports, types, runtime targets, or browser/mobile behavior changed (new Certificate.verifyHistoricalSignature(); new Wallet.listLegacyCertificates() / relinquishLegacyCertificate(); FindCertificatesArgs.legacyTypes; list/discovery results omit non-compliant records; exact-length error text)
  • Security-sensitive boundary changed (wallet result validation, certificate signature verification, storage relinquish validation)
  • Documentation or examples changed

Affected packages/services and intended patch versions (publication occurs only
through the release workflow after approval):

  • @bsv/sdk 3.2.1 → 4.0.0 (main's existing major candidate; this PR adds Certificate.verifyHistoricalSignature() and the listing change to it, no further bump)
  • @bsv/wallet-toolbox 2.14.9 → 2.15.0 (existing minor candidate; @bsv/wallet-toolbox-client and -mobile already at 2.15.0)

Verification

  • Local commands and results (worktree merged with origin/main (latest merge commit 9efb82756), Node 24.15, pnpm frozen install):
    • New tests, watched fail first: packages/sdk/src/wallet/__tests/NonCompliantCertificateType.test.ts (exact-length messages; listing omission with total adjustment for list, discovery and the binary wire; garbage still rejected; acquisition/proof unchanged), packages/sdk/src/auth/certificates/__tests/Certificate.historicalSignature.test.ts (1/16/39/128-byte historically signed types verify only through verifyHistoricalSignature(); tampered, zero-padded, 129-byte, empty, non-canonical and non-base64 types rejected even when their own preimage was signed; sign/toBinary/fromBinary stay canonical), packages/wallet/wallet-toolbox/src/__tests__/Wallet.legacyCertificateRecovery.test.ts (sqlite: legacy + canonical rows → listCertificates returns the canonical one with total 1; listLegacyCertificates returns the legacy one with fields and keyring; BRC-100 relinquishCertificate rejects it; relinquishLegacyCertificate retires it; serial/certifier still validated).
    • pnpm --filter @bsv/sdk exec jest on the result-validation, wire, certificate and auth suites plus the new files — 697/697 (17 suites). Full sdk suite: 217 suites, 7677/7677 passed.
    • pnpm --filter @bsv/wallet-toolbox exec jest on the recovery, acquireCertificate and storage-authorization suites — 26/26. Governed pnpm --filter @bsv/wallet-toolbox test: 301 suites passed, 2 governed skips; 3321/3329 passed, 7 governed skips, 1 failure (StorageClient.test.ts, fetch failed against its local server on port 8042 — a network flake unrelated to this change; the file passes alone, 11/11).
    • pnpm --filter @bsv/simple exec jest: 26 suites, 371/371 passed.
    • tsc -b (sdk), pnpm --filter @bsv/wallet-toolbox run typecheck, oxlint for both packages, prettier — clean. Root pnpm build, pnpm typecheck, pnpm lint, pnpm format:check, pnpm health:check: all pass on this head (pnpm build, pnpm typecheck, pnpm lint, pnpm format:check; pnpm health:check with 0 control errors, run alone after one load-induced flake of its local-registry peer-resolution test); pnpm --filter @bsv/sdk run pack:check and pnpm --filter @bsv/wallet-toolbox run pack:check pass.
    • pnpm docs:facts regenerated the migration ledger and stack facts; pnpm docs:facts:check — pass.
    • Coverage follow-up (7cc959e76, tests only, after codecov/patch reported 89.6% on da58fb725): legacyCertificates.ts 100% (incl. the off-curve certifier rejection), StorageIdb legacyTypes exclude/only on find and count under fake-indexeddb, Wallet.listLegacyCertificates paging and the older-storage-server path, verifyHistoricalSignature() on an unsigned certificate. sdk certificate suites 70/70; toolbox recovery/helper/Idb suites green; lint and prettier clean.
  • Hosted CI run: the run on 9efb82756 failed in the shared prepare step at pnpm audit:security on two HIGH advisories for transitive follow-redirects 1.16.0 (GHSA-q3jh-wrc6-jgq8, GHSA-8r9p-f939-6h3c, via btms-backend → mongodb-memory-server), unrelated to this change and remediated on main by fix(deps): lock follow-redirects 1.16.1 for the 2026-10-20 high advisories #1270; the three new Sonar findings on that head (S3776, S7503, S7766) are fixed in 4d2c0fd94. main will be merged again once fix(deps): lock follow-redirects 1.16.1 for the 2026-10-20 high advisories #1270 lands so the full matrix runs on the final head.
  • Conformance evidence: not applicable; canonical certificate serialization bytes and conformance vectors are unchanged.
  • Coverage delta: new branches covered by the tests above; hosted coverage job pending.
  • Lint/typecheck delta: zero warnings, zero errors.
  • Browser/mobile/packed-consumer evidence: pnpm --filter @bsv/sdk run pack:check and scripts/check-browser-package.mjs passed on the earlier head; packages/sdk/scripts/check-umd-contract.mjs fails locally only because npm 12 prints npm pack --json as an object (environmental; the hosted test:browser job is authoritative).
  • Performance or bundle-size delta: one small validation helper and one method; negligible.
  • I self-reviewed the complete diff for correctness, security,
    compatibility, public API, artifacts, dependencies, docs, and operations
  • All applicable checks are terminal and successful on the exact head; any
    scope-based skip is expected and validated by the merge gate

Security and dependencies

  • No dependency or lockfile change
  • Changelog, runtime relevance, peer compatibility, transitive graph, and
    audit results were reviewed (not applicable: no dependency change)
  • CodeQL/negative tests cover any changed trust boundary (malformed certificates still reject list results; strict argument validation unchanged; legacy relinquish validates serial number and certifier; historical verification rejects every non-canonical or out-of-range type)
  • The exact-head CodeQL analysis has no new alert (pending)
  • The exact-head repository quality gate reports zero new Sonar findings
    (including accepted or false-positive issue states) and zero unreviewed hotspots;
    Sonar's aggregate Quality Gate passed verdict alone is not merge evidence (pending)
  • No new override, advisory dismissal, quality suppression, or skipped test
  • Any temporary exception is registered with owner, evidence, review date,
    and removal condition (none added)
  • Workflow permissions and lifecycle-script behavior remain least privilege (unchanged)

Dependency evidence

Not applicable: no dependency change.

  • Release notes and necessity: n/a
  • Runtime, build, and peer compatibility: n/a
  • Deduplicated lockfile: unchanged
  • Audit and CodeQL: see above
  • Package and consumer tests: see above
  • Bundle and performance impact: see above
  • Affected public package versions: @bsv/sdk 4.0.0 (existing candidate), @bsv/wallet-toolbox 2.15.0

Release and operations

  • No npm publication was performed from a workstation or from this PR
  • Required npm patch bumps are included or intentionally deferred by the
    controlling program
  • Image/SBOM/provenance/deployment/rollback impact is documented (libraries only; a remote wallet-storage server should be upgraded with the wallet for the legacy relinquish path; roll back by publishing the previous versions)
  • Documentation, changelog, migration, and operational guidance are current

Completion evidence

  • The linked tracker is updated only for work fully proved by merged code,
    passing checks, resolved alerts, measurements, or an approved exception
  • Review conversations are resolved
  • Documentation, changelog, migration notes, release notes, and operator
    guidance are current or concretely not applicable
  • No pending, failed, stale, cancelled, or unexpectedly skipped check is
    being handed to another contributor as “complete”
  • One qualified maintainer approval is sufficient; no last-pusher
    restriction is assumed

🤖 Generated with Claude Code

…and verify (3.2.2)

Certificates issued before 2.8.0 may carry a `type` shorter than 32 bytes.
The 2.8.0 security merge made every wallet certificate validator, the
listCertificates result validator and Certificate.toBinary() require
exactly 32 bytes without recording a breaking change, so one legacy
certificate failed an unfiltered listCertificates as a whole and could not
be listed, proven, verified or relinquished (#1085).

- validateListCertificatesArgs types, validateRelinquishCertificateArgs
  type and validateProveCertificateArgs certificate.type accept 1-32
  canonical-base64 bytes; acquireCertificate (issuance and direct) keeps
  exactly 32 and the length error now says "exactly 32 bytes".
- Wallet result validation accepts a 1-32-byte type on list, discovery,
  prove and acquisition results; serial numbers stay 32 bytes.
- Certificate.verify() rebuilds the historical unpadded serialization
  through a shared serializer; toBinary(), fromBinary() and sign() stay
  canonical. The certifier signing key is derived from the exact type and
  serial number, so a shorter type cannot be confused with a canonical one.
- WalletWireTransceiver rejects a legacy type before writing a request and
  WalletWireProcessor rejects one in list, discovery and acquisition
  results with an explicit error instead of misaligning the frame.
- Add SDK regression tests, a wallet-toolbox sqlite test for a stored
  legacy certificate, changelog, release-note, baseline and package
  documentation updates; bump @bsv/sdk to 3.2.2.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…wire guard

- verify() returns false for a non-canonical, empty or oversized type, covering assertHistoricalCertificateType's rejection paths.
- The binary wire rejects a proveCertificate request whose certificate has no type before invoking the wallet.
- WalletWireProcessor's certificate type guard takes a string: result validation already rejects a non-string type, so the extra branch was unreachable.
- validateCertificate checks each encoded field through a helper with a descriptive error instead of an index loop and a bare Error (Sonar S4138, S7722).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@kjartan221

Copy link
Copy Markdown
Collaborator

Wallet-side fix and the keyID trust argument check out: short types verify under the historical preimage, non-canonical/padded spellings fail, sign/toBinary/acquire and the binary wire stay at 32 bytes. Through WalletClient, list (filtered and unfiltered with a legacy cert held), prove and relinquish now accept a 16-byte type.

Blocker

  • packages/sdk/src/auth/AuthMessageValidation.ts:145: assertRequestedCertificateSet still requires exactly 32-byte types. It came in with the same 2.8.0 commit as fix: sdk 2.8.0 32-byte certificate type check breaks legacy certificates on every certificate call, undocumented #1085, and it runs from the Peer constructor/requestCertificates (Peer.ts:158/332/434) and on every inbound message carrying requestedCertificates (:346, :367). The CHANGELOG says pre-2.8 certs are "usable again", but BRC-103 mutual auth, their main use, is still blocked. Handshake spike, in-memory transport, @bsv/sdk 2.7.1 from npm vs this PR's build (da22e30c6):

    Initiator → responder Requested type Messages sent Result
    2.7.1 → 2.7.1 16 bytes initialRequest, initialResponse handshake completes
    2.7.1 → PR 16 bytes initialRequest responder rejects: certificate type must encode exactly 32 bytes.
    2.7.1 → PR 32 bytes initialRequest, initialResponse handshake completes
    PR → PR 32 bytes initialRequest, initialResponse handshake completes
    PR → PR 16 bytes none new Peer(...) throws the same error

    With only line 145 disabled in the built output, all three 16-byte rows complete the handshake, so that check is the only cause. An app or wallet still on 2.7.x that requests a legacy type can't authenticate against anything on this version. Relax it to the same rule as the wallet paths with a Peer-level test, or narrow the CHANGELOG and say BRC-103 still rejects legacy types.

Legacy upper bound

  • packages/sdk/src/wallet/validationHelpers.ts:447: pre-2.8 had no type length limit, and simple pre-0.6 set the type to base64(utf8(schemaId)), so schema ids over 32 chars produced >32-byte types. Simple still allows legacy types up to 128 bytes (helpers/simple/src/core/certificate-validation.ts:18). Certificates signed with real 2.7.1 Certificate.sign() and checked against this PR:

    Type length 2.7.1 verify() PR verify() PR listCertificates args
    13 bytes (certification) true true accepted
    31 bytes true true accepted
    39 bytes (schema URL) true false rejected: must be no more than 32 bytes

    Those certs still break an unfiltered list. Either accept them on the legacy-tolerant paths (list/prove/relinquish/verify), or document the cap and keep the offline-export route in the simple migration row (docs/reference/package-api-migrations.md:51), which this PR removes. The "several deployed certifiers" claim also needs a source; the only one in-repo is simple pre-0.6.

Tests

  • packages/sdk/src/auth/certificates/__tests/Certificate.legacyType.test.ts:102: the bad-type verify() cases fail on signature mismatch, not validation. Letting verify() accept a 33-byte type, lenient base64 or an empty type still leaves the suite green. Sign each case over its own preimage/keyID so the check is actually exercised.
  • packages/sdk/src/wallet/substrates/WalletWireProcessor.ts:1966, :2513: disabling either the acquire-result or the discovery-result guard leaves all 785 related tests passing. The old toBinary() already threw at those sites, so "misaligned frame" in the JSDoc/CHANGELOG overstates the processor side.
  • The 1-byte lower bound is never tested; every legacy fixture is 16 bytes.

Docs

  • docs/packages/sdk/bsv-sdk.md:570: "use a JSON substrate" should name new WalletClient('json-api'); 'auto' tries the binary wire before JSON when window.CWI is absent (WalletClient.ts:155-164).
  • packages/helpers/simple/docs/guides/certification.md:47 and api-reference/certification.md:71 still say the SDK rejects short types on list/prove/relinquish.

@BraydenLangley

Copy link
Copy Markdown
Collaborator

I recommend closing this PR. [BRC-52 specifies a 32-byte certificate type](https://github.com/bsv-blockchain/BRCs/blob/master/peer-to-peer/0052.md), and I think we should enforce that consistently across the normal certificate APIs and transports.

I confirmed the legacy issue with PeerCert. PeerCert was issuing non-compliant types, so we updated it to conform. Other affected apps should do the same. Older SDKs accepting those values doesn’t make the certificates compliant with BRC-52.

This PR keeps new issuance strict, but restores support for short types through JSON while they remain unsupported by binary. I understand that this is intentional compatibility behavior, but that leaves the same certificate usable through one transport and unusable through another which is not a good approach. I’d rather handle legacy recovery explicitly than create this ambiguity or force a breaking change on the binary substrate.

Human-readable names can stay separate from the type ID. For example:

import { Hash, Utils } from '@bsv/sdk'

const schema = 'example.org/skill-endorsement/v1'
const type = Utils.toBase64(Hash.sha256(schema, 'utf8'))

const registry = {
  [type]: { name: 'Skill endorsement', schema }
}

That produces exactly 32 bytes before base64 encoding. Apps can maintain the label/schema mapping locally or use something like CertMap. Registration isn’t required to list, filter, or use the certificate.

Existing signed certificates can’t be fixed by padding or hashing their stored type as that changes what was signed.

We should document the historical validation change and provide an explicit recovery/export path if there are wallets holding non-compliant BRC-52 certificates.

@kjartan221

kjartan221 commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Agree with Brayden on direction. The goal for #1085 was a way to migrate legacy certificates, not ongoing support for them across APIs and transports, and my previous comment didn't make that clear. Dropping the Peer/BRC-103 point.

Whatever the migration ends up looking like, one thing should ship with it: listCertificates shouldn't fail as a whole on one non-compliant stored record. Leave it out of the results or report it, with no legacy support implied. Today one bad row breaks every listing for that wallet that doesn't filter by type (types: []), including for apps that never touch legacy types.

@ty-everett

Copy link
Copy Markdown
Collaborator

I agree that this should probably be closed, we just need to have a migration path so that issuers will issue valid types in the future and allow people who hold an invalid one to swap it out, then omit broken ones from the list.

The 32 byte type ID should generally be a hash or randomly generated value, any metadata should be defined separately from the type as in BRC184.

…-52 types

Supersedes the transition-window approach on this branch, per review on
#1100 (#1085): the normal certificate APIs and transports stay strict at
the BRC-52 32-byte type; the change is how a wallet handles records it
already holds from before @bsv/sdk 2.8.0.

@bsv/sdk 3.3.0:
- listCertificates and discovery result validation leave out a certificate
  whose type is canonical base64 of 1-128 bytes other than 32 and that is
  valid in every other respect, lowering totalCertificates, instead of
  rejecting the whole result; the binary wallet wire serializes the same
  validated result. Any other malformed certificate still rejects.
- New recovery-only Certificate.verifyHistoricalSignature() rebuilds the
  pre-2.8.0 serialization for such a type and verifies the certifier
  signature; verify(), sign(), toBinary() and fromBinary() are unchanged.
- An exact-length validation failure reads "exactly 32 bytes".

@bsv/wallet-toolbox 2.15.0:
- FindCertificatesArgs.legacyTypes ('exclude' | 'only') on Knex and
  IndexedDB; listCertificates excludes non-compliant records at the query.
- Wallet.listLegacyCertificates() and Wallet.relinquishLegacyCertificate()
  (outside BRC-100, Wallet instance only); storage-level relinquish
  validates a legacy-tolerant reference while the BRC-100 method and the
  storage manager keep the strict validator.

Docs: sdk/toolbox changelogs, release notes (sdk minor), package pages,
@bsv/simple guide and api-reference, regenerated ledger and stack facts,
repository-health baseline. Tests cover the listing omission (JSON and
wire), the historical verification bounds, and the sqlite recovery flow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@sirdeggen sirdeggen changed the title fix(sdk): accept legacy certificate types on list, prove, relinquish and verify (3.2.2) feat(sdk,wallet-toolbox): recovery path for certificates with non-BRC-52 types Oct 20, 2026
…he release-notes ledger

The toolbox 2.15.0 row now describes FindCertificatesArgs.legacyTypes, listLegacyCertificates/relinquishLegacyCertificate, the storage-level tolerant relinquish and the remote storage upgrade note, matching its changelog and package page; the migration ledger is regenerated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@sirdeggen

Copy link
Copy Markdown
Contributor Author

@ty-everett @BraydenLangley @kjartan221 — reworked per your direction. The diff against main is now recovery-only; the earlier transition commits on this branch are superseded by b8de96fc9 / 9143944f6 (no force-push, so history stays reviewable).

  • Normal APIs stay strict. Every wallet certificate argument, BRC-103 requestedCertificates, Certificate.verify(), sign(), toBinary()/fromBinary() and both substrates still require the BRC-52 32-byte type. The validation error now says exactly 32 bytes.
  • Listings never fail on one bad row. listCertificates and discovery result validation leave out an otherwise-valid certificate whose type is canonical base64 that does not decode to 32 bytes and lower totalCertificates; anything else malformed still rejects the whole result, and acquisition/proof results are unchanged. WalletWireProcessor serializes the same validated result, so JSON and binary substrates behave identically — no transport asymmetry. wallet-toolbox listCertificates also omits such rows at the storage query (FindCertificatesArgs.legacyTypes: 'exclude').
  • Explicit recovery path. Wallet.listLegacyCertificates() and Wallet.relinquishLegacyCertificate() in wallet-toolbox 2.15.0 (outside BRC-100, on the Wallet instance only; a remote wallet-storage server must be upgraded with the wallet), plus the recovery-only Certificate.verifyHistoricalSignature() in sdk 3.3.0 to authenticate a stored legacy record before the issuer reissues it under a hash or random 32-byte type with any name/schema kept in separate metadata (BRC-184), as Ty and Brayden described.
  • Docs. Retroactive note on the 2.8.0 change in the sdk changelog and release notes, the migration recipe on the sdk and wallet-toolbox package pages, and corrected @bsv/simple guide/api-reference passages.

Kjartan — under this design the Peer/BRC-103 and >32-byte points fall away (requested sets stay strict; the recovery bound is 1–128 bytes so simple's pre-0.6 schema-id types are listable), and "one bad row must not fail the listing" is the core of it. The 1-byte lower bound and the discriminating verify() cases (each bad type signed over its own preimage/key ID) live in Certificate.historicalSignature.test.ts; the sqlite recovery flow is in Wallet.legacyCertificateRecovery.test.ts.

Local evidence is in the PR body; hosted checks on the new head are pending.

sirdeggen and others added 4 commits October 20, 2026 16:15
…ertificate-types

# Conflicts:
#	docs/reference/package-api-migrations.md
- validateCertificatesResult: extract requested-set builders, per-entry binding and discovery validation into helpers (cognitive complexity S3776) and use an intrinsic Math.max for the adjusted total (S7766).
- Certificate.verifyHistoricalSignature(): return a resolved Promise from a synchronous check instead of an async method with no await (S7503); behaviour unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…o worktree-sdk-legacy-certificate-types

# Conflicts:
#	docs/packages/sdk/bsv-sdk.md
#	docs/reference/package-api-migrations.md
#	docs/reference/stack-facts.md
#	governance/package-release-notes.json
#	governance/repository-health/baselines.json
#	packages/sdk/CHANGELOG.md
#	packages/sdk/package.json
- legacyCertificates helpers: canonical/legacy/non-canonical/non-string types; every validateLegacyCertificateReference rejection including an off-curve certifier.
- StorageIdb: legacyTypes 'exclude' / 'only' on findCertificates and countCertificates.
- Wallet.listLegacyCertificates: paging, and rows an older storage server returns without the filter or fields.
- Certificate.verifyHistoricalSignature: unsigned certificate is unverified.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants