Walkthrough: Build a Vaccine Card¶
This walkthrough demonstrates how a vaccination card can be issued, held, and shared using Verifiable Credentials with Trinsic.
Meet Allison¶
We'll follow Allison as she obtains a vaccine certificate, stores it in her digital wallet, and presents it to board an airplane.
In most credential exchange scenarios, there are three primary roles: Issuer, Holder, and Verifier.
Holder: Stores credentials received from issuers, and presents them to verifiers. (Said credentials are often, but not always, attesting information about the holder)
Issuer: Signs and issues credentials which attest information about a credential subject.
Verifier: Verifies credentials presented by holders.
In this case, Allison will be the holder, a vaccination clinic will be the issuer, and an airline will be the verifier.
Our SDKs¶
You can follow along using one of our SDKs, or use the Trinsic CLI, which implements full platform functionality.
Click here for installation instructions for the Trinsic CLI.
Click here for installation instructions for the Node/Browser SDK.
Click here for installation instructions for the .NET SDK.
Click here for installation instructions for the Python SDK.
Click here for installation instructions for the Java SDK.
Click here for installation instructions for the Go SDK.
Ecosystem Setup¶
Before we begin, you'll need an ecosystem -- somewhere for the resources we're about to create (wallets, templates, credentials) to live.
Use Existing Ecosystem¶
If you've already signed up as a customer, you'll have received an email with an ecosystem ID and authentication token.
Copy this ecosystem ID down, and skip to the next step.
Create New Ecosystem¶
If you don't already have an ecosystem provisioned for you, you'll need to create one first.
This will be a sandbox ecosystem; suitable for prototyping and testing, but not production purposes. To receive a production ecosystem, sign up.
trinsic provider create-ecosystem
const ecosystem = await trinsic
.provider()
.createEcosystem(CreateEcosystemRequest.fromPartial({}));
const ecosystemId = ecosystem.ecosystem!.id;
var trinsic = new TrinsicService(_options);
var (ecosystem, _) = await trinsic.Provider.CreateEcosystemAsync(new());
var ecosystemId = ecosystem?.Id;
ecosystem = await trinsic_service.provider.create_ecosystem()
ecosystem_id = ecosystem.ecosystem.id
var ecosystemResponse =
trinsic.provider().createEcosystem(CreateEcosystemRequest.getDefaultInstance()).get();
var ecosystemId = ecosystemResponse.getEcosystem().getId();
ecosystem, _ := trinsic.Provider().CreateEcosystem(context.Background(), nil)
ecosystemId := ecosystem.Ecosystem.Id
The response to this call contains the name and ID of your newly-created ecosystem; copy either of these down.
Further Reading: Ecosystems
- Learn more about Ecosystems
- Browse the Provider API reference
Create Accounts¶
We need to create Trinsic accounts for the participants in this credential exchange. Accounts and wallets can be considered interchangeably; all accounts have exactly one associated wallet.
Accounts can be created with a single call; they're designed to minimize onboarding friction for your users.
The clinic's account will issue the credential, Allison's account will hold it, and the airline's account will verify its contents.
The CLI makes it easy to create wallets. For demo purposes, we'll create all three on the same machine.
When using the CLI, the authentication token of the most recently used account is saved in ~/.trinsic
. In a real-world scenario, you should back this token up securely.
trinsic account login --ecosystem {ECOSYSTEM_ID}
# Save auth token in `allison.txt` before continuing
trinsic account login --ecosystem {ECOSYSTEM_ID}
# Save auth token in `airline.txt` before continuing
trinsic account login --ecosystem {ECOSYSTEM_ID}
# Save auth token in `clinic.txt` before continuing
// Create 3 different profiles for each participant in the scenario
const allison = await trinsic
.wallet()
.createWallet({ ecosystemId: ecosystemId });
const clinic = await trinsic
.wallet()
.createWallet({ ecosystemId: ecosystemId });
const airline = await trinsic
.wallet()
.createWallet({ ecosystemId: ecosystemId });
If you would like to save the account for future use, simply write the auth token to storage. Take care to store it in a secure location.
var allison = await trinsic.Wallet.CreateWalletAsync(new() { EcosystemId = ecosystemId! });
var clinic = await trinsic.Wallet.CreateWalletAsync(new() { EcosystemId = ecosystemId! });
var airline = await trinsic.Wallet.CreateWalletAsync(new() { EcosystemId = ecosystemId! });
If you would like to save an account for future use, simply write the auth token to storage. Take care to store it in a secure location.
# Create an account for each participant in the scenario
allison = await trinsic_service.wallet.create_wallet(
request=CreateWalletRequest(ecosystem_id=ecosystem_id)
)
airline = await trinsic_service.wallet.create_wallet(
request=CreateWalletRequest(ecosystem_id=ecosystem_id)
)
clinic = await trinsic_service.wallet.create_wallet(
request=CreateWalletRequest(ecosystem_id=ecosystem_id)
)
If you would like to save an account for future use, simply write the auth token to storage. Take care to store it in a secure location.
// Create an account for each participant in the scenario
var allison =
trinsic
.wallet()
.createWallet(CreateWalletRequest.newBuilder().setEcosystemId(ecosystemId).build())
.get();
var clinic =
trinsic
.wallet()
.createWallet(CreateWalletRequest.newBuilder().setEcosystemId(ecosystemId).build())
.get();
var airline =
trinsic
.wallet()
.createWallet(CreateWalletRequest.newBuilder().setEcosystemId(ecosystemId).build())
.get();
If you would like to save an account for future use, simply write the auth token to storage. Take care to store it in a secure location.
// Create an account for each participant in the scenario
createWallet := &wallet.CreateWalletRequest{EcosystemId: ecosystemId}
allison, _ := trinsic.Wallet().CreateWallet(context.Background(), createWallet)
airline, _ := trinsic.Wallet().CreateWallet(context.Background(), createWallet)
clinic, _ := trinsic.Wallet().CreateWallet(context.Background(), createWallet)
If you would like to save an account for future use, simply write the auth token to storage. Take care to store it in a secure location.
Production Usage
In this example, we've created anonymous accounts; the only way to access them is by saving the authentication token generated on account creation.
In a production scenario, you may want to create accounts tied to a user's email address or phone number. This allows users to securely access their Trinsic cloud wallets at any time.
Note that accounts are tied to their ecosystem. If you create an account tied to [email protected]
in the example1
ecosystem, it will not be visible in any other ecosystem. The same email address can be used to create accounts in multiple ecosystems.
Further Reading: Accounts and Wallets
- Learn more about Wallets
- Browse the Account API reference
- Read about authentication tokens and security
Define a Template¶
Before we can issue a credential, we need to create a Template for it.
Templates are simply a list of the fields that a credential can have.
First, prepare a JSON file which describes your template:
{
"firstName": {
"type": "string",
"description": "First name of vaccine recipient"
},
"lastName": {
"type": "string",
"description": "Last name of vaccine recipient"
},
"batchNumber":{
"type": "string",
"description": "Batch number of vaccine"
},
"countryOfVaccination":{
"type": "string",
"description": "Country in which the subject was vaccinated"
}
}
Then create the template:
trinsic template create -n "VaccinationCertificate" --fields-file templateData.json
The output of this command will include a template ID; copy this down for later use.
//Define all fields
const firstNameField = TemplateField.fromPartial({
description: "First name of vaccine recipient",
type: FieldType.STRING,
});
const lastNameField = TemplateField.fromPartial({
type: FieldType.STRING,
description: "Last name of vaccine recipient",
});
const batchNumberField = TemplateField.fromPartial({
type: FieldType.STRING,
description: "Batch number of vaccine",
});
const countryOfVaccinationField = TemplateField.fromPartial({
type: FieldType.STRING,
description: "Country in which the subject was vaccinated",
});
//Create request
let request = CreateCredentialTemplateRequest.fromPartial({
name: `VaccinationCertificate-${uuid()}`,
fields: {
firstName: firstNameField,
lastName: lastNameField,
batchNumber: batchNumberField,
countryOfVaccination: countryOfVaccinationField,
},
});
//Create template
const response = await trinsicService.template().create(request);
const template = response.data;
// Set active profile to `clinic` so we can create a template
trinsic = new TrinsicService(_options.CloneWithAuthToken(clinic.AuthToken!));
// Prepare request to create template
CreateCredentialTemplateRequest templateRequest = new() {
Name = "VaccinationCertificate",
AllowAdditionalFields = false
};
templateRequest.Fields.Add("firstName", new() { Description = "First name of vaccine recipient" });
templateRequest.Fields.Add("lastName", new() { Description = "Last name of vaccine recipient" });
templateRequest.Fields.Add("batchNumber", new() { Description = "Batch number of vaccine", Type = FieldType.String });
templateRequest.Fields.Add("countryOfVaccination", new() { Description = "Country in which the subject was vaccinated" });
// Create template
var template = await trinsic.Template.CreateAsync(templateRequest);
var templateId = template?.Data?.Id;
template = await trinsic_service.template.create(
request=CreateCredentialTemplateRequest(
name=f"VaccinationCertificate-{uuid.uuid4()}",
allow_additional_fields=False,
fields={
"firstName": TemplateField(
description="First name of vaccine recipient"
),
"lastName": TemplateField(description="Last name of vaccine recipient"),
"batchNumber": TemplateField(
description="Batch number of vaccine", type=FieldType.STRING
),
"countryOfVaccination": TemplateField(
description="Country in which the subject was vaccinated"
),
},
)
)
template_id = template.data.id
// Set active profile to 'clinic'
templateService.setAuthToken(clinic);
// Define fields for template
var fields = new HashMap<String, TemplateField>();
fields.put(
"firstName",
TemplateField.newBuilder().setDescription("First name of vaccine recipient").build());
fields.put(
"lastName",
TemplateField.newBuilder().setDescription("Last name of vaccine recipient").build());
fields.put(
"batchNumber",
TemplateField.newBuilder()
.setType(FieldType.STRING)
.setDescription("Batch number of vaccine")
.build());
fields.put(
"countryOfVaccination",
TemplateField.newBuilder()
.setDescription("Country in which the subject was vaccinated")
.build());
// Create template request
var templateRequest =
CreateCredentialTemplateRequest.newBuilder()
.setName("VaccinationCertificate")
.setAllowAdditionalFields(false)
.putAllFields(fields)
.build();
// Execute template creation
var template = templateService.create(templateRequest).get();
var templateId = template.getData().getId();
templateRequest := &template.CreateCredentialTemplateRequest{Name: "VaccinationCertificate", AllowAdditionalFields: false, Fields: make(map[string]*template.TemplateField)}
templateRequest.Fields["firstName"] = &template.TemplateField{Description: "First name of vaccine recipient"}
templateRequest.Fields["lastName"] = &template.TemplateField{Description: "Last name of vaccine recipient"}
templateRequest.Fields["batchNumber"] = &template.TemplateField{Description: "Batch number of vaccine", Type: template.FieldType_STRING}
templateRequest.Fields["countryOfVaccination"] = &template.TemplateField{Description: "Country in which the subject was vaccinated"}
createdTemplate, _ := trinsic.Template().Create(context.Background(), templateRequest)
templateId := createdTemplate.Data.Id
Further Reading: Templates
- Learn more about Templates
- Browse the Template API reference
Issue a Credential¶
Upon receiving her vaccine, the clinic issues Allison a Verifiable Credential, which proves that she was given the vaccine by the clinic.
A credential is a JSON document that has been cryptographically signed; this signature enables verifiers to trust that the data comes a trusted source, and has not been tampered with.
To issue a vaccine certificate, we'll use the template we created in the last step.
First, prepare a file named values.json
with the following content:
{
"firstName": "Allison",
"lastName": "Allisonne",
"batchNumber": "123454321",
"countryOfVaccination": "US"
}
Then issue the credential:
trinsic config --auth-token $(cat clinic.txt)
trinsic vc issue-from-template --template-id {TEMPLATE_ID} --values-file values.json --out credential.json
The output of this command will contain a signed JSON document, which has been saved to credential.json
.
Note that TEMPLATE_ID refers to the "Schema" URI of the template you created earlier called "VaccinationCertificate".
More specifically, it's the property 'schema_uri' in the JSON returned by the trinsic template create...
command.
// Prepare the credential values JSON document
const credentialValues = JSON.stringify({
firstName: "Allison",
lastName: "Allisonne",
batchNumber: "123454321",
countryOfVaccination: "US",
});
// Sign a credential as the clinic and send it to Allison
trinsic.options.authToken = clinic.authToken;
const issueResponse = await trinsic.credential().issueFromTemplate(
IssueFromTemplateRequest.fromPartial({
templateId: template.id,
valuesJson: credentialValues,
})
);
// Prepare credential values
var credentialValues = new Dictionary<string, string> {
{ "firstName", "Allison" },
{ "lastName", "Allisonne" },
{ "batchNumber", "123454321" },
{ "countryOfVaccination", "US" }
};
// Issue credential as clinic
var issueResponse = await trinsic.Credential.IssueFromTemplateAsync(new() {
TemplateId = templateId,
ValuesJson = JsonSerializer.Serialize(credentialValues)
});
var signedCredential = issueResponse?.DocumentJson;
# Prepare values for credential
values = json.dumps(
{
"firstName": "Allison",
"lastName": "Allisonne",
"batchNumber": "123454321",
"countryOfVaccination": "US",
}
)
# Issue credential
issue_response = await trinsic_service.credential.issue_from_template(
request=IssueFromTemplateRequest(template_id=template.id, values_json=values)
)
credential = issue_response.document_json
// Set active profile to 'clinic' so we can issue credential signed
// with the clinic's signing keys
trinsicService.setAuthToken(clinic);
// Prepare credential values
var valuesMap = new HashMap<String, Object>();
valuesMap.put("firstName", "Allison");
valuesMap.put("lastName", "Allissonne");
valuesMap.put("batchNumber", "123454321");
valuesMap.put("countryOfVaccination", "US");
// Serialize values to JSON
var valuesJson = new Gson().toJson(valuesMap);
// Issue credential
var issueResponse =
trinsicService
.credential()
.issueFromTemplate(
IssueFromTemplateRequest.newBuilder()
.setTemplateId(templateId)
.setValuesJson(valuesJson)
.build())
.get();
var credential = issueResponse.getDocumentJson();
// Prepare values for credential
valuesStruct := struct {
FirstName string
LastName string
batchNumber string
countryOfVaccination string
}{
FirstName: "Allison",
LastName: "Allisonne",
batchNumber: "123454321",
countryOfVaccination: "US",
}
values, _ := json.Marshal(valuesStruct)
// Issue credential
issueResponse, _ := trinsic.Credential().IssueFromTemplate(context.Background(), &credential.IssueFromTemplateRequest{
TemplateId: createdTemplate.Id,
ValuesJson: string(values),
})
issuedCredential := issueResponse.DocumentJson
Further Reading: Issuance and Credentials
- Learn more about Verifiable Credentials
- Browse the Credential API reference
Send Credential to Allison¶
Now that the clinic has a signed credential, it must be securely transmitted to Allison, so she can store it in her wallet.
Because it's just a JSON string, it could be delivered in many ways -- for example, in the response to an HTTPS request which triggered the issuance process.
Send via Trinsic
Trinsic also offers the ability to send a credential directly to a Trinsic user's wallet.
Click here to learn more about this feature.
Store Credential in Wallet¶
Once Allison receives the credential, it must be stored in her wallet.
trinsic config --auth-token $(cat allison.txt)
trinsic wallet insert-item --item credential.json
// Alice stores the credential in her cloud wallet.
trinsic.options.authToken = allison.authToken;
const insertResponse = await trinsic.wallet().insertItem(
InsertItemRequest.fromPartial({
itemJson: issueResponse.documentJson,
})
);
// Set active profile to 'allison' so we can manage her cloud wallet
trinsic = new TrinsicService(_options.CloneWithAuthToken(allison.AuthToken!));
// Insert credential into Allison's wallet
var insertItemResponse = await trinsic.Wallet.InsertItemAsync(new() {
ItemJson = signedCredential
});
var itemId = insertItemResponse?.ItemId;
# Allison stores the credential in her cloud wallet
trinsic_service.service_options.auth_token = allison.auth_token
insert_response = await trinsic_service.wallet.insert_item(
request=InsertItemRequest(item_json=credential)
)
item_id = insert_response.item_id
// Set active profile to 'allison' so we can manage her cloud wallet
// setAuthToken() {
trinsic.setAuthToken(allison.getAuthToken());
// }
// Allison stores the credential in her cloud wallet.
var insertItemResponse =
trinsic
.wallet()
.insertItem(InsertItemRequest.newBuilder().setItemJson(credential).build())
.get();
final var itemId = insertItemResponse.getItemId();
// Allison stores the credential in her cloud wallet
trinsic.SetAuthToken(allison.AuthToken)
insertResponse, _ := trinsic.Wallet().InsertItem(context.Background(), &wallet.InsertItemRequest{ItemJson: issuedCredential})
itemId := insertResponse.ItemId
The response to this call contains an Item ID; copy this down.
Further Reading: Wallets
- Learn more about Wallets
- Browse the Wallet API reference
Create a Proof of Vaccination¶
Before boarding, the airline requests proof of vaccination from Allison. Specifically, they want to see proof that she holds a VaccinationCertificate
credential.
Let's use the CreateProof call to build a proof for Allison's held credential.
trinsic config --auth-token $(cat allison.txt)
trinsic vc create-proof --item-id "{ITEM_ID}" --out proof.json
// Allison shares the credential with the venue.
trinsic.options.authToken = allison.authToken;
const proofResponse = await trinsic.credential().createProof(
CreateProofRequest.fromPartial({
itemId: insertResponse.itemId,
})
);
// Build a proof for the signed credential as allison
var proofResponse = await trinsic.Credential.CreateProofAsync(new() {
ItemId = itemId
});
var proofJson = proofResponse?.ProofDocumentJson;
# Allison shares the credential with the airline
trinsic_service.service_options.auth_token = allison.auth_token
proof_response = await trinsic_service.credential.create_proof(
request=CreateProofRequest(item_id=item_id)
)
credential_proof = proof_response.proof_document_json
// Set active profile to 'allison' so we can create a proof using her key
trinsic.setAuthToken(allison.getAuthToken());
// Allison shares the credential with the venue
var createProofResponse =
trinsic
.credential()
.createProof(CreateProofRequest.newBuilder().setItemId(itemId).build())
.get();
var credentialProof = createProofResponse.getProofDocumentJson();
// Allison shares the credential with the airline
trinsic.SetAuthToken(allison.AuthToken)
proofResponse, _ := trinsic.Credential().CreateProof(context.Background(), &credential.CreateProofRequest{
Proof: &credential.CreateProofRequest_ItemId{ItemId: itemId},
})
credentialProof := proofResponse.ProofDocumentJson
Allison sends this proof to the airline for them to verify.
Partial Proofs
In this example, the proof is being created over the entire credential; all of its fields are revealed to the verifier.
It is possible for the airline to send Allison a frame which requests only certain fields of the credential. The airline would not be able to see other fields of the credential, but cryptographic guarantees would still hold over the revealed fields.
See the CreateProof reference for more information.
OpenID Connect for Presentation
Trinsic offers an OpenID Connect service as an alternative flow for the exchange of a credential between a holder and a verifier.
In this flow, a holder simply clicks a link (or scans a QR code), logs into their Trinsic cloud wallet, and selects a credential to share.
Verify Proof¶
Once the airline receives the proof, they can use the VerifyProof call to ensure its authenticity.
trinsic config --auth-token $(cat airline.txt)
trinsic vc verify-proof --proof-document proof.json
// The airline verifies the credential
trinsic.options.authToken = airline.authToken;
const verifyResponse = await trinsic.credential().verifyProof(
VerifyProofRequest.fromPartial({
proofDocumentJson: proofResponse.proofDocumentJson,
})
);
// Set active profile to `airline`
trinsic = new TrinsicService(_options.CloneWithAuthToken(airline.AuthToken!));
// Verify that Allison has provided a valid proof
var verifyResponse = await trinsic.Credential.VerifyProofAsync(new() {
ProofDocumentJson = proofJson
});
var credentialValid = verifyResponse?.IsValid ?? false;
# The airline verifies the credential
trinsic_service.service_options.auth_token = airline.auth_token
verify_result = await trinsic_service.credential.verify_proof(
request=VerifyProofRequest(proof_document_json=credential_proof)
)
valid = verify_result.is_valid
trinsic.setAuthToken(airline.getAuthToken());
// Verify that Allison has provided a valid proof
var verifyProofResponse =
trinsic
.credential()
.verifyProof(
VerifyProofRequest.newBuilder().setProofDocumentJson(credentialProof).build())
.get();
boolean isValid = verifyProofResponse.getIsValid();
// The airline verifies the credential
trinsic.SetAuthToken(airline.AuthToken)
verifyResult, _ := trinsic.Credential().VerifyProof(context.Background(), &credential.VerifyProofRequest{ProofDocumentJson: credentialProof})
valid := verifyResult.IsValid
Interoperability
The Verifiable Credentials and Proofs that Trinsic's platform produces are based on open standards.
Although we use the VerifyProof call in this example, the proof could be verified using any standards-compliant software.
Full Source Code¶
This sample is available as VaccineDemoShared.ts
in our SDK repository.
This sample is available as VaccineWalkthroughTests.cs
in our SDK repository.
This sample is available as vaccine_demo.py
in our SDK repository.
This sample is available as VaccineDemo.java
in our SDK repository.
This sample is available as vaccine_test.go
in our SDK repository.
Governance Setup¶
Before we begin, you'll need an trust registry -- somewhere for the governance to be defined (which issuer is allowed to issue a vaccine credential).
Create New Governance¶
You can create a governance framework through the Trinsic dashboard by clicking on the 'Governance' tab in the nav bar.
trinsic trust-registry add-framework --name "Vaccine Governance" --uri "https://vaccines.trinsic.id"
The response should look like:
{
"governing_authority":"did:key:xxxxxxxxxxx........",
"id": "xxxxxx.....", // <framework_id>
"trust_registry": "urn:egf:..."
}
AddFrameworkRequest
The response to this call contains the name and ID of your newly-created ecosystem; copy either of these down.
Register Issuer¶
First lets grab our account info. You can use an email address, walletId or PublicDID as identifiers for a wallet. We will use publicDID.
trinsic account info
The response should look like:
{
...
"ecosystem_id": "urn:trinsic:ecosystems:<ecosystem-name>",
"public_did": "did:key:xxxxxxxx......", // <public_did>
"wallet_id": "urn:trinsic:wallets:xxxxxx....>"
}
did_uri
, wallet_id
, or email
may be specified.RegisterMemberRequest
Registers an authorized issuer for a specific credential type (identified by its schema_uri
), using the public_did, framework_id and template_uri from above:
trinsic trust-registry register-member \
--framework-id <framework_id> \
--schema <template_uri> \
--did <public_did>
did_uri
, wallet_id
, or email
may be specified.RegisterMemberRequest
Check Issuer Status¶
Check the status of an issuer for a specific credential type using the public_did, framework_uri and template_uri from above:
trinsic trust-registry get-membership-status \
--framework-id <framework_id> \
--schema <template_uri> \
--did <public_did>
The response should look like:
ok
GetMembershipStatusRequest
Issue a Credential with Governance framework¶
We need to prepare a credential with a governance framework specified. This will consist of:
- Issuing the credential with framework-id specified
- Inserting the credential into your wallet
- Deriving a proof of the credential
First, prepare a file named values.json
with the following content:
{
"firstName": "Allison",
"lastName": "Allisonne",
"batchNumber": "123454321",
"countryOfVaccination": "US"
}
Then issue the credential:
trinsic vc issue-from-template --framework-id <framework_id> --template-id <template_id> --values-file values.json --out credential.json
trinsic wallet insert-item --item credential.json
The response should look like:
ok
item id → "urn:uuid:..." // <item_id>
trinsic vc create-proof --item-id <item_id> --out proof.json
Verify a proof with governance status¶
Now we can verify the proof from the previous step, and get the governance status.
trinsic vc verify-proof --proof-document proof.json
The response should look like:
is valid → "true"
validation results → {
"CredentialStatus": {
"is_valid": true,
"messages": []
},
"IssuerIsSigner": {
"is_valid": true,
"messages": []
},
"SchemaConformance": {
"is_valid": true,
"messages": []
},
"SignatureVerification": {
"is_valid": true,
"messages": []
},
"TrustRegistryMembership": {
"is_valid": true,
"messages": []
}
}
Revoke Issuer¶
Revoke the status of an issuer for a specific credential type using the public_did, framework_uri and template_uri from above:
trinsic trust-registry unregister-member \
--framework-id <framework_id> \
--schema <template_uri> \
--did <public_did>
did_uri
, wallet_id
, or email
may be specified.UnregisterMemberRequest
Verify a proof with governance status after revocation of issuer¶
Now we can verify the proof from the previous step, and get the governance status.
trinsic vc verify-proof --proof-document proof.json
The response should look like:
is valid → "false"
validation results → {
"CredentialStatus": {
"is_valid": true,
"messages": []
},
"IssuerIsSigner": {
"is_valid": true,
"messages": []
},
"SchemaConformance": {
"is_valid": true,
"messages": []
},
"SignatureVerification": {
"is_valid": true,
"messages": []
},
"TrustRegistryMembership": {
"is_valid": false,
"messages": [
"issuer is not authorized in the specified registry"
]
}
}
Further Reading: Trust Registries
- Learn more about Governance
- Browse the Provider API reference
Next Steps¶
Congratulations! If you've completed all the steps of this walkthrough, you've just created a mini ecosystem of issuers, verifiers, and holders all exchanging credentials. Depending on your goals, there are a couple of possible next steps to take.
- Try out a sample app
- Browse the Service Reference
- Learn more about the key concepts and technologies at play