Trust boundaries and data
A trust boundary is a line where data leaves the control of one party and enters another's. Certichain is designed so that the most sensitive data never crosses the lines it does not need to cross, and so that a verifier can check the result without trusting the operator. This page maps the boundaries. The Threat model and Privacy design pages go through attacks and data handling in detail.
What crosses each boundary
| From | To | What crosses | What never crosses |
|---|---|---|---|
| Wallet | App | Addresses, signatures, transaction hashes | Private keys |
| App | Chain (through the wallet) | Issuer address, salted subject commitment, document SHA-256, salted metadata hash, record type, dates, nonce, EIP-712 signature, status reason codes and note hashes, enrolment commitments | Names, student ids, documents, CIDs, document keys, salts, plaintext notes |
| App | Server | The encrypted container, the claimed document hash, the issuer's SIWE message and signature, the session cookie; for API callers, credential ids and (for file checks) a VC or Open Badge export, which carries no CID and no key | The plaintext document, the document key, salts, holder details |
| Server | Pinning service | The encrypted container and its metadata (document hash, issuer address), which are public on chain anyway | Anything decryptable |
| App | RPC provider | eth_call and eth_getLogs requests; the provider learns which ids and addresses a visitor looks up | Keys, documents, package contents |
| App | Gateway | A request for a CID | The document key (a capability link keeps the key in the URL fragment, which browsers do not send) |
| Holder | Verifier | Whatever the holder chooses: a link or id; a VC or Open Badge export (no CID, no key); a disclosure file with chosen fields; a compact QR code; or the full package | Anything the holder does not hand over |
| Safe and guardian | Chain | Admin and guardian transactions, all public events | Nothing private |
The credential package is the one file that carries everything (details, salts, CID and the document key). It is the holder's to keep. Sharing a package shares the ability to open the document; the sharing flows give narrower options.
What each party relies on
| Party | Relies on | Does not need to rely on |
|---|---|---|
| Verifier | The chain's consensus; the RPC endpoint it reads through (or its own node); the code it runs (the website, the offline script or the SDK); the admin's choice of which issuers are registered | The operator's server, the pinning service, the issuer's website, any account |
| Holder | The issuer to issue correctly; the pinning service to keep the encrypted file available (or their own copy); their own device | The operator to keep their data private: the server never had it in plaintext |
| Issuer | Its wallet and key custody; the website code it signs through; the server to accept uploads | The operator to store a database of its records: everything is on chain or with the holder |
| API and SDK users | The API signer key they pinned, the freshness and data-age fields in each signed answer | The transport or any proxy in between |
| Operator | Vercel, the pinning service, Redis, the RPC providers, the Safe owners | Holding any personal data |
Integrity anchors
A few values tie everything together. If they match, the rest can be checked.
- The credential id is the EIP-712 digest of the signed credential struct under a domain that names the chain id and the registry address. The same fields on another chain or another registry give a different id, so an id cannot be replayed elsewhere.
- The document hash is the SHA-256 of the plaintext file. It is anchored on chain, recorded in the container header, and recomputed by the verify page when a verifier chooses a file.
- The subject commitment and metadata hash are salted hashes. Only someone holding the salt (the holder, or a verifier the holder gave the package or a disclosure to) can recompute and compare them.
- The issuer signature is carried in the
CredentialSignedevent and in the package, and checked against the issuer's address (or through ERC-1271 for contract wallets). - Signed server answers carry the signer address that clients pin (
NEXT_PUBLIC_API_SIGNER,NEXT_PUBLIC_STATUS_LIST_PUBLISHER), so a verifier can tell an answer from this deployment from any other.
If one part is compromised
This table states the design intent at the architecture level. The threat model lists the mitigations and residual risks in full.
| Compromised part | What an attacker could do | What still holds |
|---|---|---|
| The hosted website code | Serve altered JavaScript that misreports checks or captures data typed into it from then on | The chain records; verification through the offline script, another front end or a block explorer; documents already stored stay encrypted |
| The server routes | Refuse or delay uploads; sign false API answers or status lists if the signer keys leak | Chain state; anyone reading the chain directly; containers stay unreadable |
| An RPC provider | Return wrong or stale reads to the browsers that use it | The chain itself; a verifier can switch provider or use the signed API |
| The pinning service | Lose or withhold encrypted files | Verification, which does not need the file; the holder's own copy; the second pin provider once configured |
| An issuer key | Issue or revoke in that issuer's name | Other issuers; the admin or guardian can suspend the issuer at once; on contract version 0.5.0 the key can be retired in favour of a successor |
| The guardian key | Pause new issuance (a denial of service) | Every credential, status and verification |
| Safe owners (two of three) | Register or remove issuers and change roles | Every past record; with the timelock in charge, a public delay before any admin action runs |
Session and role checks at the server boundary
The server does not trust a session for longer than it must. An issuer-only route (src/lib/server/issuer-session.ts) checks, in order: the per-IP budget, a valid signed session cookie, and that the wallet's issuer registration was confirmed on chain within the last five minutes (ROLE_RECHECK_SEC); otherwise it re-reads the registry. A registry outage is answered as unavailable, never as "allowed". See Sign-in and sessions and Security controls.
Source files
src/lib/eip712.ts,src/lib/commitments.ts,src/lib/credential-package.tssrc/lib/doc-crypto/container.ts,src/lib/doc-crypto/capability.tssrc/lib/server/issuer-session.ts,src/lib/server/session.ts,src/lib/server/upload-container.tssrc/lib/api/envelope.tsblockchain/contracts/v03/CredentialRegistry.sol