# @interop/verifier-core CHANGELOG

## 3.7.0 - 2026-09-30

### Changed

- Status list credential proofs are verified through the `cryptoServices`
  configured on `createVerifier`, so injected custom crypto services apply to
  `BitstringStatusListCredential`s as well as to VC and VP proofs.
- **BREAKING**: The `cryptoSuites` and `verifyBitstringStatusListCredential`
  fields are removed from the exported `VerificationContext` type. Custom suites
  that build a context by hand must drop them.
- Only `BitstringStatusListEntry` entries in `credentialStatus` have their
  `statusListCredential` fetched and proof-verified; entries of other status
  types are ignored wherever they appear in the array. The check is skipped only
  when no Bitstring entry is present, and the skip reason names every unchecked
  type. Distinct lists are loaded and verified concurrently, and a list shared
  by several entries is loaded and verified once.
- Status list proof verification is injected into `checkStatus` through the
  `verifyStatusListCredential` hook of `@interop/vc-bitstring-status-list`
  `^3.1.0`, which now owns list loading again.
- **BREAKING**: `CheckResult.id` is required. `runSuites` populates it, so the
  verifier no longer sets it afterward. Code that builds a `CheckResult` by hand
  must supply an `id`. The deprecated `check` and `suite` fields remain.
- `SuiteSummary.verified` is true unless a check marked `fatal` failed in that
  suite, matching the top-level `verified`. A suite can have `status: 'failure'`
  with `verified: true` when the failing check is non-fatal.
- `SuiteSummary.message` always reads `<passed> of <total> checks passed`, with
  non-zero counts in parentheses: `(<k> failed)`, `(<k> skipped)`,
  `(<r> not run after fatal)`.
- `proof.signature` fails with `No Proof` when the subject has no `proof`,
  instead of `No Applicable Crypto Service`. A presentation verified with
  `unsignedPresentation: true` is exempt.
- `recognition.profile` skips with `no recognizers configured` when
  `recognizers` is empty, and `no recognizer matched` when none applies.

### Added

- `registry.issuer` success outcomes carry the `RegistryLookupResult` (`found`,
  `matchingRegistries`, `uncheckedRegistries`) on `outcome.payload`.

### Fixed

- The `verified` doc comments and docs state that only `fatal` failures make
  `verified` false.
- Presentation `holder` accepts an object with an `id` as well as a URL string,
  per VCDM 2.0.

## 3.6.0 - 2026-09-28

### Added

- The default document loader resolves `did:webvh` DIDs through
  `@interop/did-method-webvh`, fetching the DID log and witness file via the
  injected `HttpGetService`. A `did:webvh` log that cannot be fetched (an HTTP
  error status or a network failure) is reported as `DID_WEB_UNRESOLVED`, as is
  a `DIDResolutionError` coded `notFound` or `internalError` from any DID
  driver.

### Fixed

- `BuiltinHttpGetService` reads the response body once, so a non-JSON body under
  a JSON-like content type (e.g. a `did.jsonl` log served as
  `application/jsonl`) comes back as text instead of throwing.

## 3.5.10 - 2026-09-27

### Changed

- Update to latest vc and status list deps.

## 3.5.9 - 2026-09-25

### Fixed

- The http(s) document loader protocol handler now parses string bodies, so a
  custom `HttpGetService` that returns raw text (e.g. a `text/plain` status
  list) no longer passes a string through to `checkStatus`. Unparseable bodies
  surface as a url-bearing `NotFoundError`.

## 3.5.8 - 2026-09-10

### Changed

- Update to latest ed25519 key dep (`didKeySigner()`).

## 3.5.7 - 2026-09-10

### Fixed

- `verifyCredential` / `verifyPresentation` now run the suites against the
  caller's original object instead of the Zod parse output, and return that
  object as `verifiableCredential` / `verifiablePresentation`. Zod rewrites
  nested objects (dropping unknown keys such as `issuer.image.caption`), which
  changed the canonicalized N-Quads and made valid proofs fail as
  `INVALID_SIGNATURE`.
- `issuer.image` uses the Open Badges Image schema: unknown keys pass through
  and `type` accepts `'Image'` or `['Image']`. String-form images normalize to
  `{ id, type: 'Image' }` (previously `['Image']`).
- OB 3.0 recognizers and `getVcVersion` accept a string-valued `@context`.

## 3.5.6 - 2026-09-05

### Changed

- Update to latest key deps.

## 3.5.5 - 2026-08-18

### Changed

- Update to latest `@interop/data-integrity-core@8.7.1`.

## 3.5.4 - 2026-08-10

### Changed

- Update to `security-document-loader@10` (byoe-context unbundled).

## 3.5.3 - 2026-08-05

### Fixed

- Presentation verification now derives the proof purpose by scanning every
  proof in the proof set instead of reading `proof[0]` alone. A presentation
  carrying an `assertionMethod` proof ahead of its `authentication` proof was
  verified under `AssertionProofPurpose`, which skips non-matching proofs and
  aggregates with "any verified" -- so the authentication proof binding the
  response to the request's challenge and domain was never signature-checked. If
  any proof declares `authentication` (or `authenticationMethod`),
  `AuthenticationProofPurpose` is used.

## 3.5.2 - 2026-08-01

### Changed

- Update to latest `@interop/security-document-loader@9.5.1` dep.

## 3.5.1 - 2026-08-01

### Changed

- Update to latest `@interop/security-document-loader@9.5.0` dep.

## 3.5.0 - 2026-07-30

### Added

- `'skipped'` check outcomes can now carry an optional machine-readable `code`
  alongside the prose `reason`, so consumers can branch on the skip cause
  without parsing English text. The expiration check (`validity.expiration`) is
  the first emitter: its three skip causes are tagged `no-credential`,
  `no-expiration`, and `unparseable-date` (catalog exported as
  `EXPIRATION_SKIP_CODES`). The distinction matters because a credential whose
  expiry exists but does not parse is an issuer defect a consumer may want to
  surface as a warning, not a pass.

## 3.4.1 - 2026-07-22

### Fixed

- The issuer registry check now treats an explicitly empty `registries` list
  (`registries: []`) as opting out of registry lookup and skips, instead of
  failing with "issuer not found in any registry" -- e.g. for self-issued
  credentials.
- `INVALID_SIGNATURE` problem details now surface the underlying sub-error
  messages from an aggregate jsonld-signatures error (deduplicated and joined),
  instead of the unhelpful top-level "Verification error(s)." message.

## 3.4.0 - 2026-07-22

### Added

- Two opt-in verification suites (pass them via a verify call's
  `additionalSuites`; neither is part of `defaultSuites`):
  - `expirationSuite` (suite id `validity`, check id `validity.expiration`) -- a
    non-fatal check that a credential is within its validity period, reading the
    expiry from VC 2.0 `validUntil` and falling back to VC 1.x `expirationDate`.
    "Now" comes from the injected time service
    (`context.timeService.dateNowMs()`) when present, else `Date.now()`. Emits
    the new exported `EXPIRED_PROBLEM_TYPE` on failure.
  - `createIssuerDetailsSuite({ lookupDid })` (suite id `trust`, check id
    `trust.issuer-details`) -- a factory for a non-fatal check that surfaces
    rich issuer registry metadata on the outcome `payload` for a UI to consume.
    The registry lookup is injected as `lookupDid`, so the library takes on no
    concrete registry-client dependency. The individual
    `createIssuerDetailsCheck` is also exported for a la carte suite
    composition.

## 3.3.1 - 2026-06-28

### Changed

- Update to `@interop/data-integrity-core@8.1.0` and related latest deps.

## 3.3.0 - 2026-06-25

### Added

- New `VERIFICATION_METHOD_UNRESOLVED` problem type. When a proof's verification
  method uses a DID method the document loader has no driver for (did-io throws
  `Driver for DID ... not found.`), `classifySignatureError` now surfaces a
  distinct, actionable problem -- "the verifier cannot resolve this DID method;
  register a resolver/driver in the document loader" -- instead of a misleading
  `INVALID_SIGNATURE`. The signature was never actually checked, so reporting it
  as invalid was wrong.

## 3.2.1 - 2026-06-13

### Changed

- Update to `@digitalcredentials/data-integrity-core@8.0.0` and related latest
  deps.

## 3.2.0 - 2026-06-09

### Added

- Verify ECDSA (P-256 / P-384) `ecdsa-rdfc-2019` Data Integrity credentials out
  of the box. Two changes to the default services:
  - `defaultCryptoSuites()` now includes `ecdsa-rdfc-2019` (via
    `@interop/ecdsa-signature`), alongside `Ed25519Signature2020` and
    `eddsa-rdfc-2022`.
  - The default document loader (`documentLoaderFromHttpGet`) registers the
    standard `did:key` suites -- Ed25519 plus ECDSA P-256/P-384/P-521 -- via
    `@interop/security-document-loader`'s new `registerDefaultDidKeyHeaders`
    helper, so ECDSA `did:key` verification methods resolve (previously only the
    Ed25519 `z6Mk` header was registered). Requires
    `@interop/security-document-loader@^9.3.0`.

## 3.1.0 - 2026-06-06

### Added

- `ProblemTypes.ISSUER_PROOF_MISMATCH`: a distinct problem type for credentials
  whose `issuer` does not match the controller of the proof's verification
  method. The Data Integrity crypto service now classifies this case explicitly
  (title `Issuer / Proof Mismatch`, with the offending issuer in the detail)
  instead of collapsing it into a generic `INVALID_SIGNATURE` -- the signature
  itself is cryptographically valid; it is the proof-purpose validation that
  fails, so reporting it as a bad signature was misleading.

## 3.0.0-3.0.1 - 2026-06-04

### Changed

- **BREAKING**: Fork from https://github.com/skybridgeskills/dcc-verifier-core
  (v2 below)
- **Tooling / packaging** (infrastructure aligned with
  `isomorphic-lib-template`, no library behavior change): build is a single-pass
  `tsc` under `moduleResolution: Bundler`; tests run on **vitest** (Node) +
  **playwright** (browser, replacing karma); lint/format on eslint flat config +
  prettier 3; package manager is **pnpm**. `engines.node` raised to `>=24`.
  `exports` now declare `react-native` / `import` / `default` conditions for `.`
  and `./openbadges`, and the package is marked `sideEffects: false`.
- **Dependencies**: switched `@digitalcredentials/security-document-loader`
  (`^8.0.0`) to the `@interop/security-document-loader` fork (`^9.2.1`). The
  `securityLoader` export and document-loader behavior are unchanged; the fork
  ships its own type declarations, so the module shim in `declarations.d.ts` was
  dropped.
- **Dependencies**: switched the DCC crypto/DID packages to their TypeScript
  `@interop/*` forks (which ship their own type declarations, so the
  corresponding `declare module` shims were dropped):
  - `@digitalcredentials/ed25519-multikey` (`^1.4.0`) and
    `@digitalcredentials/ed25519-verification-key-2020` (`^4.0.0`, dev) to
    `@interop/ed25519-verification-key` (`^7.0.1`); `Ed25519Multikey.from`
    becomes `Ed25519VerificationKey.from`.
  - `@digitalcredentials/data-integrity` (`^2.6.0`) to
    `@interop/data-integrity-proof` (`^3.2.1`) for the `DataIntegrityProof`
    suite.
  - `@digitalcredentials/did-method-web` (`^1.1.0`) to
    `@interop/did-web-resolver` (`^6.1.0`); `DidWebDriver` becomes
    `DidWebResolver` and `didUrlToHttpsUrl` is replaced by `urlFromDid`.
  - `@digitalcredentials/ed25519-signature-2020` (`^7.0.0`) +
    `@digitalcredentials/eddsa-rdfc-2022-cryptosuite` (`^1.3.0`) to
    `@interop/ed25519-signature` (`^7.0.1`), which provides both
    `Ed25519Signature2020` and the `eddsaRdfc2022` cryptosuite.
- **Dependencies**: switched the last remaining DCC packages to their TypeScript
  `@interop/*` forks: `@digitalcredentials/vc` (`^10.0.0`) → `@interop/vc`
  (`^11.0.1`); `@digitalcredentials/jsonld-signatures` (`^12.0.1`) →
  `@interop/jsonld-signatures` (`^11.6.7`);
  `@digitalcredentials/vc-bitstring-status-list` (`^1.0.0`) →
  `@interop/vc-bitstring-status-list` (`^3.0.1`); `@digitalcredentials/did-io`
  (`^1.0.2`) → `@interop/did-io` (`^4.0.2`);
  `@digitalcredentials/did-method-key` (`^3.0.0`) → `@interop/did-method-key`
  (`^7.1.1`). Added `@interop/data-integrity-core` (`^6.1.2`) as a direct
  dependency for the ecosystem's shared types.
- **Types**: The local `DocumentLoader`, `CryptoSuite`, and `ProofPurpose` types
  are now thin aliases over the shared `@interop/data-integrity-core`
  (`IDocumentLoader`/`IRemoteDocument`) and `@interop/jsonld-signatures`
  (`LinkedDataProof`/`ProofPurpose`) types, so the verifier speaks the
  ecosystem's vocabulary end-to-end. The unused `LinkedDataSuite` /
  `DataIntegritySuite` public exports were dropped (`CryptoSuite` is now
  `LinkedDataProof`).
- **Fix (status list)**: `@interop/vc-bitstring-status-list` (`v3`) splits the
  `checkStatus` result — `verified` now means "status checked without error" and
  the revoked/suspended bit moved into each `results[].status`. The status suite
  reads `results[].status` accordingly (previously relied on
  `verified === false` meaning revoked).
- **Removed (`@interop/vc`)**: the `verifyMatchingIssuers` option is gone from
  `@interop/vc`'s verify APIs; the Data Integrity adapter no longer passes it
  (it had always passed `false` to disable that check).
- **Fix (document loader)**: the custom http(s) protocol handler in
  `documentLoaderFromHttpGet` now returns the bare document. The
  `@interop/security-document-loader` (jsonld-document-loader ≥ 2) wraps the
  handler result in `{ contextUrl, document, documentUrl }` itself, so the
  previous wrapper double-nested it (`document.document`). Restores networked
  status-list / context fetching (`test:smoke`).
- **Fix (`BuiltinHttpGetService`)**: the default `HttpGetService` now also
  JSON-parses a string response body (falling back to raw text), so hosts that
  serve JSON-LD as `text/plain` (e.g. raw.githubusercontent.com status lists)
  yield a parsed document. Centralizes the contract that `body` is the parsed
  JSON document for all consumers (document loader, did:web driver, registry
  fetches), not just the JSON Content-Type path.

## 2.0.0 - Month XX 2026

Verifier results now fold per-suite checks into a single
`summary: SuiteSummary[]` rollup; `results[]` carries only failures and explicit
`<suite>.applies` skips by default. The full check list remains available via
`verbose: true` on the verifier or per call.

### Added

- `SuiteSummary` type and `summary: SuiteSummary[]` field on
  `CredentialVerificationResult` and `PresentationVerificationResult`.
- `id: string` field on `CheckResult` — dot-separated
  `<phase>.<suite>.<localPart>` namespace.
- `verbose?: boolean` on `VerifierConfig`, `VerifyCredentialCall`,
  `VerifyPresentationCall` (per-call wins over instance default;
  `verifyPresentation` propagates the flag to embedded credentials).
- Pure `foldCheckResults` helper and `computeId` namespace builder, both
  exported from the package barrel.
- New consumer doc at `docs/api/verification-results.md` covering the folded
  shape, the `id` namespace, a UI rendering recipe, and a prompt-ready appendix
  for downstream UIs.
- `timing?: boolean` flag on `VerifierConfig`, `VerifyCredentialCall`, and
  `VerifyPresentationCall`. When true, every `CheckResult`, every
  `SuiteSummary`, and every top-level
  `Credential|PresentationVerificationResult` carries a `timing: TaskTiming`
  field describing wall-clock start/end and monotonic duration. Mirrors the
  `verbose` flag's plumbing; per-call wins; propagates from `verifyPresentation`
  into embedded `verifyCredential` calls. See `docs/api/timing.md`.
- `TaskTiming` interface (`startedAt`, `endedAt`, `durationMs`, optional
  recursive `events`). The reserved `events` field is forward-compatible with
  future sub-event capture from inside a single check.
- `TimeService` interface plus `RealTimeService` and `FakeTimeService`
  factories. New optional `timeService` on `VerifierConfig` (defaults to
  `RealTimeService()`). Now available on `VerificationContext.timeService` for
  any future check that needs to ask "what time is it?" — useful groundwork for
  credential expiration, signature clock-skew, key rotation, and status-list
  freshness work.

### Changed

- **Default `results[]` shape**: failures + explicit `<suite>.applies` skips
  only. Pass `verbose: true` to restore the prior shape.
- `flattenPresentationResults` semantically unchanged; in folded mode the
  returned array is naturally smaller.

### Deprecated

- `CheckResult.check` and `CheckResult.suite` — use `CheckResult.id` instead.
  Removal target: the next major.

### Migration

- To restore the prior result shape with no other changes: pass `verbose: true`
  on the verifier or per call.
- To adopt the new shape: read `result.summary[]` for the per-suite rollup; read
  `result.results[]` for failure detail; use
  `r.id?.startsWith(summary.id + '.')` to find detail rows under a failing
  summary entry.

## 1.0.0-beta.11 - December 15 2025

### Added

- Returns staus list errors that had been incorrectly swallowed. See the README
  for new errors that are returned.

## 1.0.0-beta.10 - October 24 2025

### Added

- Returns more informative results for json-ld safe-mode errors. See the README
  for details.

## 1.0.0-beta.9 - October 2 2025

### Added

- Adds schema validation results to the returned verification results.
