Skip to content

Commit 6795f5c

Browse files
authored
fix(https-outcalls): scope cycle-cost guidance to Application subnets; normalize cross-reference sections (#357)
1 parent a67acdc commit 6795f5c

4 files changed

Lines changed: 25 additions & 14 deletions

File tree

evaluations/https-outcalls.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"skill": "https-outcalls",
3-
"description": "Evaluation cases for the https-outcalls skill. Tests whether agents use the correct 2MB response limit (2,000,000 bytes per the interface spec, not 2^21) and the correct cycle cost for omitting max_response_bytes on a 13-node subnet.",
3+
"description": "Evaluation cases for the https-outcalls skill. Tests whether agents use the correct 2MB response limit (2,000,000 bytes per the interface spec, not 2^21), the correct cycle cost for omitting max_response_bytes on a 13-node subnet, and that the Application-subnet cost formula does not apply on a cloud engine.",
44
"output_evals": [
55
{
66
"name": "2MB limit is 2,000,000 bytes and the default-size cost",
@@ -10,6 +10,15 @@
1010
"Gives the omitted-max_response_bytes cost as roughly 20.8-20.9 billion cycles",
1111
"Does NOT state the cost as ~21.5 billion or ~21.86 billion cycles"
1212
]
13+
},
14+
{
15+
"name": "Adversarial: outcall cycle cost on a cloud engine",
16+
"prompt": "My canister runs on a cloud engine (a CloudEngine subnet) and makes an HTTPS outcall with the standard Call.httpRequest wrapper. Roughly how many cycles should I budget for this outcall, and do I need to attach any? Answer briefly.",
17+
"expected_behaviors": [
18+
"States the cost is 0 on a cloud engine, not the ~20.8 billion Application-subnet figure",
19+
"Says no cycles need to be attached and the standard wrapper already handles this correctly",
20+
"Does NOT instruct topping up the canister with cycles or attaching a hardcoded non-zero fee"
21+
]
1322
}
1423
],
1524
"trigger_evals": {

skills/cloud-engine-canisters/SKILL.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,8 @@ For the management-canister key methods (`sign_with_ecdsa`, `ecdsa_public_key`,
172172
8. **Topping up the proxy without first asking what it was relaying.** On `ProxyError::InsufficientCycles`, check the call type before treating it as a funding problem. **An HTTPS outcall does not belong on the proxy at all** (Rule 3, pitfall 4): move it onto your own canister, where it is free, rather than buying budget for work that should cost nothing — raising the balance only delays the same stall. A drained balance is a genuine funding problem only for the proxy's real jobs (XRC, threshold signing, vetKD): top it up, or enable auto top-up, on the console's Proxy canisters page. Deploying and funding the proxy is a console action, not an `icp` command.
173173
9. **Expecting a direct-call key through the proxy.** Threshold-key derivation via the proxy is caller-isolated, so the derived key/address is not the same as a direct management-canister call. Fetch the public key and sign through the proxy consistently; do not mix direct and proxied key calls for the same identity.
174174

175-
## Related Skills
175+
## Additional References
176176

177-
If a referenced skill is not already available, install it the same way this one was installed — `npx skills add dfinity/icskills --skill <name>` — or read it at `https://skills.internetcomputer.org/skills/<name>/`.
178-
179-
- **deploy-to-cloud-engine** — getting the app onto the engine: CLI identity linking, subnet-targeted deploy, console app metadata.
180-
- **https-outcalls** — everything about outcalls that is not engine-specific: transform functions, `max_response_bytes`, idempotency, debugging consensus failures.
181-
- **multi-canister** — inter-canister call design, bounded vs unbounded wait semantics, and `SYS_UNKNOWN` handling.
177+
- Load `deploy-to-cloud-engine` for getting the app onto the engine: CLI identity linking, subnet-targeted deploy, console app metadata.
178+
- Load `https-outcalls` for everything about outcalls that is not engine-specific: transform functions, `max_response_bytes`, idempotency, debugging consensus failures.
179+
- Load `multi-canister` for inter-canister call design, bounded vs unbounded wait semantics, and `SYS_UNKNOWN` handling.

skills/deploy-to-cloud-engine/SKILL.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,8 @@ A cloud engine runs on a **`CloudEngine` subnet** with protocol-level call rules
318318
13. **Git metadata substitutions in a non-git project.** Outside a git repository, `$(git rev-parse HEAD)` does not fail the build — it silently bakes garbage: `service:git:sha` becomes the literal `+dirty` and `service:git:origin` comes out empty. Check for a git repo first (`git rev-parse HEAD` succeeds); if there is none, set only `service:version` with an explicit value (or `git init` and commit before deploying, if version control is wanted anyway).
319319
14. **Letting an engine app collect sign-ins before pinning a derivation origin.** Internet Identity principals are per-origin, so adding a custom domain later turns every existing user into a stranger at the new address — and the fix cannot be applied retroactively without orphaning the accounts already made under the old origin. On the first deploy of any app that uses II, set `derivationOrigin` to the address of the canister that serves the frontend, built from its canister id (`https://<frontend-canister-id>.icp.net`), even when that is currently the app's only origin. Do **not** copy `__META_BASE_URL`: it is allowed to point at a custom domain, and a custom domain is exactly what must not become the derivation origin. See the `internet-identity` skill for the `.well-known/ii-alternative-origins` half.
320320

321-
## Related Skills
321+
## Additional References
322322

323-
If a referenced skill is not already available, install it the same way this one was installed — `npx skills add dfinity/icskills --skill <name>` — or read it at `https://skills.internetcomputer.org/skills/<name>/`.
324-
325-
- **cloud-engine-canisters** — the call rules for code that runs on the engine: never attach cycles, bounded-wait cross-subnet calls, direct HTTPS outcalls, and the console proxy for cycle-bearing cross-subnet targets (XRC, threshold signing, vetKD). Load it before writing or debugging engine canister code.
326-
- **icp-cli** — general icp CLI usage (`icp.yaml`, recipes, environments, bindings, identities). Load it for anything beyond this cloud-engine deploy flow — in particular when the project does not build or package yet.
327-
- **internet-identity** — details of the Internet Identity sign-in that Step 1 triggers in the browser.
323+
- Load `cloud-engine-canisters` for the call rules for code that runs on the engine: never attach cycles, bounded-wait cross-subnet calls, direct HTTPS outcalls, and the console proxy for cycle-bearing cross-subnet targets (XRC, threshold signing, vetKD). Load it before writing or debugging engine canister code.
324+
- Load `icp-cli` for general icp CLI usage (`icp.yaml`, recipes, environments, bindings, identities). Load it for anything beyond this cloud-engine deploy flow — in particular when the project does not build or package yet.
325+
- Load `internet-identity` for details of the Internet Identity sign-in that Step 1 triggers in the browser.

skills/https-outcalls/SKILL.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ You do not deploy anything extra. The management canister is built into every su
3333

3434
1. **Forgetting the transform function.** Without a transform, the raw HTTP response often differs between replicas (different headers, different ordering in JSON fields, timestamps). Consensus fails and the call is rejected. ALWAYS provide a transform function.
3535

36-
2. **Not attaching cycles to the call.** HTTPS outcalls are not free. The calling canister must attach cycles to cover the cost. If you attach zero cycles, the call fails immediately. Both Motoko and Rust have wrappers that compute and attach the required cycles automatically: in Motoko, use `await Call.httpRequest(args)` from the `ic` mops package (`import Call "mo:ic/Call"`); in Rust, use `ic_cdk::management_canister::http_request` (available since ic-cdk 0.18). Under the hood, both use the `ic0.cost_http_request` system API to calculate the exact cost from `request_size` and `max_response_bytes`.
36+
2. **Not attaching cycles to the call.** On a normal Application subnet, HTTPS outcalls are not free — the calling canister must attach cycles to cover the cost, and attaching zero fails the call. Both Motoko and Rust have wrappers that compute and attach the required cycles automatically: in Motoko, use `await Call.httpRequest(args)` from the `ic` mops package (`import Call "mo:ic/Call"`); in Rust, use `ic_cdk::management_canister::http_request` (available since ic-cdk 0.18). Under the hood, both use the `ic0.cost_http_request` system API to calculate the exact cost from `request_size` and `max_response_bytes` — the API is cost-schedule aware, so the same wrapper attaches the correct amount on any subnet type. On a **cloud engine** (`CloudEngine` subnet), that amount is always 0 by design; do not "fix" a working outcall by attaching a hardcoded non-zero fee there — see the `cloud-engine-canisters` skill.
3737

3838
3. **Using HTTP instead of HTTPS.** The IC only supports HTTPS outcalls. Plain HTTP URLs are rejected. The target server must have a valid TLS certificate.
3939

@@ -336,6 +336,8 @@ per-request-byte term.
336336

337337
Unused cycles are refunded to the canister, so it is safe to over-budget.
338338

339+
This formula applies on a normal **Application subnet**. It does not apply on a **cloud engine** (`CloudEngine` subnet): there, `ic0.cost_http_request` returns 0 regardless of `request_size` or `max_response_bytes`, because engines run under a free cost schedule. Load the `cloud-engine-canisters` skill for the engine's call rules before writing or debugging outcall code that will run there.
340+
339341
## Deploy & Test
340342

341343
### Local Deployment
@@ -430,3 +432,7 @@ fn transform_normalize(args: TransformArgs) -> HttpRequestResult {
430432
}
431433
}
432434
```
435+
436+
## Additional References
437+
438+
- Load `cloud-engine-canisters` for canisters running on a cloud engine, including why outcall cost drops to 0 there and why outcalls must never be routed through the engine's console proxy.

0 commit comments

Comments
 (0)