While there are many init functions that are exposed and that can be used, we recommend instead sticking to the initLearnCard function.
initLearnCard is a config-driven, heavily overloaded function, that allows you to construct a wallet flexibly, without sacrificing type safety.
Under the hood, it is simply a map between the config you provide and the init function you would normally need to call, meaning calls like initLearnCard() and emptyLearnCard() are identical.
Example Usage
import { initLearnCard } from '@learncard/init';
// Constructs an empty LearnCard without key material (can not sign VCs).
// Useful for Verifying Credentials only in a light-weight form.
const emptyLearncard = await initLearnCard();
// Constructs a LearnCard from a deterministic seed.
const learncard = await initLearnCard({ seed: 'abc123' });
// Constructs a LearnCard default connected to LearnCard Network hosted at https://network.learncard.com
const networkLearnCard = await initLearnCard({ seed: 'abc123', network: true });
// Constructs a LearnCard default connected to VC-API at https://bridge.learncard.com for handling signing
const defaultApi = await initLearnCard({ vcApi: true });
// Constructs a LearnCard connected to a custom VC-API, with Issuer DID specified.
const customApi = await initLearnCard({ vcApi: 'vc-api.com', did: 'did:key:123' });
// Constructs a LearnCard connected to a custom VC-API that implements /did discovery endpoint.
const customApiWithDIDDiscovery = await initLearnCard({ vcApi: 'https://bridge.learncard.com' });
// Constructs a LearnCard with no plugins. Useful for building your own bespoke LearnCard
const customLearnCard = await initLearnCard({ custom: true });
The examples above are not exhaustive of possible ways to instantiate a LearnCard:
The learnCardFromSeed function
Helper function to quickly create a LearnCard from seed.
In production environments, take great care and caution when generating and storing key material. Insufficient entropy or insecure storage, among other vectors, can easily compromise your data and identities.
Warning: Key input should be a hexadecimal string. If you pass a string that is not valid hex, an error will be thrown!
Good to know: If you do not pass in a string that is 64 characters long. It will be prefixed with zeroes until it is 64 characters. This means that '1' and '001' are identical keys in the eyes of initLearnCard.
How to generate and store keys is left to you, the consumer. However, if you'd like to simply generate a random key, you can do so with the following code:
const randomKey = Array.from(crypto.getRandomValues(new Uint8Array(32)), dec =>
dec.toString(16).padStart(2, "0")
).join("");
import crypto from 'node:crypto';
const randomKey = crypto.randomBytes(32).toString('hex');
// Make sure you have the didkit plugin installed! pnpm i @learncard/didkit-plugin
import { initLearnCard } from '@learncard/init';
import didkit from '@learncard/didkit-plugin/dist/didkit/didkit_wasm_bg.wasm';
const learnCard = await initLearnCard({ seed: 'abc123', didkit });
// Make sure you have the didkit plugin installed! pnpm i @learncard/didkit-plugin
import { initLearnCard } from '@learncard/init';
import didkit from '@learncard/didkit-plugin/dist/didkit/didkit_wasm_bg.wasm?url';
const learnCard = await initLearnCard({ seed: 'abc123', didkit });
Create Credentials
Test Credential
One of the easiest—and fastest—ways to create a credential is to generate a test credential:
// Returns an unsigned, test credential in the OBv3 spec.
const unsignedVc = learnCard.invoke.getTestVc();
Basic Boost Credential
In it's most basic form, you can create a Boost credential using the following schema:
But sometimes the test credential is too basic for most use cases. That's why LearnCard has out-of-the-box support for some basic types of credentials, and a simple function to create new credentials:
After a credential is signed, the credential may be transferred via an exchange mechanism, where a receiving party can verify it! To verify a signed Verifiable Credential, you can use verifyCredential
const result = await learnCard.invoke.verifyCredential(signedVc);
console.log(result);
// { checks: ['proof', 'expiration'], warnings: [], errors: [] }
// OR, for a more human readable output:
const result = await learnCard.invoke.verifyCredential(signedVc, {}, true);
// [
// { status: "Success", check: "proof", message: "Valid" },
// {
// status: "Success",
// check: "expiration",
// message: "Valid • Does Not Expire"
// }
// ]
signedVc.expirationDate = '2022-06-10T18:26:57.687Z';
const result = await learnCard.invoke.verifyCredential(signedVc);
console.log(result);
// {
// checks: ['proof'],
// warnings: [],
// errors: [
// 'signature error: Verification equation was not satisfied',
// 'expiration error: Credential is expired'
// ]
// }
// OR, for a more human readable output:
const result = await learnCard.invoke.verifyCredential(signedVc, {}, true);
console.log(result);
// [
// {
// status: "Failed",
// check: "signature",
// details: "signature error: Verification equation was not satisfied"
// },
// {
// status: "Failed",
// check: "expiration",
// details: "Invalid • Expired 10 JUN 2022"
// },
// { status: "Success", check: "proof", message: "Valid" }
// ]
Issue/Verify Presentations
Similar to Verifiable Credentials, LearnCard has methods for verifying and issuing Verifiable Presentations:
const unsignedVp = await learnCard.invoke.getTestVp();
//Package signed Verifiable Credential into the presentation
unsignedVp.verifiableCredential = signedVc;
const vp = await learnCard.invoke.issuePresentation(unsignedVp);
const result = await learnCard.invoke.verifyPresentation(vp);
console.log(result);
// {
// checks: ['proof'],
// warnings: [],
// errors: [],
// }
Verifiable Presentations enable trusted sharing of one or more claims in a single, verifiable package. Claims may be bundled from one or multiple issuers, and they may consist of the original Verifiable Credential, or a derived "zero-knowledge proof."
As a general principle, you should use Verifiable Presentations when presenting Verifiable Credentials to a verifying party because it proves the relationship between the user or entity presenting the credential, and the credential itself.
Storing/Retrieving Credentials
Issuer
const holderDid = 'did:key:z6MknqnHBn4Rx64gH4Dy1qjmaHjxFjaNG1WioKvQuXKhEKL5'
const uvc = learnCard.invoke.newCredential({ subject: holderDid });
const vc = await learnCard.invoke.issueCredential(uvc);
const uri = await learnCard.store.LearnCloud.uploadEncrypted(vc);
// *** Send URI to Holder ***
Holder
// *** Receive URI from Issuer ***
const credential = await learnCard.read.get(uri);
const result = await learnCard.invoke.verifyCredential(credential);
if (result.errors.length == 0) {
await learnCard.index.LearnCloud.add({ uri, id: 'test' });
}
// Later, when the Holder would like to see the credential again
const records = await learnCard.index.LearnCloud.get();
const record = records.find(({ id }) => id === 'test');
const storedCredential = await learnCard.read.get(record.uri);
// _.isEqual(credential, storedCredential) = true
LearnCloud Network
Create a Profile
To create a new profile, use the createProfile method. This method accepts an object containing the profile information, excluding the did and isServiceProfile properties.
These examples demonstrate creating, retrieving, updating, and deleting different types of profiles and profile managers.
// --- Creating Profiles ---
const profileDetails = {
profileId: 'john.doe',
displayName: 'John Doe',
email: 'john.doe@example.com',
// ... other LCNProfile fields
};
const managerDetails = {
managerDid: 'did:example:manager123',
// ... other LCNProfileManager fields
};
try {
// Create a regular user profile
const regularProfileDid = await learnCard.invoke.createProfile(profileDetails);
console.log('Regular Profile DID:', regularProfileDid);
// Create a service profile
const serviceProfileDid = await learnCard.invoke.createServiceProfile(profileDetails);
console.log('Service Profile DID:', serviceProfileDid);
// Create a managed profile (often used for profiles requiring oversight)
const managedProfileDid = await learnCard.invoke.createManagedProfile(profileDetails);
console.log('Managed Profile DID:', managedProfileDid);
// Create a managed service profile
const managedServiceProfileDid = await learnCard.invoke.createManagedServiceProfile(profileDetails);
console.log('Managed Service Profile DID:', managedServiceProfileDid);
// Create a profile manager
const profileManagerId = await learnCard.invoke.createProfileManager(managerDetails);
console.log('Profile Manager ID:', profileManagerId);
// Create a child profile manager under an existing parent
const parentManagerUri = 'uri:manager:parent123'; // Replace with actual parent URI
const childProfileManagerId = await learnCard.invoke.createChildProfileManager(parentManagerUri, managerDetails);
console.log('Child Profile Manager ID:', childProfileManagerId);
} catch (error) {
console.error('Error creating profile:', error);
}
Retrieving & Searching Profiles
To search for profiles, use the searchProfiles method. This method accepts an optional profileId parameter and an options object. The options object can contain the following properties:
limit: Maximum number of profiles to return.
includeSelf: Whether to include the current user's profile in the results.
includeConnectionStatus: Whether to include connection status in the results.
// --- Retrieving & Searching Profiles ---
const queryOptionsMock = { limit: 10, cursor: undefined }; // Example pagination
const profileQueryMock = { displayName: 'John' }; // Example query
try {
// Get the current authenticated user's profile
const myProfile = await learnCard.invoke.getProfile(); // No argument for self
console.log('My Profile:', myProfile);
// Get another profile by its ID
const otherProfileId = 'jane.doe'; // Example profile ID
const otherProfile = await learnCard.invoke.getProfile(otherProfileId);
console.log(`Profile for ${otherProfileId}:`, otherProfile);
// Get profile manager details
const someManagerId = 'manager-xyz'; // Example manager ID
const profileManager = await learnCard.invoke.getProfileManagerProfile(someManagerId);
console.log('Profile Manager:', profileManager);
// Search for profiles
const searchResults = await learnCard.invoke.searchProfiles('john', { limit: 5, includeConnectionStatus: true });
console.log('Search Results for "john":', searchResults);
// Get profiles available to the current user (e.g., owned or managed)
const availableProfiles = await learnCard.invoke.getAvailableProfiles({ query: profileQueryMock, ...queryOptionsMock });
console.log('Available Profiles:', availableProfiles);
// Get profiles managed by the current user
const managedProfiles = await learnCard.invoke.getManagedProfiles({ query: profileQueryMock, ...queryOptionsMock });
console.log('Managed Profiles:', managedProfiles);
// Get service profiles managed by the user (or by a specific manager ID if provided)
const managerIdForServiceProfiles = 'manager-abc';
const managedServiceProfiles = await learnCard.invoke.getManagedServiceProfiles({ id: managerIdForServiceProfiles, ...queryOptionsMock });
console.log(`Managed Service Profiles for manager ${managerIdForServiceProfiles}:`, managedServiceProfiles);
} catch (error) {
console.error('Error retrieving profiles:', error);
}
Updating & Deleting Profiles
// --- Updating & Deleting Profiles ---
const profileUpdates = {
displayName: 'Johnathan Doe',
bio: 'Updated bio information.',
// ... other fields to update
};
const managerUpdates = {
displayName: 'Senior Manager',
// ... other fields
}
try {
// Update the current authenticated user's profile
// Note: updateProfile typically updates the profile associated with the current learnCard instance's DID
const updateSuccess = await learnCard.invoke.updateProfile(profileUpdates);
console.log('Profile update success:', updateSuccess);
// Update a profile manager's profile
// Assuming managerUpdates includes the manager's identifier or is for the current profile manager context
const managerUpdateSuccess = await learnCard.invoke.updateProfileManagerProfile(managerUpdates);
console.log('Manager profile update success:', managerUpdateSuccess);
// Delete the current authenticated user's profile
// Be very careful with this operation!
// const deleteSuccess = await learnCard.invoke.deleteProfile();
// console.log('Profile delete success:', deleteSuccess);
} catch (error) {
console.error('Error updating/deleting profile:', error);
}
Connection Management
Connect with a Profile
To send a connection request to another profile, use the connectWith method. This method accepts a profileId parameter.
To generate an invite, use the generateInvite method. This method now accepts two parameters: a challenge and an expiration parameter. The challenge parameter is optional and will be automatically generated if not provided. The expiration parameter sets the duration (in seconds) for which the invite remains valid and defaults to 30 days if not specified.
Advanced examples for establishing, managing, and retrieving connections between profiles.
const targetProfileId = 'jane.doe.connections'; // Example target profile ID for connection
const connectionRequestProfileId = 'john.wayne.connections'; // Example profile ID of an incoming connection request
try {
// --- Connection Operations ---
// Send a connection request
const connectSuccess = await learnCard.invoke.connectWith(targetProfileId);
console.log(`Connection request to ${targetProfileId} success:`, connectSuccess);
// Accept an incoming connection request
const acceptSuccess = await learnCard.invoke.acceptConnectionRequest(connectionRequestProfileId);
console.log(`Accepted connection request ${connectionRequestProfileId}:`, acceptSuccess);
// Disconnect with a connected profile
const disconnectSuccess = await learnCard.invoke.disconnectWith(targetProfileId);
console.log(`Disconnected from ${targetProfileId}:`, disconnectSuccess);
// Cancel an outgoing connection request you previously sent
const cancelSuccess = await learnCard.invoke.cancelConnectionRequest(targetProfileId);
console.log(`Cancelled connection request to ${targetProfileId}:`, cancelSuccess);
// --- Connection Invitations ---
// Generate an invitation
const inviteDetails = await learnCard.invoke.generateInvite('optional-challenge-string', 3600); // Expires in 1 hour
console.log('Generated Invite:', inviteDetails);
// (Off-band: share inviteDetails.profileId and inviteDetails.challenge with another user)
// Another user connects using the invitation
// This would typically be called by a different learnCard instance/user
// const connectingProfileId = inviteDetails.profileId;
// const challengeFromInvite = inviteDetails.challenge;
// const connectInviteSuccess = await otherLearnCard.invoke.connectWithInvite(connectingProfileId, challengeFromInvite);
// console.log('Connected via invite:', connectInviteSuccess);
// --- Retrieving Connections ---
const paginationOptions = { limit: 10 };
const connections = await learnCard.invoke.getPaginatedConnections(paginationOptions);
console.log('My Connections:', connections.records);
const pendingRequests = await learnCard.invoke.getPaginatedPendingConnections(paginationOptions);
console.log('My Pending Outgoing Requests:', pendingRequests.records);
const incomingRequests = await learnCard.invoke.getPaginatedConnectionRequests(paginationOptions);
console.log('My Incoming Connection Requests:', incomingRequests.records);
// --- Blocking Profiles ---
const profileToBlock = 'annoying.user';
const blockSuccess = await learnCard.invoke.blockProfile(profileToBlock);
console.log(`Blocked ${profileToBlock}:`, blockSuccess);
const blockedProfiles = await learnCard.invoke.getBlockedProfiles();
console.log('Blocked Profiles:', blockedProfiles);
// Unblock a profile
// const unblockSuccess = await learnCard.invoke.unblockProfile(profileToBlock);
// console.log(`Unblocked ${profileToBlock}:`, unblockSuccess);
} catch (error) {
console.error('Error in connection management:', error);
}
Credential & Presentation Exchange
Demonstrates sending, receiving, and managing Verifiable Credentials (VCs) and Verifiable Presentations (VPs).
Send a Credential
To send a credential to another profile, use the sendCredential method. This method accepts a profileId, a vc object (which can be an UnsignedVC or VC), and an optional encrypt parameter.
To retrieve all received credentials, use the getReceivedCredentials method. This method accepts an optional from parameter.
const from = 'johnsmith';
await learnCard.invoke.getReceivedCredentials(from);
Accept a Credential
To accept a credential, use the acceptCredential method. This method accepts a uri parameter.
const uri = 'your_credential_uri';
await learnCard.invoke.acceptCredential(uri);
Send a Presentation
To send a presentation to another profile, use the sendPresentation method. This method accepts a profileId, a vp object, and an optional encrypt parameter.
// Assume vcMock and vpMock are properly structured Verifiable Credential and Presentation objects
const vcMock = await learnCard.invoke.issueCredential(learnCard.invoke.getTestVc())
const vpMock = await learnCard.invoke.issuePresentation(await learnCard.invoke.getTestVp())
const recipientProfileId = 'bob.the.receiver';
const credentialUriToManage = 'uri:credential:xyz789'; // Example URI of a received/sent credential
try {
// --- Sending Credentials & Presentations ---
const sentCredentialUri = await learnCard.invoke.sendCredential(recipientProfileId, vcMock, true); // Encrypt = true
console.log('Sent Credential URI:', sentCredentialUri);
const sentPresentationUri = await learnCard.invoke.sendPresentation(recipientProfileId, vpMock, true); // Encrypt = true
console.log('Sent Presentation URI:', sentPresentationUri);
// --- Accepting Credentials & Presentations ---
// (This would typically be called by the recipient's learnCard instance)
// const receivedCredentialUri = 'uri:credential:abc123'; // URI from notification or shared link
// const acceptCredentialSuccess = await recipientLearnCard.invoke.acceptCredential(receivedCredentialUri);
// console.log('Accepted Credential:', acceptCredentialSuccess);
// const receivedPresentationUri = 'uri:presentation:def456';
// const acceptPresentationSuccess = await recipientLearnCard.invoke.acceptPresentation(receivedPresentationUri);
// console.log('Accepted Presentation:', acceptPresentationSuccess);
// --- Retrieving Information ---
// Assuming 'did:example:sender' is the DID of a profile that sent you credentials
const receivedCredentials = await learnCard.invoke.getReceivedCredentials('did:example:sender');
console.log('Received Credentials:', receivedCredentials);
const sentCredentials = await learnCard.invoke.getSentCredentials(recipientProfileId);
console.log('Sent Credentials:', sentCredentials);
const incomingCredentials = await learnCard.invoke.getIncomingCredentials(); // Get all pending incoming
console.log('Incoming Credentials (pending acceptance):', incomingCredentials);
// Similar retrieval for presentations
const receivedPresentations = await learnCard.invoke.getReceivedPresentations('did:example:sender');
console.log('Received Presentations:', receivedPresentations);
// --- Deleting ---
// const deleteCredentialSuccess = await learnCard.invoke.deleteCredential(credentialUriToManage);
// console.log('Deleted Credential:', deleteCredentialSuccess);
// const deletePresentationSuccess = await learnCard.invoke.deletePresentation(credentialUriToManage); // Assuming a presentation URI
// console.log('Deleted Presentation:', deletePresentationSuccess);
} catch (error) {
console.error('Error in credential/presentation exchange:', error);
}
Boost Management
Examples covering the lifecycle of Boosts, including creation, retrieval, hierarchy, recipients, permissions, and sending.
Create a Boost
To create a boost, use the createBoost method. This method accepts a credential object (which can be an UnsignedVC or VC) and an optional metadata object.
To get a boost, use the getBoost method. This method accepts a uri parameter.
const uri = 'your_boost_uri';
await learnCard.invoke.getBoost(uri);
Send a Boost
To send a boost to another profile, use the sendBoost method. This method accepts a profileId, a boostUri parameter, and an optional encrypt parameteropconst profileId = 'janesmith';
These are the API calls related to boosts management in the LearnCard Network API. Use these methods to create, update, retrieve, and delete boosts, as well as send boosts to other profiles.
Examples for registering and using signing authorities, and generating/claiming boosts via links.
const authorityEndpoint = 'https://my-authority.example.com/sign';
const authorityName = 'MyOrg Signer';
const authorityDid = 'did:example:authority123'; // DID of the signing authority
const boostUriForClaimLink = 'uri:boost:claimable123';
// LCNBoostClaimLinkSigningAuthorityType
const claimLinkSigningAuthorityConfig = {
endpoint: authorityEndpoint,
name: authorityName,
did: authorityDid
};
// LCNBoostClaimLinkOptionsType
const claimLinkOptions = { ttlSeconds: 86400, totalUses: 10 }; // e.g., expires in 24 hours, 10 max claims
try {
// --- Signing Authorities ---
const registerSuccess = await learnCard.invoke.registerSigningAuthority(authorityEndpoint, authorityName, authorityDid);
console.log('Registered Signing Authority:', registerSuccess);
const authorities = await learnCard.invoke.getRegisteredSigningAuthorities();
console.log('Retrieved Signing Authorities:', authorities);
const singleAuthority = await learnCard.invoke.getRegisteredSigningAuthority(authorityEndpoint, authorityName);
console.log('Single Retrieved Authority:', singleAuthority);
// --- Claim Links ---
const claimLinkData = await learnCard.invoke.generateClaimLink(boostUriForClaimLink, claimLinkSigningAuthorityConfig, claimLinkOptions);
console.log('Generated Claim Link Data:', claimLinkData);
// (Off-band: share this link/data with a user)
// User claims the boost using the link data
// This would typically be called by a different learnCard instance/user
// const claimedCredentialUri = await otherLearnCard.invoke.claimBoostWithLink(claimLinkData.boostUri, claimLinkData.challenge);
// console.log('Boost claimed via link, Credential URI:', claimedCredentialUri);
} catch (error) {
console.error('Error with Signing Authorities or Claim Links:', error);
}
ConsentFlow Contracts
Examples for creating and managing Consent Flow Contracts, user consent, and data access.
// Assume consentFlowContractDefinition, consentTermsObjectMock are defined according to your types
const consentFlowContractDefinition = { contract: { read: { personal: { name: { required: true } } }, write: {} }, name: "Data Sharing Agreement", description: "Share basic profile info." };
const consentTermsObjectMock = { read: { personal: { name: 'Consented Name' } }, write: {} }; // User's specific terms based on contract
const contractUriToManage = 'uri:contract:xyz789';
const termsUriToManage = 'uri:terms:abc123'; // URI of a specific consent instance
const userDidForConsentData = 'did:example:user123';
const queryOptions = { limit: 10 };
try {
// --- Contract Management (by Owner) ---
const newContractUri = await learnCard.invoke.createContract(consentFlowContractDefinition);
console.log('Created Contract URI:', newContractUri);
// const autoBoostConfigs = [{ boostUri: 'uri:boost:auto123', signingAuthority: { endpoint: '...', name: '...' } }];
// const addAutoBoostSuccess = await learnCard.invoke.addAutoBoostsToContract(newContractUri, autoBoostConfigs);
// console.log('Added Auto-Boosts:', addAutoBoostSuccess);
const contractDetails = await learnCard.invoke.getContract(newContractUri);
console.log('Contract Details:', contractDetails);
const allMyContracts = await learnCard.invoke.getContracts(queryOptions);
console.log('My Contracts:', allMyContracts.records);
// const deleteContractSuccess = await learnCard.invoke.deleteContract(newContractUri);
// console.log('Deleted Contract:', deleteContractSuccess);
// --- User Consent Actions ---
// (User consents to a contract)
const consentTermsDetails = { terms: consentTermsObjectMock, expiresAt: new Date(Date.now() + 3600 * 1000 * 24 * 30).toISOString() }; // Expires in 30 days
const consentedTermsUri = await learnCard.invoke.consentToContract(contractUriToManage, consentTermsDetails);
console.log('Consented to Contract, Terms URI:', consentedTermsUri);
// (User retrieves their consented contracts)
const myConsentedContracts = await learnCard.invoke.getConsentedContracts(queryOptions);
console.log('My Consented Contracts (Terms):', myConsentedContracts.records);
// (User updates their terms for a specific consent)
const updatedTermsDetails = { terms: { ...consentTermsObjectMock, read: { personal: { name: 'Updated Name' }}}, oneTime: true };
const updateTermsSuccess = await learnCard.invoke.updateContractTerms(termsUriToManage, updatedTermsDetails);
console.log('Updated Contract Terms:', updateTermsSuccess);
// (User withdraws consent)
// const withdrawSuccess = await learnCard.invoke.withdrawConsent(termsUriToManage);
// console.log('Withdrew Consent:', withdrawSuccess);
// --- Data Access & Transactions (by Contract Owner or authorized profiles) ---
const consentDataForContract = await learnCard.invoke.getConsentFlowData(contractUriToManage, queryOptions);
console.log('Consented Data for Contract:', consentDataForContract.records);
const consentDataForDid = await learnCard.invoke.getConsentFlowDataForDid(userDidForConsentData, queryOptions);
console.log(`Consented Data involving DID ${userDidForConsentData}:`, consentDataForDid.records);
// const allConsentData = await learnCard.invoke.getAllConsentFlowData({}, queryOptions);
// console.log('All Consented Data for my contracts:', allConsentData.records);
const transactions = await learnCard.invoke.getConsentFlowTransactions(termsUriToManage, queryOptions);
console.log('Consent Transactions for Terms:', transactions.records);
const credentialsForContractTerms = await learnCard.invoke.getCredentialsForContract(termsUriToManage, queryOptions);
console.log('Credentials related to Contract Terms:', credentialsForContractTerms.records);
// --- Writing & Syncing Credentials based on Consent ---
// (Owner writes a credential to a consented user for a specific boost related to the contract)
const didOfConsentedUser = 'did:example:consenter123';
const boostUriRelatedToContract = 'uri:boost:contractRelatedBoost456';
// const writtenCredentialUri = await learnCard.invoke.writeCredentialToContract(didOfConsentedUser, contractUriToManage, vcMock, boostUriRelatedToContract);
// console.log('Credential written to contract for user:', writtenCredentialUri);
// (Consenter syncs their credentials to the contract terms)
const credentialsToSyncByCategory = { "Achievement": ["uri:credential:ach1", "uri:credential:ach2"] };
const syncSuccess = await learnCard.invoke.syncCredentialsToContract(termsUriToManage, credentialsToSyncByCategory);
console.log('Synced credentials to contract:', syncSuccess);
// --- Verifying Consent ---
const isConsentValid = await learnCard.invoke.verifyConsent(termsUriToManage, userDidForConsentData); // Check if userDidForConsentData has valid consent for termsUriToManage
console.log(`Consent valid for ${userDidForConsentData} on ${termsUriToManage}:`, isConsentValid);
} catch (error) {
console.error('Error in Consent Flow management:', error);
}
DID Metadata Management
Examples for adding, retrieving, updating, and deleting DID metadata.
// Assume didDocumentPartial is an object with some DID Document properties
const didDocumentPartial = { service: [{ id: '#service-1', type: 'MyService', serviceEndpoint: 'https://example.com/service' }] };
const metadataIdToManage = 'some-metadata-id'; // This would be an ID returned by addDidMetadata or associated with a DID
try {
const addMetadataSuccess = await learnCard.invoke.addDidMetadata(didDocumentPartial);
console.log('Added DID Metadata Success (or ID):', addMetadataSuccess); // Might return ID or boolean
const myDidMetadataList = await learnCard.invoke.getMyDidMetadata();
console.log('My DID Metadata Records:', myDidMetadataList);
if (myDidMetadataList.length > 0) {
const firstMetadataId = myDidMetadataList[0].id;
const specificMetadata = await learnCard.invoke.getDidMetadata(firstMetadataId);
console.log(`Specific DID Metadata for ${firstMetadataId}:`, specificMetadata);
const updatesToMetadata = { service: [{ id: '#service-1', type: 'UpdatedService', serviceEndpoint: 'https://new.example.com/service' }] };
const updateMetadataSuccess = await learnCard.invoke.updateDidMetadata(firstMetadataId, updatesToMetadata);
console.log('Updated DID Metadata:', updateMetadataSuccess);
// const deleteMetadataSuccess = await learnCard.invoke.deleteDidMetadata(firstMetadataId);
// console.log('Deleted DID Metadata:', deleteMetadataSuccess);
}
} catch (error) {
console.error('Error managing DID Metadata:', error);
}
Claim Hooks
Examples for managing claim hooks for Boosts.
// --- Claim Hooks ---
// Example 1: Creating a 'GRANT_PERMISSIONS' Claim Hook
// This hook would grant specific permissions on 'targetBoostUri'
// when 'sourceBoostClaimUri' is successfully claimed.
const grantPermissionsHookDefinition: ClaimHook = {
type: 'GRANT_PERMISSIONS', // As per ClaimHookTypeValidator
data: {
claimUri: 'uri:boost:sourceBoostClaimUri123', // The boost that, when claimed, triggers this hook
targetUri: 'uri:boost:targetBoostUri456', // The boost on which permissions will be granted
permissions: { // Partial<BoostPermissions> from BoostPermissionsValidator
canEdit: true,
canIssue: false,
// Add other permission fields as needed, e.g., canViewAnalytics: true
},
},
};
// Example 2: Creating an 'ADD_ADMIN' Claim Hook
// This hook would make the claimer an admin of 'targetResourceUriForAdmin'
// when 'anotherSourceBoostUri' is successfully claimed.
const addAdminHookDefinition: ClaimHook = {
type: 'ADD_ADMIN', // As per ClaimHookTypeValidator
data: {
claimUri: 'uri:boost:anotherSourceBoostUri789', // The boost that, when claimed, triggers this hook
targetUri: 'uri:boost:targetResourceUriForAdmin000', // The resource (e.g., another boost) the claimer becomes admin of
},
};
const boostUriToQueryHooksFor = 'uri:boost:sourceBoostClaimUri123';
const paginationOptions = { limit: 10 };
// Example Query for getClaimHooksForBoost
const specificClaimHookQuery: ClaimHookQuery = {
type: 'GRANT_PERMISSIONS', // Filter for hooks of type 'GRANT_PERMISSIONS'
data: {
targetUri: 'uri:boost:targetBoostUri456', // Further filter by the targetUri in the hook's data
// You could also add filters for permissions if your BoostPermissionsQueryValidator supports it
// permissions: { canEdit: true }
}
};
try {
// Create the 'GRANT_PERMISSIONS' hook
const grantPermissionsHookId = await learnCard.invoke.createClaimHook(grantPermissionsHookDefinition);
console.log('Created GRANT_PERMISSIONS Claim Hook ID:', grantPermissionsHookId);
// Create the 'ADD_ADMIN' hook
const addAdminHookId = await learnCard.invoke.createClaimHook(addAdminHookDefinition);
console.log('Created ADD_ADMIN Claim Hook ID:', addAdminHookId);
// Get all claim hooks for a specific boost URI with pagination
const allHooksForBoost = await learnCard.invoke.getClaimHooksForBoost({
uri: boostUriToQueryHooksFor,
...paginationOptions
});
console.log(`All Claim Hooks for ${boostUriToQueryHooksFor}:`, allHooksForBoost.records);
// Get claim hooks for a specific boost URI with a query and pagination
const filteredHooksForBoost = await learnCard.invoke.getClaimHooksForBoost({
uri: boostUriToQueryHooksFor,
query: specificClaimHookQuery,
...paginationOptions,
});
console.log(`Filtered Claim Hooks for ${boostUriToQueryHooksFor}:`, filteredHooksForBoost.records);
// Delete a claim hook (e.g., the first one we created)
// const deleteGrantPermissionsHookSuccess = await learnCard.invoke.deleteClaimHook(grantPermissionsHookId);
// console.log('Deleted GRANT_PERMISSIONS Claim Hook:', deleteGrantPermissionsHookSuccess);
} catch (error) {
console.error('Error managing Claim Hooks:', error);
}
Authorization Grants & API Tokens
Examples for managing authorization grants and generating API tokens.
const authGrantPartial = { name: "External Service XYZ", description: "Auth Grant for XYZ Service", scope: 'read:profile write:boosts', expiresAt: new Date(Date.now() + 3600000).toISOString() };
const authGrantIdToManage = 'grant-id-xyz'; // Example ID of an existing grant
try {
const newAuthGrantId = await learnCard.invoke.addAuthGrant(authGrantPartial);
console.log('Added Auth Grant ID:', newAuthGrantId);
const specificAuthGrant = await learnCard.invoke.getAuthGrant(newAuthGrantId);
console.log('Specific Auth Grant:', specificAuthGrant);
const allAuthGrants = await learnCard.invoke.getAuthGrants({ limit: 10 });
console.log('All Auth Grants:', allAuthGrants);
const updatesToAuthGrant = { description: 'Updated grant for special access' };
const updateGrantSuccess = await learnCard.invoke.updateAuthGrant(newAuthGrantId, updatesToAuthGrant);
console.log('Updated Auth Grant:', updateGrantSuccess);
const apiToken = await learnCard.invoke.getAPITokenForAuthGrant(newAuthGrantId);
console.log('API Token for Auth Grant:', apiToken);
// const revokeGrantSuccess = await learnCard.invoke.revokeAuthGrant(newAuthGrantId);
// console.log('Revoked Auth Grant:', revokeGrantSuccess);
// const deleteGrantSuccess = await learnCard.invoke.deleteAuthGrant(newAuthGrantId);
// console.log('Deleted Auth Grant:', deleteGrantSuccess);
} catch (error) {
console.error('Error managing Auth Grants:', error);
}
Create an AuthGrant
Use the LearnCard SDK's addAuthGrant method to create a new AuthGrant:
// Step 1: Create an AuthGrant with specific permissions
const grantId = await learnCard.invoke.addAuthGrant({
name: "Boost Sender Auth",
description: "Permission to send boosts",
scope: 'boosts:write',
});
// Step 2: Generate an API token from the AuthGrant
const token = await learnCard.invoke.getAPITokenForAuthGrant(grantId);
// Step 3: Prepare the payload for your API request
const payload = {
boostUri: "uri-of-the-boost-to-send",
signingAuthority: "your-signing-authority"
};
// Step 4: Make an authenticated HTTP request using the token
const response = await fetch(
`https://api.learncard.network/api/boost/send/via-signing-authority/RECIPIENT_PROFILE_ID`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(payload),
}
);
// Step 5: Process the response
if (response.status === 200) {
const sentBoostUri = await response.json();
console.log(`Boost sent successfully: ${sentBoostUri}`);
} else {
console.error(`Error sending boost: ${response.status}`);
const errorDetails = await response.json();
console.error(errorDetails);
}
Retrieving AuthGrants
// Get a single AuthGrant by ID
const authGrant = await learnCard.invoke.getAuthGrant(authGrantID);
// Get multiple AuthGrants with optional filtering
const authGrants = await learnCard.invoke.getAuthGrants({
query: {
status: 'active',
name: { contains: 'API' }
}
});
// Revoke an AuthGrant to invalidate its tokens
await learnCard.invoke.revokeAuthGrant(authGrantID);
// Or delete it completely
await learnCard.invoke.deleteAuthGrant(authGrantID);
General Utilities
const someLearnCardNetworkUri = 'uri:boost:abc123'; // Example URI
try {
// Resolve any LCN URI to its underlying object (VC, VP, Contract, etc.)
const resolvedObject = await learnCard.invoke.resolveFromLCN(someLearnCardNetworkUri);
console.log(`Resolved object for ${someLearnCardNetworkUri}:`, resolvedObject);
// Get the underlying LCN Client instance (if advanced usage is needed)
const lcnClient = await learnCard.invoke.getLCNClient();
console.log('LCN Client instance retrieved.');
// Now you could potentially use methods directly on lcnClient if necessary,
// though most operations should be covered by learnCard.invoke wrappers.
} catch (error) {
console.error('Error with LCN utilities:', error);
}
For more on initialization with a VC-API, check out the .
To speed up instantiation of the wallet, you can host our wasm binary yourself.
If you're curious about what the above code is doing, read more.
First, make an unsigned . You can do this yourself if you already have that set up for your app, or, if you just need to test out working with this library, you can use the newCredential method to easily create a test VC.
What is a , and why would I use one?
Credentials can be converted back and forth to , which can be stored per holder using . simplify complex processes, such as indexing and caching, over credentials stored in many different locations, such as in IPFS, device storage, or a Decentralized Web Node.
The above example uses LearnCloud storage, but there are many ways to store and retrieve a credential! Check out the for more info and options.