A Wallet Attached Storage Javascript/TypeScript client for Node, browser and React Native.
See in progress spec: https://wallet.storage/spec
To use Wallet Attached Storage (to provision data spaces, to create collections, and to read and write resources), the application or service using this client will need its own cryptographically provable identity (in the form of a DID).
At minimum, this means that the app or service using this client will need to create and manage its own public-private key pair. The challenges and security considerations of cryptographic key management are beyond the scope of this usage document, but roughly:
-
For in browser client-only Single Page Applications (SPAs), the app's identity is ephemeral -- essentially a key pair will be generated for each new user session, and stored in the browser.
-
For traditional server side web applications, it is recommended that the app's DID and key pairs are managed securely, on the server side, preferably using an HSM (Hardware Security Module).
-
For mobile apps, key pairs may be generated during setup, and can take advantage of the mobile operating system's keychain and encrypted app storage.
TBD
This client assumes:
- a remote Wallet Attached Storage server is available
- the app is able to create and store a public-private key pair, see the App Identity and Key Management section for more details.
Most W.A.S. HTTP operations (creating a space, creating collections, reading and writing resources) require authorization in the form of zCap invocations via HTTP Signatures.
The fetch-client
in these examples automatically constructs the proper http
headers, if you pass it a DID Signer object. A Signer is a minimal abstraction
used to deal with a did:key
and its associated key pair, with the interface of:
interface ISigner {
id: string // Specifically, a did:key key id
sign: ({ data: Uint8Array }) => Promise<Uint8Array>
}
Several DID-related libraries either provide signers directly, or it's fairly easy to construct them. Some examples:
import { Ed25519VerificationKey2020 }
from '@digitalcredentials/ed25519-verification-key-2020' // "^5.0.0-beta.2"
const keyPair = await Ed25519VerificationKey2020.generate()
keyPair.id = `did:key:${keyPair.fingerprint()}#${keyPair.fingerprint()}`
const appDidSigner = keyPair.signer()
or:
import { Ed25519Signer } from '@did.coop/did-key-ed25519' // "^0.0.9"
const appDidSigner = await Ed25519Signer.generate()
Provision (create) a new space :
import { StorageClient } from '@wallet.storage/fetch-client' // "^1.1.2"
const url = 'https://data.pub' // load this from config or env variable
const storage = new StorageClient(new URL(url))
const space = storage.space({ signer: appDidSigner })
// Create a new space (sends HTTP API call)
await space.put()
// Save the space.id for later re-use
const spaceId = space.id
Some sample serialization and importing of keys (warning: not recommended for production use -- this is what HSMs are for).
// Serialize to JSON for storage
// If using @digitalcredentials/ed25519-verification-key-2020
const exportedKeyPair = await keyPair.export({ publicKey: true, privateKey: true })
// If using @did.coop/did-key-ed25519@0.0.9
const exportedKeyPair = appDidSigner.toJSON()
localStorage.setItem('app-DID', JSON.stringify(exportedKeyPair))
// Load from storage and turn back into a DID Signer
const serializedKeyPair = localStorage.getItem('app-DID')
// If using @digitalcredentials/ed25519-verification-key-2020
const keyPair = await Ed25519VerificationKey2020.from(serializedKeyPair)
const appDidSigner = keyPair.signer()
// If using @did.coop/did-key-ed25519@0.0.9
const appDidSigner = Ed25519Signer.fromJSON(serializedKeyPair)
import { StorageClient } from '@wallet.storage/fetch-client'
const url = 'https://data.pub' // load this from config or env variable
const storage = new StorageClient(new URL(url))
const space = storage.space({ signer: appDidSigner, id: spaceId })
Now you can read and write resources to and from collections:
// Create a reference to the Credentials collection (note that no API calls are
// made yet)
const credentials = storage.collection('credentials')
// Iterate through the collection, fetch the resources
for await (const resource of credentials) {
const response = await resource.get()
const vc = await response.json()
console.log(JSON.stringify(vc, null, 2))
}
// Upload a VC
await credentials.resource().put(
new Blob(JSON.stringify(vc), { type: 'application/vc' })
)
// Upload Evidence / Documents
await storage.collection('documents')
.resource().put(getDocumentBlob())
PRs accepted.
If editing the Readme, please conform to the standard-readme specification.
MIT License © 2025 DID.coop.