Self-sovereign Identity (SSI) allows entity to get back control on identities, and on personal data to certain extent. Hyperledger Indy is one of the solutions on SSI. This year witnesses the evolvement of this project, and now we have total three projects. The one bearing the original name Hyperledger Indy is now focusing on the ledger. The new two are Hyperledger Ursa, carrying the crypto library, and Hyperledger Aries, taking care of the agent portion.
And just last week they released a free course Hyperledger Sovereign Identity Blockchain Solutions: Indy, Aries & Ursa on edX (link).
I highly recommend those interested in SSI and decentralized identity solutions take this course. It is free and full of useful information.
In the course there is a demo showing how agent Aries works and the code is here. This demo was released last year and now is placed in the Hyperledger Aries context. I revisit the material and prepare this guide on the whole setup with some observation behind. This helps us to understand more about these projects.
If you have a docker environment, get ready for a hands-on practice!
A Note on the Demo Code
This is the note directly quoted from the repository.
“This demonstration is based on some early Indy agent code that should NOT be used as the basis of new implementations or as a reference for implementing an agent. Since this demonstration was developed the Indy (and Aries) community has evolved the notion of Agents significantly and this code base has been abandoned. It is still a good demo for understanding how agents work on a superficial level — the concepts of agents connecting and exchanging credentials. However, if you are interested in building on the latest Indy/Aries code, you should look at the Aries project, the Aries Cloud Agent — Python and other interoperable components. If you are a developer (or wannabe), check out this Becoming an Indy/Aries Developer guide.”
Bear this in mind. The purpose of this article is not to show how to develop application on Indy or Aries, but rather serves as a demonstration of how things work. If the new code follows similar logic, this should be helpful when understanding their new release.
Also a note from my side. There is another demo from indy-dev, with similar but not identical setting. You will see similar actors like Alice and Faber, and similar storyline. There are difference in the backend setup. I have done an illustration in one article. It is still referenceable. As far as you do not mix the two demonstrations, you will find values after you understand both.
Hyperledger Sovereign Identity Solution Overview
In the beginning Sovrin open sourced the code used to create the Sovrin Network, which was the initial code to Hyperledger Indy. Hyperledger Indy is a distributed ledger built for decentralized identity. Since then it has been developed as a whole project, but for larger benefits, parts of the project are taken out as separate ones. Around end of 2018 the cryptography library is accepted as a separate project named Hyperledger Ursa. In March 2019 the agent portion also became another separate project named Hyperledger Aries. As a result, the original Hyperledger Indy becomes three projects. It is best described by this diagram.
The project splitting can help leveraging resources from other groups, and at the same time make certain technologies useable in other projects. For example, other projects can reuse cryptography library from Hyperledger Ursa, while Hyperledger Aries can ultimately support a variety of ledger platforms.
In this article I am still using the original scope of Hyperledger Indy, which includes both the ledger and the agent.
A typical Hyperledger Indy setup can largely be seen as two parts: the ledger and the agent. This is how the demo setup looks like after components are brought up.
The ledger nodes form a network, holding the ledger. In this demo we have four nodes. Note that Indy ledger runs RBFT as consensus algorithm, which requires (3f+1) nodes, where f is the number of faulty node without impacting the robustness of the ledger. We will not explore how the ledger nodes interact with each other in this article. You can refer this and this for more detail.
Five agents are defined, one for each actor (Alice, Bob, Faber, Acme and Thrift), playing different roles. In this demo we roughly divide them into various stages.
- All players are given a public Decentralized ID (DID). This public DID (or endpoint DID) represents the players in the ledger.
- All players are also given a Government ID, as a credential kept in all agents.
Transcript Schema and Credential Definition
- Faber College creates the Transcript Schema.
- Faber College creates the Credential Definition based on the Transcript Schema.
- A secure communication is established between Alice and Faber. This can be requested by either side and accepted by the other.
- Faber sends Alice Credential Offer
- Alice accepts with proper information
- Credential stored in Alice’s wallet
Proof Request, Generation and Validation
- A secure communication is established between Alice and Acme.
- Acme sends proof request to Alice.
- Alice responds with proof based on the credential she has.
- Acme validates the proof and accepts the proof.
In the demo and explanation we follow these four stages.
All the components shown above are implemented as Docker containers running in localhost. Two Docker images are involved.
indy-node: this Docker image is the software for ledger node. Four containers are instantiated from indy-node. Also the ledger explorer (called webserver) is also using this image. This docker file for this image is
indy-agentjs: this Docker image is the agent software. Agent specific information is given through environment variables. In this demo five agents are instantiated.This docker file for this image is
The actual containers we are interacting are instantiated with docker-compose, and the configuration file is
docker-compose.yml. Inside this file,
- webserver: the ledger explorer based on indy-node image
- node1–4: the ledger node based on indy-node image, with proper node-specific information and command
- the five agents: the agent based on indy-agentjs image, with proper agent-specific information, such as name, endpoint, etc.
There are total ten containers up and running after docker-compose.
Finally, we are using several ports for demonstration and observation.
- Port 9000: Ledger explorer
- Port 3000: Alice
- Port 3002: Faber
- Port 3003: Acme
Obtain the repository.
git clone https://github.com/cloudcompass/ToIPLabs
Build Docker images. The build is done only once. After the images are built, they are in your localhost and can be used in upcoming demonstration.
This script equivalently perform a docker build on the two images.
Bring up the containers
This script performs docker-compose up based on the
docker-compose.yml file. Also this terminal continues showing the log of these containers. We will capture some behaviour in this terminal.
To check if all containers are up and running, open another terminal and perform
Now you should see the ten containers up and running, as shown before.
For demo purpose, open three browser tabs for Alice, Faber and Acme.
Observing these agents page, we see some commonality.
Each agent has an “Endpoint DID”, which is shown at the bottom of the page. This is the DID representing the actor, and written into the ledger.
On each agent there are several items: Relationships, Credentials, Proof Requests, Issuing and Messages. We will use them during the demo.
Each agent has a Government ID, which is a credential showing one’s identity, inside the Credentials tag.
These credentials are created by the demo. When we later inspect the ledger, we will see where these credentials come from.
Transcript Schema and Credential Definition
In this demo it is Faber College creates both schema and credential definition. Go to Faber’s agent and Issuing tag. We see three boxes here.
- Create Schema: defining attributes for a transcript. Attributes here defined is name, degree, status, year, average and ssn.
- Create Credential Definition: after schema is created, Faber can create a Credential Definition based on that schema.
- Send Credential Offer: send the credential offer to an individual already in relationship.
In this part we perform item 1 and 2. Item 3 is performed after a relationship of Alice and Faber is established.
Note that in this demo item 1 and 2 are created by Faber. In reality they are more likely created by different parties. For example, it is the Education Bureau creates the schema as a template that all colleges follow. Then individual college creates the credential definition using this schema. Nevertheless, let’s assume both are created by Faber in this demonstration.
Press Submit in Create Schema.
Then Submit in Create Credential Definition. It takes some time for cryptography process.
With both Schema and Credential Definition complete, Faber is ready for issuing credentials.
Now Alice is establishing a relationship to Faber. By relationship it is a secure communication channel between these two agents.
First, go to Alice’s agent, choose Relationships, and click Send New Connection Request. Here we specify Faber’s Endpoint DID as the counterpart (copied from Faber’s Agent page).
Press Send Connection Request. Then go to Faber’s Agent page, choose Messages,
The demo code requires an attribute name from Faber. Faber accepts it by pressing Accept.
If we go back to Alice’s Agent, choose Messages, we see similar request from Faber. Press Accept on Alice’s Agent as well.
After both requests are accepted, we can see a relationship is established between them. Go to both Alice’s and Faber’s Agent, choose Relationships, we will see Faber in Alice’s Agent, and Alice in Faber’s Agent.
Now we have some observation. First on each relationship, “Their Public Did” is the counterpart’s DID we capture before. We also see something new called Relationship DIDs. This is a pair of DID used only in this relationship (between these two agents) and different from their public DIDs.
With this relationship established, we go back to Faber’s Agent, choose Issuing, to send Credential Offer to Alice.
By pressing Submit, this Credential Offer is sent to Alice.
Go to Alice’s Agent, choose Messages. We see a new message.
Press Accept. Now we will see a new credential Transcript 1.3 created in Alice’s Agent. And inside this credential we see the detail of the transcript issued by Faber. This information to certain extent is private information, and we will see they are not stored in the ledger later.
Proof Request, Generation and Validation
In the proof stage, a relationship is first established between Alice and Acme. Without going detail, here are the steps
- Go to Alice’s Agent Relationships and click Send New Connection Request.
- Enter Acme’s Endpoint DID.
- Go to Acme’s Agent Messages, accept it.
- Go to Alice’s Agent Messages, accept it.
Now we see another relationship is established between Alice and Acme. It looks similar to that between Alice and Faber, but a new pair of Relationship DIDs are generated, purely for Alice and Acme.
Then go to Acme’s Agent Proof Requests.
Acme’s Agent is creating a Proof Request to Alice. The content was kept in Faber (a bit confusing but to certain extent it makes sense). Anyway, I copy the Proof Request here.
Without knowing the detail of format, we roughly know that this Proof Request requires proof on attributes degree, status, and year.
In Acme’s Agent Proof Requests, choose Other in Select a Proof Request, and paste the Proof Request above, and press Submit.
Now in Alice’s Agent Messages, we see another message, requesting proof on the attributes from Acme.
Press Accept. The demo code will prepare the proof automatically and send back to Acme. The agent will handle all the required cryptography for proving.
Now in Acme’s Agent Relationships, Alice has another Proof coming. By pressing Validate, the Transcript-Data is validated.
This ends the whole demo process.
What Happened Behind?
Besides what we see in the agent pages, we can combine two sources to find out what happened during the demo. One source is the transaction records in the ledger. We observe it using the ledger explorer. Another source is the logs terminal, which gives us much insight outside the ledger. They help us to paint the a more complete picture.
Use http://localhost:9000 to access the Explorer. The information we are interested is in Domain inside Ledger State. It is a JSON holding all transactions recorded in the ledger from the beginning. With the help of Indy’s documentation we can understand the transaction better.
After the demo completes there are total 30 transactions in the ledger. Interesting enough, the first 26 transactions are done in this preparation stage. We are not going to show them one-by-one. Instead we group them to make it more meaningful and a better picture on the backend. The description here is in sequence
- (1 transaction): Add a DID for Trustee
- (4 transactions): Add four DIDs for four Stewards by the Trustee
- (5 transactions): One of the Stewards adds five DIDs, corresponding to the five actors in our demo
- (5 transactions): Each actor adds an “endpoint’ attribute, corresponding to the IP:port of the agent container.
- (1 transaction): This Steward creates a Schema called Government-ID version 1.1. The attributes inside are email, tax_id and name.
- (5 transactions): This Steward then creates Credential Definitions based on the Schema. The five transactions corresponding to each actor. The actual credential are communicated outside the ledger. From the logs we see how the actor received the credential. For example,
7. (5 transactions): We see again a transaction for each actor to set attribute, similar to Step 4.
Now we know what happens during the preparation. Hyperledger Indy has a trust model built-in. The roles we see here are Trustee and Steward. Though we do not go into detail, we can tell trustee is the ultimate root of trust, and stewards are created by trustee. Then the steward is responsible for adding DID for actors. Each DID comes with corresponding verification key (verkey) such that anyone can refer the verkey using one’s DID in the ledger. Also for demo purpose a credential Government ID is issued to every actor, though it is not a must in other deployment.
And we also know the Public DID (or Endpoint DID) for each actor. They are added by Steward, and we see this Public DID in their corresponding agent. This is the transaction which created Alice’s DID (line 274 the DID of Alice) by a Steward (line 280).
Transcript Schema and Credential Definition
When Faber creates Transcript Schema and Credential Definition, they are recorded in the ledger. It is desired as everyone can retrieve the same set of Schema and Credential Definition from the ledger.
Transaction (#27) for Schema: We see this is a Schema (line 983, type 101), sent by Faber (line 979) and signed by Faber (line 956). Schema content from line 965–972.
Transaction (#28) for Credential Definition: We see this is a Credential Definition (line 1038, type 102), sent by Faber (line 1034) and signed by Faber (line 1003). We also see in Credential Definition certain cryptography material (line 1012–1026) is generated. This matches the signature type selected (line 1029, CL)
When Alice establishes a relationship with Faber, Alice creates a new DID (pairwise DID) and records this into the ledger. We see in transaction #29 this new DID. We can cross check the new DID (line 1065) matches the one in the relationship with Faber.
At the same time, there are messages between Alice and Faber. This is done outside the ledger, just between Alice and Faber. From the log we understand that,
- Faber posts a send_credential_offer to Alice
- Alice posts a accept_offer to Faber
- Faber sends credential to Alice
Inside these communications there are personal information of Alice. First this information is not written in the ledger, and therefore no one can retrieve them from the ledger. Secondly, the communication between Alice and Faber are secured by encryption and therefore only these two actors know this information.
Proof Request, Generation and Validation
We see similarly a new transaction (#30) when Alice is establishing a connection to Acme. This is the pairwise DID created by Alice and written in the ledger.
The proof request, generation and validation again happens outside the ledger. Interestingly we only see some communication between Alice and Acme, with not much information shown. It is obvious all computation happens inside the client, and the cryptography ensures the whole proof process!
This demo provides a user frontend experience on agent, and we see how agents can build relationship, issue credentials, construct and send proof request, submit proof, etc. We also take a look on both the ledger and the logs in the agent containers. Ledger provides a record of transactions and we see what types of activity are recorded in ledger, and what are not. Certain portion of credential and proof operation is in fact performed outside the ledger, through a secure communication channel between agents. This gives us a bigger and more complete picture on Hyperledger Indy.
Hope this article, together with the one before working through the indy-dev, has given you a better idea on how Hyperledger Indy implements this sovereign identity solution. And I am looking forward to the upcoming Hyperledger Aries code, and wish this Hyperledger Sovereign Identity Solution a big success in coming years.