What are tags?
Version Control Systems (VCS) provide the functionality to tag specific points in a repository’s history as milestones which can be used for a marked version release (i.e. v1.0.1). A tag can be considered as a branch which should not change after being created and mapped to a commit.
Tags point to commits
Why make tags immutable?
Original comic: https://xkcd.com/2347/
The above comic accurately depicts our modern digital infrastructure where dependencies are stacked upon each other like these blocks, each block supporting the weight of all the blocks above it.
Tags allow software developers to determine which versions of code they can reliably use as dependencies in their projects. Many automation systems rely on tags to build and install dependencies.
Therefore, mutating these tags by changing the commits that they point to can cause:
- build failures
- incompatibilites
- bugs
- security vulnerabilities
in the project and other projects that depend on it.
Mutating a tag can cause issues
Our solution
Our solution uses the tamper-evident and tamper-resistant nature of
blockchain
to guarantee tag immutability and provide assurance to users that their products will continue functioning with the same level of reliability.

v1.0.0
which points to commit b06cdb0
, then it should not be possible to delete or modify it by pointing it to a different commit 49a68cd
. This way, users who were depending on the changes that were made until commit b06cdb0
will not be pointed to the potentially buggy and incompatible commit 49a68cd
.
Our solution leverages blockchain and
smart contracts
to:
- Create tags immutably
- Retrieve created tags
We have exposed RESTful APIs so that third-party applications can easily use our solution to provide tag immutability.
Since most VCS support the concept of tags, they can make use of our tool-agnostic solution to support tag immutability in their products. Source-code repository hosting services such as
GitHub
,
GitLab
,
Bitbucket
, and
SourceForge
can also provide their users with tag immutability and since these host most of the world’s open-source projects, the benefit can be extended to millions of developers and end-users worldwide.
Our solution
Scope of this project
While our solution is conceptually VCS-agnostic, for the purposes of this project, we have created an immutable tagging system compatible with GitHub, since it is the most popular code hosting platform with easily accessible APIs.
Application Architecture
Our application is made up of three fundamental components:
- Ethereum Smart Contracts written in Solidity
- A Node.js Middleware
- Front-end Web Application written using ReactJS
In the following sections, we will go through each of these components in detail.
Smart Contract
We have used
Ethereum smart contracts
because of their ease of use and widely available support. Using
Solidity
, a programming language used to code smart contracts in Etheruem, we have created a
struct
to store all details about tags:
contract ImmutableTag {
struct Tag {
string repoURL;
string tagID;
string commitHash;
}
...
}
We then have a mapping
(equivalent to a dictionary or hashtable in other programming languages) of string
to Tag
objects inside the smart contract:
mapping(string => Tag) private tags;
By using a mapping
instead of a array, we can look up tags in O(1)
time. The key for this mapping is a concatenation of the repository URL and the tag.
For example, if we want to create a tag v1.0.0
in the repository https://github.com/Immutable-Tag/SmartContacts
, then the key in the mapping
will be "https://github.com/Immutable-Tag/SmartContacts_v1.0.0"
and the value will be the following Tag
object:
{
repoURL: "https://github.com/Immutable-Tag/SmartContacts"
tagID: "v1.0.0"
commitHash: "66190b9fc987cb12c3a302c84123122e68ef6450"
}
We have added three functions to support our product functionality:
- createTag - This function creates tags to be stored in the Ethereum Blockchain by adding it to the mapping
tags
. The function accepts the repository URL, tag, and the commit to be associated with the tag as parameters.
function createTag(string memory _repoURL, string memory _tagID, string memory _commitHash) public {
string memory key;
key = string(abi.encodePacked(string(abi.encodePacked(_repoURL, "_")), _tagID));
tags[key] = Tag(_repoURL, _tagID, _commitHash);
}
- getTag - This function takes a repository URL and tag ID and looks up in the
mapping
to retrieve the tag’s details.
function getTag(string memory _repoURL, string memory _tagID) public view returns (Tag memory) {
string memory key;
key = string(abi.encodePacked(string(abi.encodePacked(_repoURL, "_")), _tagID));
return tags[key];
}
- checkTag - This function checks if a tag already exsits on the blockchain or not. If the tag exists in the
mapping
then this function returnstrue
, otherwise it returnsfalse
.
function checkTag(string memory _repoURL, string memory _tagID) public view returns (bool) {
string memory key;
key = string(abi.encodePacked(string(abi.encodePacked(_repoURL, "_")), _tagID));
return bytes(tags[key].commitHash).length != 0;
}
We are using the
Truffle Suite
for developing our solution as it provides a development environment, testing framework and asset pipeline for blockchains using the Ethereum Virtual Machine (EVM). It also provides features like built-in Smart Contract compilation, linking, deployment and binary management and automated contract testing. We used
Ganache
to simulate an Ethereum blockchain and deployed our smart contract to it.
After writing our smart contract, we deploy it to Ganache, our local blockchain, by running the command truffle migrate
. This compiles our Solidity smart contract code to JavaScript and deploys it to Ganache. The output of this deployment looks like this:
...
2_tag_contracts.js
==================
Replacing 'ImmutableTag'
------------------------
> transaction hash: 0x0e1ef624894d2a75d2356deac0daf3a077195eff60d1e96452662ce0804c3c5e
> Blocks: 0 Seconds: 0
> contract address: 0x5A58B7cAf4609a1c9f7934A0bDBcbeF3B4d27bb8
> block number: 3
> block timestamp: 1637725439
> account: 0x851F4BE5447fbC3e40b50A650584551434D9579F
> balance: 9999.97973206
> gas used: 779116 (0xbe36c)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.01558232 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.01558232 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0.01942118 ETH
We use the contract address of the ImmutableTag
smart contract (0x5A58B7cAf4609a1c9f7934A0bDBcbeF3B4d27bb8
in this case) to invoke it from the middleware. We provide more details about this in the next section.
You can find the entire code and documentation for our smart contract in
this GitHub repository
.
Middleware
Our middleware layer is a RESTful API server built using Node.js. The APIs provided by the server are consumed by our React front-end.
The server also calls GitHub APIs to verify the existence of a commit to which a tag will point, and optionally, to create tags in the GitHub repository using GitHub personal access tokens.
Connecting Node.js server to the blockchain
In the previous section, we described how we deploy our smart contract to Ganache and get a contract address once the deployment succeeds. We use this address to connect the middleware to the smart contract using
web3.js
and invoke smart contract functions for tag creation and tag retrieval.
Creating a Tag
This API creates a new tag on the blockchain. It first checks if the tag already exists in the blockchain by invoking the checkTag
smart contract function. If the tag does not exist, it makes a request to GitHub to verify the existence of the commit that is being mapped to the tag. If the commit is valid, it proceeds to create the tag in the blockchain by invoking the createTag
smart contract function. (Please see the previous section on Smart contracts to understand more.)
If a GitHub personal access token is passed to it, then the API also creates the tag in the GitHub repository.
API endpoint
POST /v1/tags
Parameters
Name | Type | In | Description |
---|---|---|---|
Content-Type | string | header | application/json |
repo_url | string | body | repository URL |
tag_id | string | body | tag name |
commit_id | string | body | commit hash |
Example
curl -i -X POST "http://localhost:5000/v1/tags" \
-H 'Content-Type: application/json' \
-d '{"repo_url": "https://github.com/Immutable-Tag/SmartContacts",\
"tag_id": "v1.0.0",\
"commit_id": "66190b9fc987cb12c3a302c84123122e68ef6450"}'
Response:
HTTP/1.1 201 Created
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 39
ETag: W/"27-p5b2vwAQyjd8Enu5ruBWmlgkkzw"
Date: Wed, 24 Nov 2021 04:58:54 GMT
Connection: keep-alive
Keep-Alive: timeout=5
{"message":"Successfully created tag."}
If we try to map an existing tag to a different commit, the response looks like:
HTTP/1.1 400 Bad Request
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 30
ETag: W/"1e-KM94dQmY+pgv0Ecd8L77N5qIs0c"
Date: Wed, 24 Nov 2021 04:58:27 GMT
Connection: keep-alive
Keep-Alive: timeout=5
{"error":"Tag already exists"}
If the commit doesn’t exist, the response looks like:
HTTP/1.1 400 Bad Request
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 30
ETag: W/"1e-KM94dQmY+pgv0Ecd8L77N5qIs0c"
Date: Wed, 24 Nov 2021 04:58:27 GMT
Connection: keep-alive
Keep-Alive: timeout=5
{"error":"Commit does not exist"}
Retrieving a Tag
This API retrieves a tag and its related information from the blockchain. For this, we invoke the getTag
smart contract function which returns the Tag
object if it exists in the mapping
. (Please see the previous section on Smart contracts to understand more.)
API endpoint
POST /v1/tags/{tag_id}
Parameters
Name | Type | In | Description |
---|---|---|---|
Content-Type | string | header | application/json |
tag_id | string | path | tag name |
repo_url | string | body | repository URL |
Example
curl -i -X POST "http://localhost:5000/v1/tags/v1.0.0" \
-H 'Content-Type: application/json' \
-d '{"repo_url": "https://github.com/Immutable-Tag/SmartContacts"}'
Response:
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 134
ETag: W/"86-aGtC1lmpzo6bWnvAnDjnyX41gHg"
Date: Wed, 24 Nov 2021 04:57:49 GMT
Connection: keep-alive
Keep-Alive: timeout=5
{"repo_url":"https://github.com/Immutable-Tag/SmartContacts","tag_id":"v1.0.0","commit_id":"66190b9fc987cb12c3a302c84123122e68ef6450"}
If we try to get a non-existent tag, the response looks like:
HTTP/1.1 404 Not Found
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 30
ETag: W/"1e-Tox4M2y0N7WkRGDAFErMm9hsPbA"
Date: Wed, 24 Nov 2021 04:57:36 GMT
Connection: keep-alive
Keep-Alive: timeout=5
{"error":"Tag does not exist"}
You can find the entire code and documentation for our middleware in
this GitHub repository
.
The React App
We have created a frontend using
ReactJS
to showcase how to:
- Create a tag immutably
- Retrieve created tag
Home Page
Creating a Tag
Upon navigating to the create tag page, we can see a form that requires the following fields:
- The Repository URL for which we wish to create a tag
- The tag name
- The commit which we wish to map the tag to
Optionally, the form also accepts a GitHub token which includes permissions for creating tags in the GitHub repository as well as the blockchain.
Creating a Tag
A tag is not created in the following scenarios:
- Tag already exists - To guarantee immutability, a tag once created cannot be modified. Therefore, trying to map a pre-exisiting tag to a different commit gives an error message
Tag already exists
.
Tag already exists
- Commit does not exist - Trying to create a tag with an invalid commit, i.e. a commit which does not exist in the provided Repository URL gives an error message
Commit does not exist
Commit does not exist
Retrieving a tag
Once a tag is created in the blockchain, it can be retrieved by providing:
- The Repository URL for which we created the tag
- The tag name
Retrieving a tag
Tag retrieval will be unsuccessful if we pass a tag name that does not exist in the corresponding repository URL.
Failed to retrieve tag
You can find the entire code and documentation for our React application in
this GitHub repository
.
Application Workflow
The sequence diagrams below show the application workflow for creating and retrieving tags:
Demo
Running the application locally
Please refer to
this link
for steps on how to run the application locally. In case you encounter any issues, please create a GitHub issue
here
.
Using the application
Once you the application running, the following video shows how you can use the application to create tags immutably and retrieve them.
Demo video
Future Work
Our team decided to test the idea of tag immutability using Ethereum, which allowed us to quickly build a Proof-of-Concept using smart contracts written in Solidity.
Compatibility with other blockchains
Our next step is to allow our solution to easily plug in any blockchain that supports smart contracts. We would also like to use permissioned blockchains such as
ResilientDB
in order to achieve a higher throughput.
GitHub OAuth App
Currently our application optionally reads a GitHub access token from the UI and creates tags in the GitHub repositories. We would like to instead create a
GitHub OAuth App
that users can give permissions for creating tags in the repositories they have access to.
Compatibility with other VCS
Our current scope is limited to working with git tags for repositories hosted on
GitHub
. In the future, we would like our APIs to be easily consumed by not only other git hosting providers such as
GitLab
and
Bitbucket
, but also hosting providers for
Subversion
and
Mercurial
VCS.
Automation with existing CI/CD workflows
While our project has a web front-end which makes it easy for our users to use our application, we can also provide Command Line Interface (CLI) tools for interacting with our API. The primary benefit of this would be easy integration into automated CI/CD workflows of engineering teams.
API Authentication
In the future we will add include authentication for our API using API Keys and also add rate limiting to prevent security attacks.
Application outside VCS
The concept of tag immutability can be applied to not only VCS, but also other areas where tagging is used. A popular example is that of
Docker
tags, where tags point to specific image digests/hashes. As cloud computing continues to be more popular, this will be extremely helpful for
Docker Hub
and cloud providers that have a container registry for storing Docker images. This includes
Amazon Web Services
,
Google Cloud Platform
,
Microsoft Azure
,
IBM Cloud
and
Oracle Cloud
.
About the team
This product was conceived and developed as part of our coursework for the Fall 2021 class
ECS 265: Distributed Database Systems
taught by
Prof. Mohammad Sadoghi
at the
University of California, Davis
. Our team consists of following members:
- Vasudha Jha ( vajha@ucdavis.edu )
- Parichaya Chatterji ( parichay@ucdavis.edu )
- Anusha Kulkarni ( ahkulkarni@ucdavis.edu )
- Preethi Muruganandam ( pmuruganandam@ucdavis.edu )
- Ashwitha Kassetty ( avkassetty@ucdavis.edu )