Web application
The web application is the Next.js project in src/. It is the only user interface Certichain has: verifiers, holders, issuers, guardians and v0.2 administrators all use it. It does most of the work in the browser. Hashing, encryption, EIP-712 signing, commitments and zero-knowledge proofs run on the user's device, chain reads go straight to a public RPC endpoint, and transactions go through the user's wallet. The server side of the same project (the API routes under src/app/api/) is described in Server routes.
The same pages serve both contract generations. readContractVersion() in src/lib/contract-version.ts returns v03 only when NEXT_PUBLIC_CONTRACT_VERSION is exactly v03, and v02 otherwise. Pages with version-specific behaviour pick a v0.3 component or a legacy component from that value at render time. See Contract versions and sets.
Stack
The versions below are the ones declared in the root package.json as of 2026-10-24.
| Concern | Package | Version |
|---|---|---|
| Framework | next (App Router, next dev --turbopack) | 16.1.5 |
| UI library | react, react-dom | ^19.0.0 |
| Styling | tailwindcss with @tailwindcss/postcss | ^4 |
| Component variants | class-variance-authority, clsx, tailwind-merge, @radix-ui/react-slot | ^0.7.1, ^2.1.1, ^3.3.1, ^1.2.3 |
| Icons | lucide-react | ^0.522.0 |
| Chain access | ethers | ^6.14.4 |
| Sign-in messages | siwe | ^3.0.0 |
| Document encryption | @hpke/core, @hpke/dhkem-x25519, @noble/curves, @noble/hashes | 1.9.0, 1.8.0, 1.2.0, 1.3.2 |
| Zero-knowledge access | @semaphore-protocol/group, identity, proof; snarkjs | 4.14.3; 0.7.5 |
| Verifiable credentials | jsonld, canonicalize | 9.0.0, ^2.1.0 |
| QR codes | qrcode.react (draw), jsqr (read) | ^4.2.0, 1.4.0 |
| Server-only | jose (session tokens), @upstash/redis (shared store) | ^6.2.12, ^1.39.0 |
| Unit tests | vitest, @testing-library/react, jsdom, fast-check | ^3.2.4, 16.3.3, 26.1.0, 4.10.2 |
| Browser tests | @playwright/test | ^1.64.0 |
npm run dev and npm run build first run npm run zk:public (scripts/zk-public.mjs), which copies the hash-checked circuit files the browser prover needs into public/zk/. See Command line tools.
Directory map
| Path | What it holds |
|---|---|
src/app/ | Routes. Each folder with a page.tsx is a page; src/app/api/ holds the server routes. Page-specific components sit next to their page. |
src/app/layout.tsx | The root layout: fonts, metadata and the ServicesProvider. |
src/components/layout/ | The page shell: MainLayout, Header, NetworkBanner, DemoBanner, Footer, navigation.ts, LegalDocument. |
src/components/ui/ | Shared primitives (Button, Notice, Seal, StatusStamp, CheckLine and others). See Design system. |
src/components/ (other folders) | Feature components shared by several pages: access/, home/, present/, vc/, plus ConfirmDialog, PausedNotice, RecordTypeLabel, RedirectTo, VerificationQr. |
src/services/ | The service layer: wallet, gateways, domain services, indexing, React glue. See Service layer. |
src/lib/ | Framework-free logic: hashing, commitments, EIP-712 types, encryption (doc-crypto/), VC and status list code (vc/), RPC (rpc/), zero-knowledge helpers (zk/), the seal geometry (seal/) and the server modules (server/). |
src/contracts/ | ABIs and deployment manifests: v03/deployment.<chainId>.json, zk/deployment.<chainId>.json, and the v0.2 ABI. |
public/ | manifest.webmanifest, sw.js (the offline shell worker for the phone pages), icons and zk/ circuit files. |
How a page reaches the chain
src/app/layout.tsxis a server component. It loads the three fonts, sets the page metadata and wraps every page inServicesProvider.ServicesProvider(src/services/react/services-provider.tsx) is a client component. On first render it callscreateBrowserServices()once and keeps the result in state, so every component on the page shares one set of services. Tests pass aservicesprop with fakes instead.- Components call
useServices()and use a domain service, for exampleservices.locator.locate(id)on the verify page. They never build contracts or providers themselves. - Domain services read through gateways. Gateways read over the public RPC endpoints (no wallet needed) and ask the wallet only for signatures and transactions. See Reading the chain and Wallet and network.
Server and client components
| Kind | Pages | Why |
|---|---|---|
| Server component that renders a client component | /, /about, /verify, /present, /wallet, /open, /access, /records/add, /records/import, /privacy-policy, /terms-conditions | The page file stays small, can export metadata (/present, /wallet) or wrap a client component in Suspense (/verify, /records/add, which read query parameters). |
Client component ("use client" in page.tsx) | /login, /student, /dashboard, /admin, /records, /records/[id], /records/types, /students/invite | These pages read the wallet session and redirect on role, so they run in the browser. |
Every page that touches a wallet, a contract or the browser's storage is a client component, because the services use window.ethereum, window.location and localStorage. The home and about pages render their copy on the server from a per-version table (see Design system).
Page shell
Every page renders inside MainLayout (src/components/layout/MainLayout.tsx), in this order:
- A Skip to content link to
#main. DemoBanner: arole="note"strip with the text "Test network demo. Do not upload real personal documents. Data written to the blockchain is public."Header: the role workspaces, the area tabs and the wallet chip. See Design system.NetworkBanner: shown only while a connected wallet is on another chain. See Wallet and network.<main id="main">with the page.Footer.
Browser configuration
The browser bundle reads only NEXT_PUBLIC_* variables. Next.js inlines them at build time, so changing one needs a rebuild. Names are listed in example.env; values are never shown in these docs.
| Variable | Used for | When missing |
|---|---|---|
NEXT_PUBLIC_NETWORK_ID | The expected chain id: network guard, chain checks on writes, manifest lookup | Writes fail with "Blockchain service is not configured"; the network banner is disabled |
NEXT_PUBLIC_RPC_URLS, NEXT_PUBLIC_RPC_URL | Public RPC endpoints for wallet-free reads (comma list first, then the single URL) | Wallet-free reads fail with an explanation |
NEXT_PUBLIC_CONTRACT_VERSION | v03 selects the v0.3 pages; anything else is v0.2 | v0.2 pages |
NEXT_PUBLIC_CONTRACT_ADDRESS | The v0.2 AcademicRecords address (v0.3 addresses come from the manifest) | v0.2 reads fail |
NEXT_PUBLIC_START_BLOCK | First block of the v0.2 share history scan | v0.2 share lists report that history is not configured |
NEXT_PUBLIC_PINATA_GATEWAY_URL | First IPFS gateway for reading documents | Only the public gateways are used |
NEXT_PUBLIC_STATUS_LIST_BASE_URL, NEXT_PUBLIC_STATUS_LIST_PUBLISHER | Status list entries in exported VCs and their checks | VC export carries the chain status only |
NEXT_PUBLIC_API_SIGNER | The published verification API signer (read by the server, the monitor and callers) | Not used by the pages themselves |
The full list, including server-only names, is in Configuration.
Pages in this section
| Page | What it covers |
|---|---|
| Routes | Every route: who sees it, v0.2 and v0.3 behaviour, purpose, main components |
| Service layer | The composition root, every service and how they depend on each other |
| Wallet and network | The wallet service, its errors, the session hook, the network guard and chain switching |
| Reading the chain | RPC failover, log scans, caches, the start block, the credential and share indexes, signature lookup |
| Contract-set routing | How the app finds the contract set that holds a credential and sends every check there |
| Design system | Tokens, fonts, primitives, the seal, navigation workspaces, version-aware copy, accessibility rules |
Source files
src/app/layout.tsxsrc/components/layout/MainLayout.tsx,src/components/layout/DemoBanner.tsxsrc/services/react/services-provider.tsx,src/services/react/browser-services.tssrc/services/index.tssrc/lib/contract-version.ts,src/lib/chain.tsnext.config.ts,package.json
Tests
src/components/layout/MainLayout.test.tsx,src/components/layout/DemoBanner.test.tsxsrc/services/react/services-provider.test.tsx,src/services/index.test.tssrc/lib/contract-version.test.ts- Browser journeys under
e2e/journeys/exercise the pages end to end; see End-to-end tests.