Skip to main content

Holder inbox

The student page (/student, header Holder, title My credentials) lists "Everything issued to this wallet, read from the chain." There is no server-side list and no database. The chain deliberately keeps no wallet-to-credential index, so the browser rebuilds the list from the wallet's enrolments and the CredentialIssued events for those enrolments.

Preconditions​

  • A connected wallet. Without one the page sends the visitor to /login. Listing needs no signature and no transaction.
  • The wallet has accepted an invitation from at least one issuer (see Enrolment).
  • The credentials were issued with Deliver to the student's linked wallet ticked, so their subject commitment equals the enrolment commitment. Credentials issued without delivery never appear, by design. Credentials from Bulk import are issued without delivery.

How the list is built​

InboxService.myCredentials (src/services/domain/inbox-service.ts) does work proportional to the number of issuers and the holder's own credentials, never to all credentials:

  1. Issuers. CredentialIndex.issuers() reads IssuerAdded and IssuerUpdated events from the IssuerRegistry, so removed issuers are included and names are current.
  2. Enrolments. For each issuer, HolderRegistry.enrolmentOf(issuer, wallet). A zero result means no enrolment there.
  3. Credentials. For each enrolment, a log query on CredentialIssued filtered by the indexed issuer and subjectCommitment topics.
  4. Description. Each entry gets its record type name (built-in names locally, custom names from the RecordTypeRegistry) and its status. A status that cannot be read leaves the item in the list with Status unavailable.
  5. Filter and sort. An event whose credential has status None is not a credential, whatever the event says, and is dropped. The rest are sorted by block, newest first.

AllSetsInbox (src/services/domain/all-sets-lists.ts) runs this for every listed contract set and merges the results, tagging each item with its set.

Log scanning​

The log scans run in the browser through the CredentialIndex interface (LogQueryIndex in src/services/indexing/credential-index.ts), so a hosted indexer could replace them later without touching pages. Defaults (DEFAULT_SCAN_OPTIONS in log-scan.ts):

OptionValue
Start blockThe set's deploymentBlock from the manifest
Window20,000 blocks per request, shrinking to 500 when the provider refuses a range
Request cap1,000 requests, so a broken endpoint cannot keep the client scanning
Reorg marginThe last 12 blocks are scanned again on resume
Retries3, starting at 500 ms and doubling, with jitter
CacheResults are kept in localStorage under keys starting certichain.scan.v1:, per chain, contract and topic filter

The list​

Section "Issued to this wallet". Each row shows:

ElementMeaning
SealDrawn from the credential id (greyed when revoked)
Type nameBuilt-in name or the issuer's own, marked issuer-defined
Issuer nameFrom the IssuerRegistry events
BadgeActive, Suspended, Revoked, Superseded, or Status unavailable; an Active credential that is not valid shows a caution tone with "Recorded as active but not valid right now, usually because it expired. Open Verify for the reason."
Short idThe first and last characters of the id
Older set 0.3.0When the credential lives in a set that is not the active one
VerifyOpens /verify?id=...
Show QRA QR code of the verification link
Manage accessOnly where the access gate serves that set; see Private access proofs

States of the panel:

StateShown
LoadingA placeholder, announced to screen readers as "Reading your credentials from the chain..."
FailedCould not load your credentials. "This is not proof that you have none. The chain could not be read just now." with Try again
EmptyNo credentials yet: "They appear here after a university you accepted an invitation from issues one to you."

The package and the inbox​

The inbox proves that a credential was issued to this wallet's enrolment, but it carries none of the private parts: the name, the student ID, the salts and the document key are only in the credential package, which the issuer hands over separately (download, email, archive). The tools below the list therefore take the package as input.

GroupPanelUses
Open and share your documentsOpen a documentThe package opens the stored document; see Document storage
Open a shared documentA shared document file from someone else; see Sharing
My share codeThe holder's own share code, for receiving documents
Show only what is neededDisclosure panelMakes a disclosure file
VC export panelExports a verifiable credential

The page also shows Accept an invitation for linking with another institution.

Opening a document from the inbox​

Opening needs no wallet signature: the key is in the package. The result card links to the verify page because the package says nothing about status.

Variants​

  • v0.2 (legacy). The student page shows My records: "The records issued to this wallet, and the records other people shared with you." Records are read from AcademicRecords, and the "shared with you" list is rebuilt from sharing events, which needs NEXT_PUBLIC_START_BLOCK. See Legacy v0.2 contract.
  • Several contract sets. Items from an older set carry the Older set badge and are routed to that set for every action.

Limits​

  • The list can lag a fresh issuance by the time the RPC endpoint takes to index the block.
  • A holder who lost the package can see and verify the credential but cannot open the document or prove their details. The issuer re-issues in that case.
  • Every listed issuer costs one enrolmentOf call, so the cost grows with the number of registered issuers.

Code map​

ConcernFile
Pagesrc/app/student/page.tsx, MyCredentialsPanel.tsx, CredentialRow.tsx, ToolGroup.tsx
Servicesrc/services/domain/inbox-service.ts, all-sets-lists.ts, set-fan-out.ts
Indexsrc/services/indexing/credential-index.ts, log-scan.ts, storage-scan-cache.ts, browser-cache.ts, provider-log-source.ts
Badgessrc/components/credential-status.ts
Contractsblockchain/contracts/v03/HolderRegistry.sol (enrolmentOf), CredentialRegistry.sol (CredentialIssued)

Tests​

  • src/services/domain/inbox-service.test.ts, all-sets-lists.test.ts, set-fan-out.test.ts
  • src/services/indexing/credential-index.test.ts, log-scan.test.ts, storage-scan-cache.test.ts, browser-cache.test.ts
  • src/app/student/MyCredentialsPanel.test.tsx, StudentPageV03.test.tsx
  • E2E: e2e/journeys/invite-and-inbox.journey.ts, delivery.journey.ts, contract-sets.journey.ts