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
| Surface | Check |
|---|---|
| Pages | None. The operator uses the site like anyone else. |
/api/ops/check, /api/ops/report | Authorization: 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 scripts | The 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.
| Feature | Variables | Off until |
|---|---|---|
| Contract system and network | NEXT_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 reads | RPC_URL, RPC_URLS (may hold provider keys, so never NEXT_PUBLIC_) | Falls back to the public endpoint |
| Document storage | PINATA_JWT (or PINATA_API_KEY and PINATA_API_SECRET_KEY), NEXT_PUBLIC_PINATA_GATEWAY_URL, PINATA_GATEWAY_ACCESS_KEY, SECOND_PIN_PROVIDER, PIN_READ_GATEWAYS | Upload needs a Pinata credential; the JWT needs file read and write permission for issuers to find and remove their copies |
| Issuer sign-in | SESSION_SECRET (at least 32 characters), SIWE_ALLOWED_DOMAINS (required in production) | Upload refuses without them |
| Shared store | UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN, or KV_REST_API_URL and KV_REST_API_TOKEN | Required in production for nonces, rate limits and monitor history |
| Status lists | STATUS_LIST_SIGNER_KEY, NEXT_PUBLIC_STATUS_LIST_BASE_URL, NEXT_PUBLIC_STATUS_LIST_PUBLISHER, STATUS_LIST_CACHE_SECONDS | The signer key and base address are set |
| Verification API | API_SIGNER_KEY, NEXT_PUBLIC_API_SIGNER, API_KEYS (SHA-256 hashes only), API_CACHE_SECONDS, API_ALLOW_LOCAL_LIMITS | The signer key is set |
| Monitor | CRON_SECRET, OPS_WEBHOOK_URL, OPS_MAX_LAG_BLOCKS, OPS_LOOKBACK_BLOCKS, OPS_WARN_DAYS, OPS_KEY_EXPIRIES | The secret and a schedule are set |
| Canary probe | OPS_PROBE_API_URL, OPS_PROBE_CANARY_ID, OPS_PROBE_API_KEY | All are set and a canary credential exists |
| Deployment (local only) | PRIVATE_KEY, INFURA_API_KEY, ETHERSCAN_API_KEY, GUARDIAN_ADDRESS, ADMIN_DELAY_SEC, ADMIN_MULTISIG | Used by the Hardhat scripts, never by the site |
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.
| Key | Signs | Published as | If it changes |
|---|---|---|---|
STATUS_LIST_SIGNER_KEY | Bitstring status list credentials served at /api/status-lists/... | NEXT_PUBLIC_STATUS_LIST_PUBLISHER | Verifiers who pinned the old address reject new lists |
API_SIGNER_KEY | Every answer of the verification API | NEXT_PUBLIC_API_SIGNER | SDK 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:
| Check | Looks at |
|---|---|
rpc | Every configured endpoint answers the right chain and is not trailing the best by more than OPS_MAX_LAG_BLOCKS |
chain-events | Administrative events in the last OPS_LOOKBACK_BLOCKS blocks: pause, issuer changes, retirements, role changes, admin transfers, issuer suspensions, timelock operations |
governance | Every watched registry has the same admin, no pending transfer, and (once recorded) the timelock as admin |
signers | Each signer key the server holds matches its published address (NEXT_PUBLIC_STATUS_LIST_PUBLISHER, NEXT_PUBLIC_API_SIGNER) |
keys | Dates in OPS_KEY_EXPIRIES within OPS_WARN_DAYS |
probe | A 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/:
| Task | Command |
|---|---|
| Deploy a v0.3 set | npm run deploy:v03:sepolia (with ADD_SET=1 and ISSUER_REGISTRY to add a set to an existing issuer registry) |
| Verify on Etherscan | npm 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 Safe | npm run handover:v03:sepolia |
| Timelock | deploy:timelock:sepolia, timelock:nominate:sepolia, timelock:handover:sepolia, timelock:propose:sepolia |
| Make a set active | npm run set:ready:sepolia (read only, lists blockers), then npm run set:activate:sepolia (sends nothing, records the active set) |
| Read governance state | npm 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.
| Runbook | Use |
|---|---|
incident.md | Turning the monitor on, severity levels, the first ten minutes, communication, the record |
support.md | Channels (placeholders until named), the support log, response targets, the monthly report |
keys.md | Inventory, rotation and expiry of every key and token |
admin-keys.md, timelock.md | Safe, guardian and timelock procedures |
contract-sets.md | Adding, activating and retiring a contract set |
rpc-and-pinning.md | Providers and pin repair |
vulnerability-reports.md | Handling 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_EXPIRIESkept current. - Every public-network deployment approved first and recorded in the manifest.
- Support channels named for each partner before onboarding.
Source files
example.envsrc/lib/server/config.ts,src/lib/server/issuer-session.ts,src/lib/server/session.tssrc/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.tsscripts/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.tssrc/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