Skip to content

feat: migrate Credo 0.5 to 0.7 - #61

Open
jrhender wants to merge 6 commits into
chore/eslint9-prettier3from
chore/credo-0.7
Open

feat: migrate Credo 0.5 to 0.7#61
jrhender wants to merge 6 commits into
chore/eslint9-prettier3from
chore/credo-0.7

Conversation

@jrhender

@jrhender jrhender commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Fourth of five stacked PRs for #57 (stacked on #60) — the headline item.

Upgrades @credo-ts/{core,askar,node} 0.5.10 → 0.7.0 and replaces the @hyperledger/aries-askar bindings with @openwallet-foundation/askar 0.6 (koffi-based — no more ffi-napi/ref-napi compilation). Node 22 → 24 everywhere, which the new bindings support and which Jest needs to require() the now ESM-only Credo packages.

Code migration (Credo 0.6/0.7 breaking changes)

  • CredoService: walletConfig moved from the agent InitConfig to the AskarModule store config; the separate raw AskarWallet + SigningProviderRegistry are gone. A withAskarSession() helper (via AskarStoreManager) replaces the raw wallet accessor; autoUpdateStorageOnStartup migrates pre-0.6 stores.
  • KeyService: agent.wallet.createKey → the new KMS API. Keys are imported with an explicit kid so the REST keyId contract (base58 of the public key) is unchanged. Private-key export (GET /key/:keyId) is intentionally absent from the KMS API and now reads through a raw Askar session (keeps the documented export/import portability flow). Fixes a latent bug where secp256k1 keys were stored as mislabelled Ed25519 keys; they are now proper EC JWKs, unblocking did:ethr generation.
  • DIDKeyFactory: dids.create takes a KMS keyId (the Key/Ed25519Jwk classes are gone). Duplicate DID creation now throws on the DidRecord, so the factory returns the existing document — the seeder re-registers its DIDs on every boot (restart-with-same-wallet verified).
  • CredentialsService: proofPurpose must be a jsonld-signatures purpose instance instead of a string.
  • TypedArrayEncoder.fromBase64 is strict in 0.7 (no unpadded/url-safe input) — JWK fields now decode via fromBase64Url/Buffer.

Load-bearing constraints (commented in code)

  • @openwallet-foundation/askar-nodejs must be imported before any @credo-ts/* package — it registers the native binding that the ESM-only Credo packages snapshot at load time.
  • Jest runs with NODE_OPTIONS=--experimental-vm-modules; jest-setup/preload.cjs loads the askar packages once per worker (koffi type registration is process-global) and moduleNameMapper shims hand the singletons to each test registry; webcrypto-core is pinned to its CJS build to avoid concurrent dual-format loading.
  • The @credo-ts/core patch is re-created against 0.7: signing a DIDAuth presentation (no verifiableCredential) still fails class validation (Make verifiableCredential property on W3cPresentation optional openwallet-foundation/credo-ts#2038).

Verification

  • All unit suites (69 tests), the did library suite (incl. a new idempotency test), the e2e project and the app e2e suite pass (the 4 Resident Card tests need w3id.org access — CI covers them)
  • node:24 Docker image builds and runs; manual smoke: boot + seeding, DID create, key export round-trip (private JWK), credential issuance, wallet reuse across restarts

Part of #57

🤖 Generated with Claude Code

jrhender and others added 2 commits July 3, 2026 09:38
Upgrade @credo-ts/{core,askar,node} from 0.5.10 to 0.7.0 and replace
the @hyperledger/aries-askar bindings with @openwallet-foundation/askar
0.6 (koffi-based - no more ffi-napi/ref-napi native compilation).
Node 22 -> 24 everywhere, which the new bindings support and which
jest needs to require() the now ESM-only Credo packages.

Code migration (Credo 0.6/0.7 breaking changes):
- CredoService: walletConfig moved from the agent InitConfig to the
  AskarModule store config; the separate raw AskarWallet +
  SigningProviderRegistry are gone. A withAskarSession() helper
  (AskarStoreManager) replaces the raw wallet accessor, and
  autoUpdateStorageOnStartup migrates pre-0.6 stores on first start.
- KeyService: the removed wallet API (agent.wallet.createKey) is
  replaced by the new KMS API. Keys are imported with an explicit kid
  so the REST keyId contract (base58 of the public key) is unchanged.
  Private-key export (GET /key/:keyId) is intentionally not part of
  the KMS API and now reads through a raw Askar session. Fixes a
  latent bug where secp256k1 keys were stored as mislabelled Ed25519
  keys; they are now proper EC JWKs (keyId = base58 of the compressed
  public key), which unblocks did:ethr generation.
- DIDKeyFactory: dids.create now takes a KMS keyId (Key/Ed25519Jwk
  classes are gone) and creating a duplicate DidRecord throws, so the
  factory resolves and returns the existing document instead - the
  seeder re-registers its DIDs on every boot (verified: restart with
  the same wallet re-seeds cleanly).
- CredentialsService: proofPurpose must now be a jsonld-signatures
  purpose instance instead of a string; map authentication and
  assertionMethod accordingly.
- TypedArrayEncoder.fromBase64 is strict in 0.7 (no unpadded/url-safe
  input) - use fromBase64Url/Buffer where JWK fields are decoded.

Import-order constraint: @openwallet-foundation/askar-nodejs must load
before any @credo-ts/* package (it registers the native binding that
the ESM-only Credo packages snapshot at load time). Enforced in
CredoService, main.ts and the did-key-factory spec, with comments.

Jest / require(esm) plumbing:
- test scripts run with NODE_OPTIONS=--experimental-vm-modules (jest
  loads ESM through vm modules) via cross-env
- jest-setup/preload.cjs loads the askar packages once per worker
  process (their koffi type registration is process-global and cannot
  run once per test file); moduleNameMapper hands the singletons to
  every test module registry
- webcrypto-core is mapped to its CJS build so the dual-format package
  is never loaded as CJS and ESM concurrently

The @credo-ts/core patch is re-created against 0.7: signing a DIDAuth
presentation (no verifiableCredential property) still fails class
validation (credo-ts issue #2038).

Unit suites, the did library suite (incl. new idempotency test), the
e2e project and the app e2e suite pass; the 4 Resident Card e2e tests
need w3id.org network access and are covered in CI. Verified manually:
boot + seeding, DID create, key export round-trip, credential issue,
wallet reuse across restarts.

Part of #57

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: John Henderson <jrhender@users.noreply.github.com>
For the shared root jest.config.js, jest resolves <rootDir> to the
package under test (the directory containing its package.json), not the
repository root - CI caught the wrong mapper paths that local runs of
individual packages had not.

webcrypto-core now goes through the same preload singleton mechanism as
the askar packages instead of a hard-coded pnpm virtual-store path,
which also removes the dependence on pnpm hoisting internals.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: John Henderson <jrhender@users.noreply.github.com>
@jrhender
jrhender force-pushed the chore/eslint9-prettier3 branch from e8fcf8a to fe6237b Compare July 3, 2026 13:39
jrhender and others added 4 commits July 3, 2026 10:02
Credo 0.7's JSON-LD stack signs in safe mode, which rejects credentials
using terms that their context does not define (previously the unknown
type was silently dropped from the signed document). Define the
ConsentCredential type term in the consent credential fixture, matching
what the credentials e2e suite already does.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: John Henderson <jrhender@users.noreply.github.com>
- Log the errors at warn level when a presentation submission fails
  verification in the exchanges and workflows submission verifiers -
  useful for operating the service and for diagnosing e2e failures in
  CI, where only the resulting 400 status was visible
- nock 14.0.0 -> 14.0.16 (bugfix releases for the newer Node runtimes)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: John Henderson <jrhender@users.noreply.github.com>
- The presentation definitions in the consent-and-resident-card suite
  pin the consent credential's inline context with
  additionalProperties: false; allow the ConsentCredential type term
  that the credential now defines (required by Credo 0.7's safe-mode
  JSON-LD signing)
- Exchange callbacks are submitted fire-and-forget by the server, so
  poll the nock scope instead of asserting immediately; Credo 0.7's
  verification is slow enough that CI consistently lost this race in
  the workflows suite

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: John Henderson <jrhender@users.noreply.github.com>
CI does not reliably capture the app's own stdout, so failing exchange
continuations only showed a bare status code. Print the response body
from the test side when the status is unexpected, and surface nock
'no match' events for callback requests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: John Henderson <jrhender@users.noreply.github.com>
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.

1 participant