You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The certified-variables skill is brought up to date with @dfinity/certificate-verification 4.0.0, which peers @icp-sdk/core ^6, and with the current Rust and Motoko libraries. Every code block was compiled, deployed to a local network (icp-cli 1.5.0) and exercised end to end, and several were broken on main.
Broken on main, now fixed:
Frontend verification did not type-check against certificate-verification 4 / core 6. The parameters are now Uint8Array, not ArrayBuffer. lookup_path returns a LookupResult, not bytes, so the old if (!leafData) check never fired. The new code switches on Found/Absent and rejects Unknown. Treating Unknown as absent would let a replica hide a real value behind a witness for a different key (new pitfall 9).
The Motoko CertTree example did not compile.let ct = CertTree.Ops(...) fails with M0131 in a persistent actor and needs transient. The deprecated postupgrade hook is dropped.
The Rust example failed icp deploy. It lacked ic_cdk::export_candid!(), so the Rust recipe failed with get_candid_pointer.
Every icp canister call … get was wrong. icp-cli sends an update call unless --query is passed, so the Rust getter trapped and the Motoko getters returned certificate = null.
The upgrade pitfall was wrong. Certified data survives upgrades (abstract-behavior.md:2690-2693, confirmed locally). What is lost is a heap tree, so Rust must rebuild it and re-set the hash, while Motoko's CertTree.Store needs nothing.
The HTTP certification snippet was incomplete. It had no http_request handler, no witness header, and no IC-CertificateExpression header in the certified response. The last one makes ic-http-certification 4 return CertificateExpressionHeaderMissing. It is replaced by a complete minimal canister, moved to references/http-certification.md.
Restructured for agents. The skill now opens with "Who Verifies What", which answers whether client-side verification is needed and with which library:
Response
Verified by
HTTP via <id>.icp.net or a custom domain
the gateway
HTTP via raw or your own client
nobody; use @dfinity/response-verification
Update call through an actor
the agent
Candid query
only the answering node's signature; use certified data plus @dfinity/certificate-verification
The ICRC-3 tip certificate is covered as a use case.
The body shrinks from 482 to about 370 lines, and the size warning is gone:
Overlapping pitfalls are merged.
The conceptual diagram is dropped.
The deploy walkthrough and "Verify It Works" become a two-command check.
The HTTP material moves to a reference.
Updated:
Rust:ic-cdk 0.20, ic-certification 4 (same RbTree API as ic-certified-map, and what the official examples use), ic-http-certification 4.
New no-witness frontend path (Certificate.create + certified_data) for the single-value Motoko example, linking motoko/cert-var.
present → value; missing → proof of absence; tampered value → throws; other key's witness → Unknown, throws
Motoko single value, verifySingleValue
fresh install and after a set both verify; tampered value throws
HTTP canister through the local gateway
200 hello, and a certified 404 for other paths. A variant serving an uncertified body is rejected with backend_response_verification; certifying without IC-CertificateExpression returns CertificateExpressionHeaderMissing
Upgrade without re-setting
Motoko single value still verifies; Rust verifies with a proof of absence after post_upgrade
Local gateway, tampered response
rejected on <id>.localhost, served on <id>.raw.localhost
Mainnet ckBTC ledger icrc3_get_tip_certificate
verifyCertification passes; last_block_index and last_block_hash are Found
Eval results (new file, all cases with baseline, rerun on the restructured skill)
Case 1, "Adversarial: certified getter traps when called from icp-cli": WITH 3/3 | WITHOUT 3/3. This is a regression guard for the --query pitfall.
Case 2, "Frontend witness verification with certificate-verification 4": WITH 4/4 (twice) | WITHOUT 3/4. The baseline returns null regardless of lookup status; a later baseline run timed out.
Case 3, "Adversarial: certified queries fail after a Rust canister upgrade": WITH 3/3 (three runs) | WITHOUT 1/3, 2/3, 1/3. The baseline claims certified data is reset on upgrade.
Case 4 (new), "Adversarial: gateway assumed to verify Candid query calls": WITH 3/3 | WITHOUT 3/3. This is a regression guard for the decision table.
Case 5, "Adversarial: CertTree declarations in a persistent Motoko actor": WITH 3/3 (twice) | WITHOUT 2/3. The baseline insists on a postupgrade hook.
Case 6, "Adversarial: ic-http-certification header and fallback errors": WITH 4/4 (three runs) | WITHOUT 4/4, 4/4, 3/4. This is a regression guard for pitfall 9 and the certified 404.
Case 7, "Adversarial: certify the initial value of a Motoko certified variable": WITH 4/4 (twice) | WITHOUT 3/4 (twice). The baseline never certifies the initial value.
Refs #406 (its certified-variables row: the rootKey type fix is done here). The developer-docs guides with the same errors are fixed in dfinity/developer-docs#408.
…x broken examples
Frontend code moves to @dfinity/certificate-verification 4 and @icp-sdk/core 6
(Uint8Array, LookupResult statuses) and gains a no-witness path. Rust moves to
ic-cdk 0.20 and ic-certification 4 with export_candid; the Motoko CertTree
example compiles again. The HTTP certification example is complete and moves
to a reference. Certified getters are tested with icp canister call --query,
and the upgrade pitfall now matches the spec. Adds the first eval file.
Adds a verified decision table (HTTP gateway, update calls, Candid queries,
raw hosts) and the ICRC-3 tip certificate case, merges overlapping pitfalls,
and replaces the deploy walkthrough with a two-command check.
The Motoko single-value actor now certifies its initial value, since
certified data starts empty. The HTTP reference certifies a wildcard 404 so
other paths do not fail gateway verification. Restores the CertTree eval.
Missing output eval for IC-CertificateExpression requirement
skills/certified-variables/SKILL.md:72
Pitfall 9 is newly introduced, but none of the five output evals checks that an agent includes IC-CertificateExpression in the response before certification (or recognizes CertificateExpressionHeaderMissing). The HTTP trigger case only tests skill activation, so this easy-to-regress requirement has no content-level regression coverage. Add a focused output eval for this pitfall.
Re the latest Copilot review: added in 048a780 as case 6 ("Adversarial: ic-http-certification header and fallback errors"). It covers pitfall 9 (CertificateExpressionHeaderMissing → the IC-CertificateExpression header must be in the certified response) and the certified wildcard 404 from the reference. Results: WITH 4/4 in three runs | WITHOUT 4/4, 4/4, 3/4, so it is a regression guard. A first draft expected details the prompt did not ask for (the prompt says /hello already works); the expectations were rescoped to the question.
The reason will be displayed to describe this comment to others. Learn more.
Copilot review overview
🔵 Needs a closer look
The new witness-free single-value verification flow lacks corresponding regression coverage.
Review effort: Balanced Findings: None
Previously missed (1)
In code that hasn't changed since last review
Add output evaluation for single-value verification path
evaluations/certified-variables.json:60
The new “single value without a witness” verification path has no output eval, while the parallel witness flow does. Add a scoped case so regressions in Certificate.create, the certified_data lookup, or the SHA-256 comparison are caught.
The reason will be displayed to describe this comment to others. Learn more.
Copilot review overview
🔵 Needs a closer look
The fresh-install certification fix needs a regression evaluation before approval.
Review effort: Balanced Findings: None
Previously missed (2)
In code that hasn't changed since last review
Add regression coverage for install-time certification behavior
evaluations/certified-variables.json:60
The newly added install-time certification behavior has no regression eval, even though omitting it already caused the fresh-install example to fail earlier in this PR. Add a focused output case requiring the initial value to be certified in the actor body and the same helper to run after mutations.
Rewrite sentence fragment as a complete explanation
…tall-time certification
The verification helpers take the ?blob certificate the Motoko getters return
and fail clearly when it is null. Adds an eval case for certifying the
initial value at install, and fixes a sentence fragment in the HTTP reference.
Install-time certification now has regression coverage in case 7, "Adversarial: certify the initial value of a Motoko certified variable". WITH 4/4 in both runs | WITHOUT 3/4 in both runs; the baseline fails exactly the install-time item, never certifying the initial value until the first set.
The HTTP reference's introduction fragment is now a complete sentence.
Also from the parallel developer-docs#408 review: both verification helpers now accept the ?blob certificate that the Motoko getters return, and fail with a clear message when it is null. That was verified against live canisters: the Rust and Motoko witness paths and the single-value path still verify, and null throws.
The local check says to use gateway_url, but the command still hardcodes port 8000, so it fails for networks configured with gateway.port: 0 or any alternate port. Use the URL returned by icp network status --json; this matches the explicit guidance in skills/icp-cli/SKILL.md:310-320.
The HTTP reference's local check now takes the gateway URL from icp network status --json instead of hardcoding port 8000. It was verified on a gateway.port: 0 network: /hello returned 200 with both certification headers, and /missing a certified 404.
Also from developer-docs#408: the witness helper accepts the Motoko CertTree getter's ?Blob value (Uint8Array | null, decoded as UTF-8) as well as the Rust opt text value. It was verified with the raw responses of both getters, including an absent key and a tampered value.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
certified-variablesskill is brought up to date with@dfinity/certificate-verification4.0.0, which peers@icp-sdk/core^6, and with the current Rust and Motoko libraries. Every code block was compiled, deployed to a local network (icp-cli 1.5.0) and exercised end to end, and several were broken onmain.Broken on
main, now fixed:Uint8Array, notArrayBuffer.lookup_pathreturns aLookupResult, not bytes, so the oldif (!leafData)check never fired. The new code switches onFound/Absentand rejectsUnknown. TreatingUnknownas absent would let a replica hide a real value behind a witness for a different key (new pitfall 9).CertTreeexample did not compile.let ct = CertTree.Ops(...)fails withM0131in a persistent actor and needstransient. The deprecatedpostupgradehook is dropped.icp deploy. It lackedic_cdk::export_candid!(), so the Rust recipe failed withget_candid_pointer.icp canister call … getwas wrong. icp-cli sends an update call unless--queryis passed, so the Rust getter trapped and the Motoko getters returnedcertificate = null.abstract-behavior.md:2690-2693, confirmed locally). What is lost is a heap tree, so Rust must rebuild it and re-set the hash, while Motoko'sCertTree.Storeneeds nothing.http_requesthandler, no witness header, and noIC-CertificateExpressionheader in the certified response. The last one makesic-http-certification4 returnCertificateExpressionHeaderMissing. It is replaced by a complete minimal canister, moved toreferences/http-certification.md.Restructured for agents. The skill now opens with "Who Verifies What", which answers whether client-side verification is needed and with which library:
<id>.icp.netor a custom domainrawor your own client@dfinity/response-verification@dfinity/certificate-verificationThe ICRC-3 tip certificate is covered as a use case.
The body shrinks from 482 to about 370 lines, and the size warning is gone:
Updated:
ic-cdk0.20,ic-certification4 (sameRbTreeAPI asic-certified-map, and what the official examples use),ic-http-certification4.Certificate.create+certified_data) for the single-value Motoko example, linkingmotoko/cert-var.ic_envcookie, oricp network status --jsonin Node, with the same serving-network caveat as fix(icp-cli): drop false claim that createActor ignores { agent } #405.static-siteskill instead.Verification (local network)
ic-certification4 /ic-cdk0.20), MotokoCertTreeUnknown, throwsverifySingleValue200 hello, and a certified404for other paths. A variant serving an uncertified body is rejected withbackend_response_verification; certifying withoutIC-CertificateExpressionreturnsCertificateExpressionHeaderMissingpost_upgrade<id>.localhost, served on<id>.raw.localhosticrc3_get_tip_certificateverifyCertificationpasses;last_block_indexandlast_block_hashareFoundEval results (new file, all cases with baseline, rerun on the restructured skill)
--querypitfall.Refs #406 (its
certified-variablesrow: therootKeytype fix is done here). The developer-docs guides with the same errors are fixed in dfinity/developer-docs#408.