certified-variables
Certified Variables
A query call is answered by a single replica, without consensus, so a faulty or malicious replica can return fabricated data. Certification closes that gap: during update calls the canister stores a 32-byte hash (usually the root of a Merkle tree over its data) in the subnet's certified state, and query responses carry a certificate signed by the subnet's threshold BLS key. Clients verify the certificate and a Merkle witness, and get a fast query response that is as trustworthy as an update call.
Who Verifies What
Decide this first. Certification only helps if something checks it.
| Response | Verified by | Client code needed |
|---|---|---|
HTTP from a frontend canister or http_request, via <id>.icp.net or a custom domain |
the HTTP gateway | none |
The same, via <id>.raw.icp.net or your own HTTP client |
nobody | @dfinity/response-verification (verifyRequestResponsePair) |
Update call through an actor (agent.update) |
consensus; the agent checks the response certificate | none |
| Candid query call through an actor | only the answering node's signature | certified data + @dfinity/certificate-verification (this skill) |
The gateway never verifies Candid API calls (/api/...), even when the page came from a verifying host: on ic0.app, icp0.io or local origins the agent sends them through the page origin, and the gateway only proxies them. Certify a query when a client acts on its result: balances, permissions, prices, anything security-relevant. The alternative is to call the method as an update and accept consensus latency. Ledgers implementing ICRC-3 already certify their tip: icrc3_get_tip_certificate returns opt { certificate; hash_tree }, which verifies with verifyCertification like any witness below (labels last_block_index, a LEB128 number, and last_block_hash, so decode them instead of the helper's UTF-8 compare).
Static frontends served by the certified-assets canister (@dfinity/static-site recipe) are certified automatically: load the static-site skill for those.