Shared wallet-domain logic (WAS sync engine core and wallet Space layout contracts) for Interop wallet apps.
@interop/wallet-core is the shared, correctness-critical code two WAS-enabled
wallet apps (a React Native mobile wallet and a browser wallet) hold in common:
the cross-replica byte-compatibility surface both must agree on to converge on
identical bytes. It is isomorphic (browser, Node.js, React Native) and has no UI
or storage dependencies -- side effects are injected, and the dep-heavier
protocol subpaths are import-directly-only.
The subpaths:
-
@interop/wallet-core/sync-- the Wallet Attached Storage (WAS) replication engine core: theSyncEngineorchestration (single-flight, migrate-once, backoff), therunPull/runPushalgorithms, the replica-sideSyncStoreseam, and the genericSyncedCollectionSpecshape. The wire contract and port (WasSyncPort,WireDoc,DocCipher, ...) are re-exported from@interop/was-clientso an engine consumer imports one package. -
@interop/wallet-core/space-- the wallet Space layout contract: the shared collection ids and descriptive specs (private-credentials,public-credentials,wallet-activity,app-connections), thewallet-activitywire shape with its pureaddHistory*payload builders, thepublicCredentialUrlderivation, and thewas-linkQR hand-off contract. Contacts collection specs live in@interop/social-core. -
@interop/wallet-core/identity-- the WAS identity derivation both wallet apps must perform byte-for-byte identically:agentsFromSecret/agentsFromSeed(controller secret or 32-byte seed to the did:keyCapabilityAgent,ZcapClient, X25519 key agreement key, and single-key resolver, under the fixed bootstrap handle / key name),agentsFromKeyAgent(the same assembly from aCapabilityAgentan app derived itself), andsingleKeyResolver. -
@interop/wallet-core/request-- wallet-request / exchange protocol handling: request classification and parsing (CHAPI get/store events, wallet-api messages and URLs), QueryByExample matching, cryptosuite negotiation,composeVp(signer and holder injected), the pureprocessRequest(consent runs in the caller; zcap / App Connect processing injected), the App Connect app-key credential module (query validation, matching, minting, store-time refusal, legacy re-issue), theWalletOnboardingQuerytransport vocabulary (compose and classification), the VC-API exchange client, VCALMinteraction:URL handling, the requester's half of a server's ephemeral exchange (createEphemeralExchange/pollEphemeralExchange, the poll bounded by a caller signal or its own deadline), andcomposeCapabilityRequest, the zcap-only VPR a requester stores on such an exchange. The VPR type vocabulary lives in@interop/data-integrity-coreand is re-exported here. -
@interop/wallet-core/webvh-- the account's did:webvh identity: the hosted DID log, its per-client update-key rotation, the client enrollment entries, the one-entry client-revocation edit (verification methods, update key, and standing commitments out in a single entry, the staged commitment recovered by log attribution), the enrolled-client listing over a caller-verified log (for a "your wallets" surface), the fetch-and-verify-the-published-log step those ceremonies share, the WAS-backed store they write through, and ZCap signing under the did:webvh verification-method id. -
@interop/wallet-core/resourceLog-- the client side of the Resource Log Profile (the hash-linked log format governing key resources co-managed between a wallet's clients and the storage server): full chain verification against an adversarial host (parse shape, SCID and entry-hash recomputation, entry proofs, the external-authorization rule against the independently verified did:webvh controller document, terminal handover entries), the chain-head pin with its continuity rules, the entry builders, the read/append/create path (compare-and-swap with rebase-and-retry, read-back confirmation), and the sealing sweep -- the idempotent backstop append that re-anchors a log's head past the controller's latest membership change. Transport lives in@interop/was-client's/logsubpath; the hashing and proof kernel in@interop/did-method-webvh. -
@interop/wallet-core/keys-- the user key and its wrap-set roster, governed by thekey-map/user-key.jsonlresource log: minting, the roster's init/read/rotate primitives with their client-side guards (the verified log itself, the latest-seen epoch pin, the document-backed recipient resolver), the log-governed (and sealable) descriptor store those primitives drive, and the user key rotation cascade's per-collection op (re-epoch a collection onto the roster's current user key, staleness detected from durable state alone, history escrowed -- also the completion sweep's building block), plus the detector that converges a roster left wrapping the current key to a recipient the account document no longer keys. AlsoensureWalletSpaceEpochs, the provision-time install of each encrypted wallet collection's key epoch[0] (a fresh random epoch key wrapped to the user key) -- the EDV-bearing second step ofprovisionWalletSpace. Also the enrolled-client display labels (key-map/client-labels.json) and their WAS-backed store. Also the client-key record codec: the contents and strict validation of the local record each wallet client keeps its own key material in (storage and wrapping stay app-side). -
@interop/wallet-core/clients-- the enrolled-client management surface: the listing over the locally verified did:webvh log with display labels merged, the disconnect-eligibility policy as pure functions, the revocation cascade orchestrator (document edit, roster rotation with its seal backstop, collection fan-out, optional recovery re-mints), and the login-time roster policy (which now also seals a converged roster's governing log). -
@interop/wallet-core/descriptors-- collection encryption-descriptor acquisition (fetch / cache / offline fallback), the log-governed descriptor source (every read re-verifies the governing resource log), and the unknown-epoch refresh policy, including a self-refreshing EDV document cipher. -
@interop/wallet-core/keyring-- the unlock layer: the unlock derivation, the signed{ version, encryption, wrapped, proof }account-pointer record codec (the proof is verified before the record is decrypted), and the unlock Space lifecycle. -
@interop/wallet-core/enrollment-- the client enrollment ceremony (connect code, approval, completion) plus the onboarding-response envelope that carries a connect code back over an exchange. -
@interop/wallet-core/genesis-- the account-genesis ceremony: the local mint of a brand-new account's complete key set (mintAccountKeySet: Space id, client identity seed, user key, did:webvh update keys) and the staged provisioning both apps must encode identically (ensureAccountGenesis: Space provisioning, the optional KMS key-map acquisition, did:webvh genesis, user-key roster genesis after DID publication, epoch[0] on every encrypted roster collection, and the Space-controller promotion, also exported standing alone asensurePromotedSpaceController). Idempotent end to end: a torn run heals by re-running. The keyring bind is deliberately not a stage, so a wallet with no unlock method bound at creation drives the same ceremony. -
@interop/wallet-core/unlock-- standing unlock credentials: every unlock method (passphrase, passkey PRF, recovery code) as a standing credential in the recovery-code configuration, with self-enrolling login. The credential-derived client identity and binding MAC key, the update-key ladder (latent-and-consumed did:webvh update authority from a random seed carried in the unlock record, the current rung recovered from the log itself), the unlock record codec (shell / bridge delegation / ladder members under a credential-authenticated binding, with the bridge-only re-mint), the merged document-inventory edit (a verbatimkeyAgreemententry or apublicKeyCommitmententry for a low-entropy-derived key), and the self-enrolling continuation with its composed completion. -
@interop/wallet-core/recovery-- recovery codes on the roster identity model, over theunlockmachinery: a code as a minimal always-enrolled wallet client (format and derivation, the document half of issuance / revocation / recovery with its spend-on-use continuation, the pre-minteddid.jsonldelegation builder and the revocation cascade's bridge re-mint core; the record codec is theunlocksubpath's, re-exported here).
- Node.js 24+ is recommended.
pnpm install @interop/wallet-core
git clone https://github.com/interop-alliance/wallet-core.git
cd wallet-core
pnpm install
import { SyncEngine, runPull, runPush } from '@interop/wallet-core/sync'
import {
PRIVATE_CREDENTIALS_COLLECTION,
publicCredentialUrl,
buildWasLinkPayload,
parseWasLinkPayload,
addHistoryCredentialCreated
} from '@interop/wallet-core/space'The sync and space subpaths are re-exported from the package root as well.
Every other subpath (identity, request, webvh, keys, clients,
descriptors, keyring, enrollment, recovery) is import-directly-only, so
consumers of the root never pull the signing / KMS / document-loader dependency
graph.
PRs accepted. See CONTRIBUTING.md for editor setup (Prettier, ESLint, and EditorConfig) and how it maps to CI.
If editing the Readme, please conform to the standard-readme specification.
MIT License © 2026 Interop Alliance.