Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

System Design (High-Level Design)  Module 28 – Case studies: India's digital public infrastructure

Design a document wallet like DigiLocker

Design a national document wallet: references to issuer-signed documents, encrypted uploads, consent grants that expire, and result-day peaks.

  • Intermediate
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot
Free intro, Pro: 6 of 30 minutes free
Free: One locker, many issuers, and a reference to each document. With a pass: the other 13 sections and 3 interview answers, about 24 minutes.

What you will learn

  • Design issued-document references and user uploads
  • Verify issued documents and share them with consent that expires
  • Plan for result-day and admission-season peaks
  • Estimate the references, uploads and result-day load of a national wallet

Before you start

What you’ll learn · Before you start

What you will learn

  • Design issued-document references and user uploads
  • Verify issued documents and share them with consent that expires
  • Plan for result-day and admission-season peaks
  • Estimate the references, uploads and result-day load of a national wallet

Before you start

On this page

Not affiliated

Based on public information about DigiLocker (MeitY). MySmartCoPilot is not affiliated with DigiLocker (MeitY), and DigiLocker (MeitY)’s real architecture may differ from what this lesson describes.

On the morning an exam board publishes 2.5 million results, a million students may ask a document wallet for their mark sheets within 15 minutes, from a board that answers 200 requests a second. A document wallet gives every person one place for official papers, such as mark sheets and driving licences, and its central choice is to hold references, not loose copies: for a document an issuer publishes, it stores a URI built from the issuer’s id, the document type and a document id, and fetches the issuer-signed file on demand or keeps a cached copy; for a scan someone uploads, an encrypted file in object storage.

A bank, a university or an employer gets a document only through a consent grant that names the documents, the purpose and an end time, and it checks the issuer’s signature itself instead of trusting the wallet. The hard parts are that check, consent withdrawn at any moment, and the result morning. India’s DigiLocker is the best-known wallet of this kind.

One locker, many issuers, and a reference to each document

Why the wallet keeps a reference, not a copy

The issuer stays the authority for its documents, so the wallet points at them. Walk through the life of one mark sheet and watch what the wallet holds at each step:

Timeline · 6 events in order The life of one issued mark sheet Walk through one mark sheet from the board's signature to a correction, and see what the wallet keeps at each step.
  1. Issued

    The board signs the mark sheet

    The exam board issues Ravi's mark sheet as a signed file in its own repository, under a URI made of its issuer id, the document type and a random document id.

  2. Linked

    The locker gains a reference about 500 bytes

    When Ravi searches for it, or the board pushes it, his locker gains a reference of about 500 bytes, the URI with a version and a status, not the file.

  3. Opened

    The signed file is fetched

    Opening the reference fetches the signed file from the board, or from a copy the wallet cached to spare the board a burst of requests.

  4. Shared

    A lender reads it with consent

    Ravi grants a lender access for one purpose until an end time. The lender receives the signed file and checks the board's signature itself, instead of trusting the wallet.

  5. Corrected

    The board corrects it the issuer decides

    The board corrects the mark sheet. It is still the authority, so the same reference now leads to the corrected document.

  6. Elsewhere

    A loose copy stays as it was

    A copy saved anywhere else still shows the mark sheet as it was first issued, and nothing in that copy tells its reader that the board corrected it.

The timeline has 6 events, from Issued to Elsewhere.

Checkpoint

Check: Why a wallet keeps references

  1. Question 1 of 2 The board corrects Ravi's mark sheet. Why does his locker show the corrected document without anyone copying it again?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The locker holds a reference to the board's document, and the board stays the authority for it

    The wallet points at the issuer's document instead of keeping its own version. When the board corrects it, the reference leads to the corrected document, while a copy saved anywhere else still shows the first one.

  2. Question 2 of 2 What does Ravi's locker hold for his issued mark sheet?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: A reference of about 500 bytes, the URI with a version and a status

    The URI is built from the issuer's id, the document type and a document id. With its version and status, a reference is about 500 bytes, which is why nine billion of them fit in 13.5 TB with three copies.

Requirements

Settle these first, and write down what you assume:

  • Which documents? Issued ones, from exam boards, universities, transport offices and tax departments, and scans that people upload themselves.
  • Who reads them? The owner; requesters, only with consent; and the issuer, which corrects or withdraws.
  • How does a requester know a document is genuine? From the issuer’s signature, checked with the issuer’s own published key.
  • How long does sharing last? Until an end time the owner picks, or until the owner revokes it.
  • When is traffic worst? On result days and in admission season, when millions want one issuer’s documents within minutes.

What DigiLocker's public specifications say

DigiLocker’s homepage reports more than 70 crore registered users and more than 900 crore issued documents (DigiLocker). Its issuer specification gives every document a URI of the form IssuerId-DocType-DocId, with document ids it recommends drawing at random so that nobody can guess the next one, and each issuer implements one Pull URI API that the wallet calls when a person searches for a document (issuer specification). Requesters use OAuth 2.0 with PKCE (Proof Key for Code Exchange): An addition to OAuth 2.0, RFC 7636, that binds an authorization code to the app that started the sign-in, so a code intercepted on its way back is useless. More in “Authentication and authorisation for services”, and the consent request can name document types, a purpose and the time the consent ends; uploads are JPG, PNG or PDF files of at most 10 MB (requester specification).

The functional requirements: link issued documents by search or by the issuer’s push, open the signed copy, upload and delete scans, share with a grant, revoke it, let requesters read with a token, and show the owner every read. The non-functional ones: a requester can verify a document without trusting the wallet; a revocation works within a minute; nothing leaves without a grant; uploads are encrypted at rest; and a result-day surge never reaches an issuer’s servers as a burst.

Estimates

Work out three figures first, then compare them with the sheet:

Checkpoint

Check the estimates

  1. Question 1 of 3 9 billion issued documents, a reference of 500 bytes each, kept in three copies. How many terabytes is that?

    Type a number.

    Show the answer: Question 1 of 3

    Answer: 13.5 TB (anything from 12.1 to 14.9 counts)

    9,000,000,000 × 500 B = 4.5 TB; three copies make 13.5 TB. References are small beside the uploads, which fill 1.26 PB.

  2. Question 2 of 3 A board publishes 2,500,000 results, and 40 % of the students open their mark sheet within the first 15 minutes. How many mark sheets a second is that?

    Type a number.

    Show the answer: Question 2 of 3

    Answer: 1111 mark sheets a second (anything from 1000 to 1222 counts)

    2,500,000 × 0.40 = 1,000,000 mark sheets in 900 seconds, about 1,111 a second, against a repository that answers 200: on demand it falls 5.6 times short.

  3. Question 3 of 3 The board pushes all 2,500,000 signed mark sheets into the wallet evenly over the 8 hours before results go live. How many a second is that?

    Type a number.

    A hint

    Seconds in 8 hours first.

    Show the answer: Question 3 of 3

    Answer: 87 mark sheets a second (anything from 78 to 96 counts)

    8 hours is 28,800 seconds; 2,500,000 ÷ 28,800 ≈ 87 a second, well inside the 200 a second the board's repository answers.

The document wallet's estimate sheet Python · estimate/wallet_sheet.py
# The estimate sheet of a national document wallet (lesson system-design-hld/design-document-wallet). Issuers publish
# documents such as mark sheets and licences; the wallet keeps a reference to each one, caches some signed copies, and
# stores the scans people upload themselves. The account and document counts are about the size the largest wallet
# reports; every other number is a design target for the exercise, not any wallet's real traffic. Sizes are decimal
# (1 KB = 1,000 bytes, 1 TB = 10**12 bytes, 1 PB = 10**15 bytes).
HOUR = 3_600

ACCOUNTS = 700_000_000
ISSUED = 9_000_000_000       # references to issued documents
REF_BYTES = 500              # one reference row with its indexes: URI, owner, issuer, type, version, status
REF_COPIES = 3
CACHED_SHARE = 0.30          # issued documents whose signed copy the wallet keeps
COPY_KB = 150                # the signed PDF and its machine-readable XML
EC_OVERHEAD = 1.5            # erasure coding in the object store, instead of three full copies
UPLOAD_SHARE = 0.20          # accounts that upload anything at all
FILES_EACH = 5
UPLOAD_MB = 1.2
VISITS_PER_DAY = 14_000_000  # 2 % of accounts on an ordinary day
REQUESTS_PER_VISIT = 8
BUSY_HOUR = 2.5              # the busiest hour against the day's average
STUDENTS = 2_500_000         # one exam board's results, published at a fixed minute
FIRST_SHARE = 0.40           # of them open their mark sheet within the first quarter of an hour
FIRST_MINUTES = 15
SHEET_KB = 200
ISSUER_PULLS = 200           # pull requests a second the board's repository can answer
PUSH_HOURS = 8               # pushing every document the night before instead

refs_tb = ISSUED * REF_BYTES * REF_COPIES / 10**12
cached_tb = ISSUED * CACHED_SHARE * COPY_KB * 1_000 * EC_OVERHEAD / 10**12
uploaders = ACCOUNTS * UPLOAD_SHARE
upload_raw_tb = uploaders * FILES_EACH * UPLOAD_MB * 10**6 / 10**12
upload_pb = upload_raw_tb * EC_OVERHEAD / 1_000
average = VISITS_PER_DAY * REQUESTS_PER_VISIT / (24 * HOUR)
result_rate = STUDENTS * FIRST_SHARE / (FIRST_MINUTES * 60)
gbps = result_rate * SHEET_KB * 1_000 * 8 / 10**9
push_rate = STUDENTS / (PUSH_HOURS * HOUR)

print("Document wallet: the estimate sheet")
print(f"  References    {ISSUED / 10**9:.1f} billion issued documents x {REF_BYTES} B x {REF_COPIES} copies = {refs_tb:.1f} TB")
print(f"  Cached copies {CACHED_SHARE:.0%} of them at {COPY_KB} KB, stored with {EC_OVERHEAD}x for erasure coding = {cached_tb:,.0f} TB")
print(f"  Uploads       {uploaders / 10**6:.0f} million accounts x {FILES_EACH} files x {UPLOAD_MB} MB = {upload_raw_tb:,.0f} TB, {upload_pb:.2f} PB stored")
print(f"  A normal day  {VISITS_PER_DAY / 10**6:.0f} million visits, {average:,.0f} requests/s on average, {average * BUSY_HOUR:,.0f}/s in the busiest hour")
print(f"  Result day    {STUDENTS * FIRST_SHARE:,.0f} of {STUDENTS:,} students in {FIRST_MINUTES} minutes: {result_rate:,.0f} mark sheets/s, {gbps:.1f} Gbit/s, and as many sign-in codes")
print(f"  The issuer    answers {ISSUER_PULLS} pulls/s: on demand it is {result_rate / ISSUER_PULLS:.1f}x short; pushed over {PUSH_HOURS} hours the night before, it needs {push_rate:.0f}/s")

Output

Document wallet: the estimate sheet
  References    9.0 billion issued documents x 500 B x 3 copies = 13.5 TB
  Cached copies 30% of them at 150 KB, stored with 1.5x for erasure coding = 608 TB
  Uploads       140 million accounts x 5 files x 1.2 MB = 840 TB, 1.26 PB stored
  A normal day  14 million visits, 1,296 requests/s on average, 3,241/s in the busiest hour
  Result day    1,000,000 of 2,500,000 students in 15 minutes: 1,111 mark sheets/s, 1.8 Gbit/s, and as many sign-in codes
  The issuer    answers 200 pulls/s: on demand it is 5.6x short; pushed over 8 hours the night before, it needs 87/s

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 wallet_sheet.py

The sheet’s lessons are about shape, not size. References are small, 13.5 TB for nine billion of them, while uploads fill 1.26 PB. A board’s result morning asks for 1,111 mark sheets a second from a repository that answers 200: on demand it falls 5.6 times short, while the same documents pushed over the night before need 87 a second.

Exercise · Easy · Python

Size a document wallet for an exam result day

A document wallet is sized by its references, its uploads and the minutes after an exam board publishes results. Write four functions in sizing.py. Sizes are decimal (1 MB = 106 bytes, 1 TB = 1012 bytes, 1 PB = 10**15 bytes).

  • result_day_rate(students, share, minutes): mark sheets a second when share of students open theirs within the first minutes minutes.
  • reference_tb(documents, bytes_each, copies): the terabytes that documents reference rows of bytes_each bytes take with copies copies.
  • upload_pb(accounts, share_uploading, files_each, mb_each, overhead): the petabytes of uploads when share_uploading of accounts upload files_each files of mb_each MB, stored with overhead times their size.
  • push_rate(documents, hours): the documents a second an issuer sends when it pushes documents evenly over hours hours.

The sample tests accept each figure within 10 %, as an estimate should.

Starter code · sizing.py

HOUR = 3_600


def result_day_rate(students, share, minutes):
    """Mark sheets a second in the first minutes after results go live."""
    # Replace this line with your code.
    return 0


def reference_tb(documents, bytes_each, copies):
    """Terabytes of reference rows, with every copy."""
    # Replace this line with your code.
    return 0


def upload_pb(accounts, share_uploading, files_each, mb_each, overhead):
    """Petabytes of uploaded files, with the storage overhead."""
    # Replace this line with your code.
    return 0


def push_rate(documents, hours):
    """Documents a second when an issuer pushes them evenly."""
    # Replace this line with your code.
    return 0
The sample tests · test_sizing.py
import math

from sizing import push_rate, reference_tb, result_day_rate, upload_pb


def near(actual, expected):
    """Within 10%, as an estimate should be."""
    return math.isclose(actual, expected, rel_tol=0.10)


def test_result_day_rate():
    """spreads the first wave of students over its seconds"""
    assert near(result_day_rate(2_500_000, 0.40, 15), 1_111)
    assert near(result_day_rate(1_200_000, 0.50, 20), 500)


def test_reference_tb():
    """counts every copy of every reference row"""
    assert near(reference_tb(9_000_000_000, 500, 3), 13.5)
    assert near(reference_tb(2_000_000_000, 300, 2), 1.2)


def test_upload_pb():
    """only the accounts that upload, with the storage overhead"""
    assert near(upload_pb(700_000_000, 0.20, 5, 1.2, 1.5), 1.26)
    assert near(upload_pb(100_000_000, 0.30, 4, 2.0, 3.0), 0.72)


def test_push_rate():
    """spreads a push evenly over its hours"""
    assert near(push_rate(2_500_000, 8), 86.8)
    assert near(push_rate(600_000, 5), 33.3)
A hint

The first minutes minutes hold students * share requests spread over minutes * 60 seconds. Bytes become terabytes by dividing by 10**12, and megabytes become bytes by multiplying by 10**6. A push spread over hours hours lasts hours * 3600 seconds.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

The first design

People and requesters reach a wallet through a gateway with consent checks; issued documents come from issuers, uploads are encrypted.Peopleapp and web,sign-in with a one-time codeRequestersbanks, universities,employersAPI gatewaysessions, limits per accountand per requesterConsent servicegrants: documents, purpose,requester, expiryWallet servicereferences to issued documents,uploads, versionsAudit logevery read, allowed or refused,shown to the ownerIssuer gatewaysigned pull requests,pushes from issuersIssuer repositoriesboards, transport offices,tax departmentsSigned copiesobject storage,keyed by document URIUploadsencrypted object storage,a data key per objectIssuer directoryissuer ids, document types,public keysfetch, upload,share, revokeread with a tokencheck the grantfind or fetchan issued documentsigned document,or its URIpublic keysverify signatures

The document wallet's first design

Text description of the diagram

The diagram runs from top to bottom.

  1. People use the app or the website and sign in with a one-time code; requesters such as banks, universities and employers call the same API gateway with a token.
  2. The gateway keeps sessions and applies limits per account and per requester. For every read by a requester it asks the consent service, which holds grants: which documents, for which purpose, for which requester and until when. The consent service writes every read, allowed or refused, to an audit log that the owner can see.
  3. The wallet service keeps each person's references to issued documents, their uploads and the versions of each.
  4. To find or fetch an issued document, the wallet calls the issuer gateway, which sends signed pull requests to the issuer repositories of exam boards, transport offices and tax departments, and also accepts documents that issuers push. A dashed arrow shows the signed document, or its URI, coming back.
  5. The wallet keeps signed copies in object storage, keyed by document URI, and stores uploads in a separate encrypted object store with a data key for each object.
  6. Two dashed arrows show the issuers' public keys being published to an issuer directory, which requesters use to verify signatures themselves.

The owner shares through the consent service, and a lender reads through the gateway with a token. Step through one share on the diagram’s parts:

Interactive · step by step One share on the parts of the first design Step through one share, from the owner's grant to the lender's own check of the board's signature.
One share on the parts of the first design, step 5 of 5. Showing Owner, Lender, API gateway, Consent service (one grant recorded), Wallet service, Issuer directory, Board repository. In focus: Issuer directory, Lender.sharereadOwnerapp and webLendera requesterAPI gatewaysessions and limitsConsent serviceone grant recordedWallet servicereferences and uploadsIssuer directorypublic keysBoard repositorysigned documentsthe board's key

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The grant. Ravi picks three documents, the lender, the purpose and an end date. The consent service records the grant.
  2. A read with a token. The lender calls the gateway with its token and the URI of the mark sheet it wants.
  3. Is this read allowed?. The gateway asks the consent service whether this read is allowed, and the answer, yes or no, is logged where Ravi can see it.
  4. The signed copy. The wallet returns the signed file, from its cache or fetched from the board's repository.
  5. The lender checks. The lender checks the signature with the board's public key, which it takes from the issuer directory, never from the wallet.

Checkpoint

Check: The first design: one share through it

  1. Question 1 of 2 A lender reads Ravi's shared mark sheet with its token. Which parts does that read pass through? Choose every one.

    Choose every answer that is right.

    Show the answer: Question 1 of 2

    Answer:

    • The consent service
    • The API gateway
    • The wallet service

    The read goes through the gateway, which asks the consent service and logs the answer, and the wallet returns the signed copy. The owner's app took part only when the grant was made.

  2. Question 2 of 2 Where does the lender get the key it checks the board's signature with?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: From the issuer directory, where the board publishes its public keys

    The board publishes its public keys to the issuer directory, and requesters verify signatures with them. The wallet carries signed documents but is never the thing a requester has to trust.

The deep-dive questions

  1. What does the wallet store for an issued document, and when does it keep a copy?
  2. How does a requester check a document without trusting the wallet?
  3. What is inside a consent grant, and how fast does a revocation take effect?
  4. How are uploads encrypted, checked and limited?
  5. How does the wallet survive the morning an exam board publishes its results?
  6. What changes at ten and at a hundred times the volume?

The URI names a document wherever it lives, so it keys every table Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • A requester checks four things, and the issuer’s signature is only the first
    • Twelve cases through a requester’s verifier
    • The checks in order
  • A grant names one requester, a set of documents, one purpose and an end time
    • Seven attempts against one grant
    • The grant check in code
  • A grant checked on every read makes a revocation immediate
  • Uploads are untrusted bytes, scanned first and encrypted with a key each
  • On result day only moving the board’s work to the night before keeps up
    • Four designs on one board’s morning
    • The morning with the work moved
  • At 10× the reference store is sharded by account, and at 100× object storage is the bill
  • Failure drills: what breaks, and what keeps working
  • Alternatives considered and rejected
  • What each level covers, and a 45-minute rehearsal
    • What each level is expected to cover
    • A 45-minute rehearsal
  • Practise the whole design, weakest idea first
  • Interview questions
    • Why does a document wallet keep a reference to an issued document, made of the issuer, the document type and an id, instead of only its own copy of the file?
    • A bank receives a mark sheet through a document wallet. How does it check that the document is genuine and still valid without trusting the wallet, and what has to happen when the exam board rotates its signing key?
    • An owner shares three documents with a lender for 30 days and revokes the share after two days. What must stop working at once, what can never be undone, and how do you design grants, tokens and the audit log for it?
    • An exam board publishes 2.5 million results at 10:00, and 40 % of the students open their mark sheet within 15 minutes. The board's repository answers 200 requests a second. Design the wallet's side of that morning.
  • Key takeaways

3 diagrams · 4 runnable examples (JavaScript, Python) · 3 interview answers · about 57 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Read a complete sample first: Design a real-time leaderboard

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.