Skip to main content

Operator

The operator is the party that runs a Certichain deployment: they deploy and verify the contracts, host the web application, hold the server keys, pay for the RPC and pinning services, watch the monitor, answer partners and walk new institutions through onboarding. On the Sepolia deployment this is the project team. The operator usually also proposes admin actions through the Safe, but that power belongs to the Admin role, not to the operator as such.

The operator has no on-chain role of their own. Their powers are off-chain: what the site is configured to read, which optional features are on, which keys sign server answers, and where alerts go.

How the system recognises the operator​

SurfaceCheck
PagesNone. The operator uses the site like anyone else.
/api/ops/check, /api/ops/reportAuthorization: Bearer <CRON_SECRET>, compared in constant time; the secret must be at least 32 characters. Any other caller, and every caller when no secret is set, gets the same 401.
Deployment scriptsThe deployer key in the operator's local environment (PRIVATE_KEY), used only for deployments; on Sepolia it was retired after the handover.
Hosting (Vercel)Access to the project's environment variables and scheduler, outside the repository.

Configuration the operator owns​

Variable names only; values live in the hosting provider's settings or a local .env, never in the repository. example.env lists every name with a comment. The Configuration page gives the full reference.

FeatureVariablesOff until
Contract system and networkNEXT_PUBLIC_CONTRACT_VERSION (v03, otherwise v0.2), NEXT_PUBLIC_NETWORK_ID, NEXT_PUBLIC_RPC_URL, NEXT_PUBLIC_RPC_URLS, NEXT_PUBLIC_START_BLOCK (v0.2 share history), NEXT_PUBLIC_CONTRACT_ADDRESS (v0.2)Always needed
Server chain readsRPC_URL, RPC_URLS (may hold provider keys, so never NEXT_PUBLIC_)Falls back to the public endpoint
Document storagePINATA_JWT (or PINATA_API_KEY and PINATA_API_SECRET_KEY), NEXT_PUBLIC_PINATA_GATEWAY_URL, PINATA_GATEWAY_ACCESS_KEY, SECOND_PIN_PROVIDER, PIN_READ_GATEWAYSUpload needs a Pinata credential; the JWT needs file read and write permission for issuers to find and remove their copies
Issuer sign-inSESSION_SECRET (at least 32 characters), SIWE_ALLOWED_DOMAINS (required in production)Upload refuses without them
Shared storeUPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN, or KV_REST_API_URL and KV_REST_API_TOKENRequired in production for nonces, rate limits and monitor history
Status listsSTATUS_LIST_SIGNER_KEY, NEXT_PUBLIC_STATUS_LIST_BASE_URL, NEXT_PUBLIC_STATUS_LIST_PUBLISHER, STATUS_LIST_CACHE_SECONDSThe signer key and base address are set
Verification APIAPI_SIGNER_KEY, NEXT_PUBLIC_API_SIGNER, API_KEYS (SHA-256 hashes only), API_CACHE_SECONDS, API_ALLOW_LOCAL_LIMITSThe signer key is set
MonitorCRON_SECRET, OPS_WEBHOOK_URL, OPS_MAX_LAG_BLOCKS, OPS_LOOKBACK_BLOCKS, OPS_WARN_DAYS, OPS_KEY_EXPIRIESThe secret and a schedule are set
Canary probeOPS_PROBE_API_URL, OPS_PROBE_CANARY_ID, OPS_PROBE_API_KEYAll are set and a canary credential exists
Deployment (local only)PRIVATE_KEY, INFURA_API_KEY, ETHERSCAN_API_KEY, GUARDIAN_ADDRESS, ADMIN_DELAY_SEC, ADMIN_MULTISIGUsed by the Hardhat scripts, never by the site
Status as of 2026-10-24

The Sepolia site runs the v0.3 contracts. The verification API, the status lists, the monitor and the canary probe are built and stay off until the operator sets their keys, secret and schedule. The repository has no vercel.json, so the monitor's schedule is set in the hosting provider.

Server keys​

Two server keys sign things that outsiders trust. Both must be dedicated keys that hold no funds.

KeySignsPublished asIf it changes
STATUS_LIST_SIGNER_KEYBitstring status list credentials served at /api/status-lists/...NEXT_PUBLIC_STATUS_LIST_PUBLISHERVerifiers who pinned the old address reject new lists
API_SIGNER_KEYEvery answer of the verification APINEXT_PUBLIC_API_SIGNERSDK users who pinned the old address reject answers

API keys for the higher tier are made with npm run api:key; only their SHA-256 hashes go into API_KEYS. The keys runbook (docs/process/runbooks/keys.md) inventories every key and token, how to rotate it and what breaks if it is lost. See Key management.

The monitor​

/api/ops/check runs a set of read-only checks and posts alerts to OPS_WEBHOOK_URL when a result changes. A broken configuration shows up as a failing check named config; otherwise the checks are:

CheckLooks at
rpcEvery configured endpoint answers the right chain and is not trailing the best by more than OPS_MAX_LAG_BLOCKS
chain-eventsAdministrative events in the last OPS_LOOKBACK_BLOCKS blocks: pause, issuer changes, retirements, role changes, admin transfers, issuer suspensions, timelock operations
governanceEvery watched registry has the same admin, no pending transfer, and (once recorded) the timelock as admin
signersEach signer key the server holds matches its published address (NEXT_PUBLIC_STATUS_LIST_PUBLISHER, NEXT_PUBLIC_API_SIGNER)
keysDates in OPS_KEY_EXPIRIES within OPS_WARN_DAYS
probeA canary credential checked through the public API against the pinned signer, and how far the API's view trails the chain

GET /api/ops/report?month=YYYY-MM returns the month's objectives and alert counts; with &post=1 from the scheduler it posts the text to the webhook once. History needs Redis. Flow: Monitoring. Server: Monitoring endpoints.

Pinning and RPC​

Encrypted containers are pinned to Pinata through the upload route; a second pinning service can be named in SECOND_PIN_PROVIDER (no adapter for a specific second service is named yet). Reads go through one failover provider that tries the configured endpoints in order. The runbook docs/process/runbooks/rpc-and-pinning.md covers adding a provider, an outage of the primary, and repairing a missing copy. Pages: Storage and IPFS, Upload and documents.

Deployments​

Every deployment to a public network sends transactions and needs the project owner's explicit go-ahead each time. The scripts live in blockchain/scripts/ and run from blockchain/:

TaskCommand
Deploy a v0.3 setnpm run deploy:v03:sepolia (with ADD_SET=1 and ISSUER_REGISTRY to add a set to an existing issuer registry)
Verify on Etherscannpm run verify:v03:sepolia, npm run verify:access:sepolia
Register issuers (before the handover)ISSUERS="0xAddr=Name" npm run issuers:v03:sepolia
Hand the admin role to the Safenpm run handover:v03:sepolia
Timelockdeploy:timelock:sepolia, timelock:nominate:sepolia, timelock:handover:sepolia, timelock:propose:sepolia
Make a set activenpm run set:ready:sepolia (read only, lists blockers), then npm run set:activate:sepolia (sends nothing, records the active set)
Read governance statenpm run governance:v03:sepolia (read only)

Each deployment writes a manifest under src/contracts/v03/ that the site reads. Pages: Deployments, Deployment and scripts, Deployment manifest, Contract set upgrade, Commands.

Support and incidents​

The runbooks under docs/process/runbooks/ are the operator's procedures. Runbooks summarises them.

RunbookUse
incident.mdTurning the monitor on, severity levels, the first ten minutes, communication, the record
support.mdChannels (placeholders until named), the support log, response targets, the monthly report
keys.mdInventory, rotation and expiry of every key and token
admin-keys.md, timelock.mdSafe, guardian and timelock procedures
contract-sets.mdAdding, activating and retiring a contract set
rpc-and-pinning.mdProviders and pin repair
vulnerability-reports.mdHandling a private report received through GitHub's private vulnerability reporting (see SECURITY.md)

The support runbook's response targets are a first human reply within 4 hours for severity 1, 1 working day for severity 2 and 3 working days for severity 3 (working days in Asia/Kathmandu).

Onboarding an institution​

The playbook in docs/onboarding/ takes an institution through six stages: fit and review, key ceremony (two named key custodians, a written and tested recovery plan), registration (the admin registers the address and it is checked on the explorer), staff training on the test network, first credentials (a rehearsal and a small batch checked by a verifier), and operating and leaving. Each stage has a checklist and the end has a sign-off sheet. Registration itself is an admin action: see Admin.

What the operator cannot do​

  • Issue, revoke or change credentials. Only issuers can.
  • Register an institution alone. That needs the Safe owners' signatures (and, after the handover, the timelock delay).
  • Read documents or personal data. The server stores only encrypted containers and never sees document keys, names or student IDs.
  • Forge a verification answer without the API signer key, and verifiers pin that key's address.

Checklist​

  • Every server secret set in the hosting provider, marked sensitive, never prefixed NEXT_PUBLIC_.
  • Signer keys are dedicated, hold no funds, and their addresses are published.
  • Redis configured before production use.
  • Monitor secret, webhook and schedule set; OPS_KEY_EXPIRIES kept current.
  • Every public-network deployment approved first and recorded in the manifest.
  • Support channels named for each partner before onboarding.

Source files​

  • example.env
  • src/lib/server/config.ts, src/lib/server/issuer-session.ts, src/lib/server/session.ts
  • src/lib/server/ops/handler.ts, src/lib/server/ops/config.ts, src/lib/server/ops/wiring.ts, src/lib/server/ops/checks/
  • src/lib/server/pinning/, src/lib/server/status-lists/, src/lib/server/api/
  • src/app/api/ops/check/route.ts, src/app/api/ops/report/route.ts
  • scripts/api-key.mjs, blockchain/scripts/
  • docs/process/runbooks/, docs/onboarding/

Tests​

  • src/lib/server/ops/handler.test.ts (bearer secret, same 401 for every refusal), config.test.ts, monitor.test.ts, wiring.test.ts, src/lib/server/ops/checks/*.test.ts
  • src/lib/server/pinning/*.test.ts, src/lib/server/config.test.ts, scripts/api-key.test.ts
  • E2E: e2e/journeys/service-probe.journey.ts, status-lists.journey.ts, verification-api.journey.ts