Skip to main content

Local development

Everything Certichain does can run on one laptop: a local Hardhat chain (chain id 31337) with the v0.3 contract sets and the access gate, and the Next.js app in development mode reading it. Only document storage needs an outside service (Pinata); the E2E harness replaces even that with a mock.

Prerequisites​

ToolVersionWhy
Node.js20.9 or later (Next.js 16 requires >=20.9.0); CI uses Node 22Root app, contracts, SDK, interop tests. The SDK package declares node >=20. There is no .nvmrc in the repository.
npmThe one that ships with NodeEach package has its own package-lock.json; use npm ci
GitAny recent
Chromium for Playwrightnpx playwright install chromiumOnly for npm run test:e2e
A browser wallet (MetaMask)Any recentTo sign in and issue by hand; not needed for the verify page
circom 2.2.2OptionalOnly for zk:reproduce, which rebuilds the proving keys from source

Install​

npm ci # root: Next.js app, unit tests, shared code
(cd blockchain && npm ci) # contracts, Hardhat, deploy scripts
(cd packages/sdk && npm ci) # only for SDK work
(cd interop && npm ci) # only for the interoperability tests

The contract tests import shared TypeScript from src/lib, so the root install is needed for blockchain/ too.

A local chain with contracts​

  1. Start the node in its own terminal: cd blockchain && npx hardhat node. It prints twenty funded test accounts. They are public, well-known development accounts: use them only on this chain.
  2. Deploy the v0.3 set: cd blockchain && npm run deploy:v03:local. The first account becomes admin of the registries. Optional: GUARDIAN_ADDRESS=0x... names a guardian, ADMIN_DELAY_SEC changes the admin transfer delay (default 3 days). The script writes src/contracts/v03/deployment.31337.json (gitignored) and exports the ABIs into src/contracts/v03/.
  3. Register issuers: ISSUERS="0xAddr=Name;0xAddr=Name" npm run issuers:v03:local. Addresses already registered are skipped.
  4. Optional, for the ZK access features: npm run deploy:access:local deploys the pinned Poseidon library, the Semaphore verifier, Semaphore and the access gate, checks their code hashes and writes src/contracts/zk/deployment.31337.json.
  5. Optional smoke: npm run smoke:v03:local registers the first account as issuer, issues a valid and a revoked credential and reads both back.

A second contract set is added the same way as on a public network: ADD_SET=1 SET_LABEL=next npm run deploy:v03:local (see Contract set upgrade).

The legacy v0.2 contracts still deploy with npm run deploy:local, which writes blockchain/deployments/localhost.json and copies the v0.2 ABIs. See Legacy v0.2 contracts.

Run the app​

Create .env.local in the repository root (gitignored; never commit it). The names and their meaning are in Configuration. For a v0.3 local setup the essentials are:

VariableLocal value
NEXT_PUBLIC_NETWORK_ID31337
NEXT_PUBLIC_CONTRACT_VERSIONv03
NEXT_PUBLIC_RPC_URL and RPC_URLhttp://127.0.0.1:8545
SESSION_SECRETAny random string of 32 or more characters, for issuer sign-in
PINATA_JWT, NEXT_PUBLIC_PINATA_GATEWAY_URLYour own test Pinata account, only if you upload documents

Then npm run dev. Before it starts, predev runs npm run zk:public, which places the circuit files for tree depths 1 to 6 (about 21 MB) in the gitignored public/zk/: it keeps files that already match src/lib/zk/circuit-hashes.json, copies from blockchain/.zk-artifacts when present, and otherwise downloads from the pinned address. The first run can take a while.

Outside production the server keeps sessions, rate limits and monitor state in memory, so Redis is not needed. SIWE_ALLOWED_DOMAINS is required only in production.

In the wallet, add the network http://127.0.0.1:8545 with chain id 31337 and import one of the node's test accounts. Restarting the node wipes the chain, so redeploy and re-register; the wallet may also need its activity reset because the nonces start again.

Optional server features

The status lists, the verification API and the monitor stay off until their keys are set. For local trials use throwaway keys created for this purpose only, for example a fresh STATUS_LIST_SIGNER_KEY with NEXT_PUBLIC_STATUS_LIST_BASE_URL=http://localhost:3000 and the key's address as NEXT_PUBLIC_STATUS_LIST_PUBLISHER. The E2E harness does exactly this for its status list journey.

Unit tests and checks​

CommandScope
npm run test:runVitest over the root app, packages/sdk and the E2E support code (blockchain/, interop/ and the journeys excluded)
npm run test:coverageThe same with coverage thresholds
npm run lint, npm run typecheckESLint and tsc --noEmit
cd blockchain && npm testHardhat contract tests
cd blockchain && npm run coverageContract coverage; fails under 95% lines or 90% branches

The full local bundle before a commit is in the handbook's verification checklist. Test details: Unit tests, Contract tests.

The E2E harness​

npm run test:e2e runs Playwright against a world it builds itself (e2e/global-setup.ts):

  • It refuses to start if something already answers on port 8545 or 3100, or if src/contracts/v03/deployment.31337.json or src/contracts/zk/deployment.31337.json exists. Move your own local manifests away first.
  • The browser's upload and gateway requests are intercepted by an in-memory mock storage (e2e/support/mock-upload.ts), so no Pinata account is needed.
  • The app server gets a throwaway status list signer and API signer and no server cache.
  • One worker, 90 second timeout per test, Chromium only. Failures keep traces in the HTML report.

See End-to-end tests for the journeys.

Source files​

  • package.json, blockchain/package.json, blockchain/hardhat.config.ts (loads ../.env)
  • blockchain/scripts/deploy-v03.ts, add-issuers-v03.ts, deploy-access-gate.ts, smoke-v03.ts, deploy.ts
  • scripts/zk-public.mjs, src/lib/zk/circuit-hashes.json
  • playwright.config.ts, e2e/global-setup.ts, e2e/support/paths.ts, e2e/support/app-server.ts, e2e/support/mock-storage.ts
  • Handbook: docs/process/commands.md, docs/process/verification-checklist.md

Tests​

  • e2e/support/guards.test.ts, e2e/support/local-chain.test.ts (the harness refusals)
  • src/lib/rpc/*.test.ts, src/lib/server/config.test.ts (settings readers)