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
feat: adopt icp.net as the mainnet HTTP gateway domain (icp-cli v1.0.0) (#226)
* feat: adopt icp.net as the mainnet HTTP gateway domain
icp-cli v1.0.0 changed the default mainnet HTTP gateway from icp0.io to
icp.net (browser canister access is now <id>.icp.net / raw.icp.net).
Reconcile the skills with the developer-docs change.
Changed (gateway / browser canister-access URLs):
- asset-canister: mainnet access URL, certified/raw serving domains,
browser-open URL; also fix the Node.js HttpAgent host from the legacy
ic0.app access domain to the canonical API endpoint icp-api.io
- custom-domains: default access URL, ic-domains fetch URLs, and the
custom-domains/v1 REST API host (confirmed to resolve on icp.net)
- certified-variables: mainnet curl example
- deploy-to-cloud-engine: frontend canister access URL
- internet-identity: illustrative frontend_origins / TRUSTED_ORIGIN
Kept (NOT the gateway):
- API/agent hosts stay icp-api.io (icp.net is gateway-only)
- Named dapp domains (nns.ic0.app) in icp-cli and agent-web-identity
- II delegation rewriting pitfall: II still rewrites icp0.io -> ic0.app
at the protocol level (per the II spec, unchanged by the gateway
rename), so that behavioral claim stays as-is rather than becoming a
meaningless icp.net-vs-icp.net statement
Evals: updated custom-domains API-endpoint and asset-canister host
assertions; added an asset-canister guard that the mainnet browser URL
is <id>.icp.net (not ic0.app/icp0.io/icp-api.io). Both new/updated
cases pass with the skill.
Closes#224
* fix(custom-domains): correct HttpAgent host guidance (Raymond's review)
The pitfall and HttpAgent Configuration section claimed the agent
"cannot auto-detect the IC API host on custom domains, so you must set
host explicitly." That is inaccurate for recent agents. Per
@icp-sdk/core determineHost(), an omitted host defaults to the local
replica for localhost/127.0.0.1 origins and to https://icp-api.io for
every other origin — including custom domains and icp.net. So on a
custom domain you do not need to set host; it already resolves to the
mainnet API boundary nodes.
Reframe the pitfall around the real mistake: pointing host at the custom
domain (or window.location.origin), which serves only the HTTP gateway,
not /api/v2, so calls fail. Update the eval case to match.
* docs(custom-domains): make HttpAgent host instructions crystal clear
Rewrite the HttpAgent Configuration section with an explicit
resolution table (what an omitted host resolves to per runtime) and a
per-scenario "what to do" list covering all cases:
- mainnet browser frontend (icp.net / custom domain / legacy gateways):
leave host unset → resolves to icp-api.io
- mainnet Node.js: unset or icp-api.io
- local dev in the browser on *.localhost: unset (auto-detects replica)
- local dev in Node.js: MUST set host to the local replica URL — no
window.location to detect localhost, so omitted host hits mainnet
- non-mainnet/custom network: MUST set host to that network's API
endpoint — the icp-api.io default points at mainnet
Clarify that host is the API endpoint, never the gateway domain the
frontend is served from. Tighten pitfall 8 to point at the section.
Add an eval for the Node.js-local case (omitted host silently hits
mainnet), the most counterintuitive of the must-set-host scenarios.
* docs(custom-domains): scope HttpAgent host guidance to mainnet only
The custom-domains skill covers only the mainnet IC custom-domains
service — you cannot have a custom domain on a local replica or a custom
network. Drop the local-development and custom-network HttpAgent cases
(and the resolution table) added in the previous commit; they were
out of scope and diluted the skill.
The section now states only what is relevant: a mainnet frontend served
from a custom domain does not need host set (it resolves to icp-api.io),
and must never point host at the gateway/custom domain. Remove the
matching out-of-scope Node.js-local eval case.
Copy file name to clipboardExpand all lines: evaluations/asset-canister.json
+10-1Lines changed: 10 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -3,6 +3,15 @@
3
3
"description": "Evaluation cases for the asset-canister skill. Tests whether agents produce correct asset canister configuration, avoid top-level await in programmatic upload code, and handle SPA routing.",
4
4
5
5
"output_evals": [
6
+
{
7
+
"name": "Mainnet browser-access domain is icp.net",
8
+
"prompt": "I deployed my asset (frontend) canister to mainnet. What URL do I open in the browser to view it? Just the URL pattern.",
9
+
"expected_behaviors": [
10
+
"Gives the URL as https://<canister-id>.icp.net",
11
+
"Does NOT use the legacy ic0.app or icp0.io gateway domains",
12
+
"Does NOT use icp-api.io (that is the API endpoint, not the browser gateway)"
13
+
]
14
+
},
6
15
{
7
16
"name": "Programmatic asset upload",
8
17
"prompt": "Show me a Node.js script to create an HttpAgent and AssetManager, then upload a single file to my asset canister on the local replica. Keep it minimal — no icp.yaml, no deploy steps.",
@@ -49,7 +58,7 @@
49
58
"expected_behaviors": [
50
59
"Sets shouldFetchRootKey to true ONLY for local development",
51
60
"Explicitly warns that shouldFetchRootKey on mainnet is a security vulnerability",
52
-
"Uses different host values for local (localhost:8000) vs mainnet (ic0.app or icp-api.io)"
61
+
"Uses different host values for local (localhost:8000) vs mainnet (icp-api.io)"
Copy file name to clipboardExpand all lines: evaluations/custom-domains.json
+9-9Lines changed: 9 additions & 9 deletions
Original file line number
Diff line number
Diff line change
@@ -17,9 +17,9 @@
17
17
"name": "Domain registration API",
18
18
"prompt": "I've set up my DNS records and deployed my canister with the ic-domains file. What curl commands do I run to register my custom domain app.example.com? Just the commands.",
19
19
"expected_behaviors": [
20
-
"Uses the validate endpoint: GET https://icp0.io/custom-domains/v1/app.example.com/validate",
21
-
"Uses the registration endpoint: POST https://icp0.io/custom-domains/v1/app.example.com",
22
-
"Uses the status check endpoint: GET https://icp0.io/custom-domains/v1/app.example.com",
20
+
"Uses the validate endpoint: GET https://icp.net/custom-domains/v1/app.example.com/validate",
21
+
"Uses the registration endpoint: POST https://icp.net/custom-domains/v1/app.example.com",
22
+
"Uses the status check endpoint: GET https://icp.net/custom-domains/v1/app.example.com",
23
23
"Does NOT invent non-existent API endpoints or parameters"
24
24
]
25
25
},
@@ -53,19 +53,19 @@
53
53
},
54
54
{
55
55
"name": "HttpAgent host configuration",
56
-
"prompt": "My IC frontend works fine on icp0.io but API calls fail when I access it through my custom domain. What's wrong? Just the fix.",
56
+
"prompt": "My IC frontend's canister calls fail when it's served from my custom domain, though they work on icp.net. I set the HttpAgent host to my custom domain. What's wrong? Just the fix.",
57
57
"expected_behaviors": [
58
-
"Identifies that HttpAgent cannot auto-detect the IC API host on custom domains",
59
-
"Shows setting host to https://icp-api.io for mainnet",
60
-
"Shows HttpAgent.create({ host }) or equivalent configuration"
58
+
"Identifies that the custom domain serves only the HTTP gateway, not the /api/v2 API endpoint, so host must not point at it",
59
+
"Says to leave host unset (a recent agent defaults it to https://icp-api.io on a custom domain) or set it explicitly to https://icp-api.io",
60
+
"Does NOT recommend setting host to the custom domain or window.location.origin"
61
61
]
62
62
},
63
63
{
64
64
"name": "Domain update flow",
65
65
"prompt": "I want to point my existing custom domain to a different canister. What do I need to do? Just the steps.",
66
66
"expected_behaviors": [
67
67
"Updates the _canister-id TXT record to the new canister ID",
68
-
"Uses PATCH https://icp0.io/custom-domains/v1/DOMAIN to notify the service",
68
+
"Uses PATCH https://icp.net/custom-domains/v1/DOMAIN to notify the service",
69
69
"Does NOT say to delete and re-register the domain"
70
70
]
71
71
},
@@ -74,7 +74,7 @@
74
74
"prompt": "How do I remove a custom domain registration from the IC? Just the steps.",
75
75
"expected_behaviors": [
76
76
"Removes the _canister-id TXT and _acme-challenge CNAME DNS records",
77
-
"Uses DELETE https://icp0.io/custom-domains/v1/DOMAIN to notify the service",
77
+
"Uses DELETE https://icp.net/custom-domains/v1/DOMAIN to notify the service",
78
78
"Does NOT suggest only removing DNS records without calling the API"
Copy file name to clipboardExpand all lines: skills/asset-canister/SKILL.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -26,7 +26,7 @@ Access patterns:
26
26
| Environment | URL Pattern |
27
27
|-------------|-------------|
28
28
| Local |`http://<canister-id>.localhost:8000`|
29
-
| Mainnet |`https://<canister-id>.ic0.app` or `https://<canister-id>.icp0.io`|
29
+
| Mainnet |`https://<canister-id>.icp.net`|
30
30
| Custom domain |`https://yourdomain.com` (with DNS configuration) |
31
31
32
32
## Mistakes That Break Your Build
@@ -45,7 +45,7 @@ Access patterns:
45
45
46
46
7.**Pinning the asset canister Wasm version below `0.30.2`.** The `ic_env` cookie (used by `safeGetCanisterEnv()` from `@icp-sdk/core` to read canister IDs and the root key at runtime) is only served by asset canister Wasm versions >= `0.30.2`. The Wasm version is set via `configuration.version` in the recipe, independently of the recipe version itself. If you pin an older Wasm version, the cookie is silently missing and frontend code relying on `ic_env` will fail. Either omit `configuration.version` (latest is used) or pin to `0.30.2` or later.
47
47
48
-
8.**Not configuring `allow_raw_access` correctly.** The asset canister has two serving modes: certified (via `ic0.app` / `icp0.io`, where HTTP gateways verify response integrity) and raw (via `raw.ic0.app` / `raw.icp0.io`, where no verification occurs). By default, `allow_raw_access` is `true`, meaning assets are also available on the raw domain. On the raw domain, boundary nodes or a network-level attacker can tamper with response content undetected. Set `"allow_raw_access": false` in `.ic-assets.json5` for any sensitive assets. Only enable raw access when strictly needed.
48
+
8.**Not configuring `allow_raw_access` correctly.** The asset canister has two serving modes: certified (via `icp.net`, where HTTP gateways verify response integrity) and raw (via `raw.icp.net`, where no verification occurs). By default, `allow_raw_access` is `true`, meaning assets are also available on the raw domain. On the raw domain, boundary nodes or a network-level attacker can tamper with response content undetected. Set `"allow_raw_access": false` in `.ic-assets.json5` for any sensitive assets. Only enable raw access when strictly needed.
49
49
50
50
9.**Downgrading the asset canister WASM version.** Upgrading a canister to an older WASM version can fail with "Cannot parse header" panics if the stable memory format changed between versions. Prefer the `@dfinity/asset-canister` recipe over `type: pre-built` with a manually specified WASM URL — the recipe loads the latest asset canister version automatically if not explicitly specified in `configuration.version`. If you must pin a version, ensure it matches or exceeds the version currently deployed on-chain. If a downgrade is intentional, use reinstall mode (`icp deploy --mode reinstall`) instead of upgrade — this wipes stable memory and all uploaded assets.
Copy file name to clipboardExpand all lines: skills/custom-domains/SKILL.md
+23-19Lines changed: 23 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,7 +12,7 @@ metadata:
12
12
13
13
## What This Is
14
14
15
-
By default, canisters are accessible at `<canister-id>.icp0.io`. The custom domains service lets you serve any canister under your own domain (e.g., `yourdomain.com`). You configure DNS, deploy a domain ownership file to your canister, and register via a REST API. The HTTP gateways then handle TLS certificate provisioning, renewal, and routing automatically.
15
+
By default, canisters are accessible at `<canister-id>.icp.net`. The custom domains service lets you serve any canister under your own domain (e.g., `yourdomain.com`). You configure DNS, deploy a domain ownership file to your canister, and register via a REST API. The HTTP gateways then handle TLS certificate provisioning, renewal, and routing automatically.
16
16
17
17
Custom domains work at the boundary node level — they map a domain to any canister ID via DNS. This works with any canister that can serve `/.well-known/ic-domains` over HTTP, not just asset canisters. That includes asset canisters, Juno satellites, and custom canisters implementing `http_request`.
18
18
@@ -40,7 +40,7 @@ Custom domains work at the boundary node level — they map a domain to any cani
40
40
41
41
7.**Not explicitly registering the domain.** DNS configuration alone is not enough. You must call `POST /custom-domains/v1/CUSTOM_DOMAIN` to start registration. It is not automatic.
42
42
43
-
8.**Not setting`host`in HttpAgent on custom domains.**When serving from a custom domain, the `HttpAgent` cannot automatically infer the IC API host like it can on `icp0.io`. You must set `host: "https://icp-api.io"`explicitly for mainnet.
43
+
8.**Setting `HttpAgent`'s`host`to your custom domain.**`host` is the **API endpoint** canister calls go to, not the domain your frontend is served from. Your custom domain is the HTTP gateway — it does not serve `/api/v2`, so pointing `host` at it (or at `window.location.origin`) makes calls fail. You do not need to set `host`: a recent `@icp-sdk/core``HttpAgent` resolves an omitted `host` to `https://icp-api.io` (the mainnet API boundary nodes) on a custom domain. Leave it unset, or set it explicitly to `https://icp-api.io` — never the gateway domain.
44
44
45
45
9.**Forgetting alternative origins for Internet Identity.** II principals depend on the origin domain. Switching from a canister URL to a custom domain changes principals. Configure `.well-known/ii-alternative-origins` to keep the same principals. See the `internet-identity` skill.
46
46
@@ -78,14 +78,14 @@ www.example.com
78
78
79
79
### Step 3: Deploy
80
80
81
-
Deploy your canister so that `/.well-known/ic-domains` is accessible at `https://<canister-id>.icp0.io/.well-known/ic-domains`.
81
+
Deploy your canister so that `/.well-known/ic-domains` is accessible at `https://<canister-id>.icp.net/.well-known/ic-domains`.
82
82
83
83
### Step 4: Validate
84
84
85
85
Check DNS records and canister configuration before registering:
86
86
87
87
```bash
88
-
curl -sL -X GET "https://icp0.io/custom-domains/v1/CUSTOM_DOMAIN/validate"| jq
88
+
curl -sL -X GET "https://icp.net/custom-domains/v1/CUSTOM_DOMAIN/validate"| jq
89
89
```
90
90
91
91
Success response:
@@ -116,7 +116,7 @@ If validation fails, common errors and fixes:
116
116
### Step 5: Register
117
117
118
118
```bash
119
-
curl -sL -X POST "https://icp0.io/custom-domains/v1/CUSTOM_DOMAIN"| jq
119
+
curl -sL -X POST "https://icp.net/custom-domains/v1/CUSTOM_DOMAIN"| jq
120
120
```
121
121
122
122
Success response:
@@ -137,7 +137,7 @@ Success response:
137
137
Poll until `registration_status` is `registered`:
138
138
139
139
```bash
140
-
curl -sL -X GET "https://icp0.io/custom-domains/v1/CUSTOM_DOMAIN"| jq
140
+
curl -sL -X GET "https://icp.net/custom-domains/v1/CUSTOM_DOMAIN"| jq
141
141
```
142
142
143
143
Status values: `registering` → `registered` (success), or `failed` (check error message).
@@ -152,13 +152,13 @@ To point an existing custom domain at a different canister:
curl -sL -X GET "https://icp0.io/custom-domains/v1/CUSTOM_DOMAIN"| jq
176
+
curl -sL -X GET "https://icp.net/custom-domains/v1/CUSTOM_DOMAIN"| jq
177
177
```
178
178
179
179
## HttpAgent Configuration
180
180
181
-
On custom domains, the agent cannot auto-detect the IC API host. Set it explicitly:
181
+
A frontend served from your custom domain still makes its canister calls through the mainnet **API boundary nodes** (`https://icp-api.io`), not through the domain it is served from. `HttpAgent`'s `host` is that API endpoint — *not* your frontend's origin. The custom domain is the HTTP gateway and does not serve `/api/v2`.
182
+
183
+
You do not need to set `host`. When it is omitted, a recent `@icp-sdk/core``HttpAgent` resolves to `https://icp-api.io` on a custom domain (and on `icp.net`). Do **not** point `host` at your custom domain or `window.location.origin` — that is the gateway, so calls would fail.
Copy file name to clipboardExpand all lines: skills/deploy-to-cloud-engine/SKILL.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -160,7 +160,7 @@ icp deploy -e ic --subnet <subnet-id>
160
160
- The `icp deploy` output reports the deployed canister ids.
161
161
- The canisters appear on the engine's **Applications** page in the console; each canister's detail view offers an "Open in browser" link.
162
162
- If you set the metadata in Step 2, the canisters are grouped under your `__META_PROJECT` name with their `__META_NAME` labels, and the main canister shows an "Open" button — instead of bare principal rows.
163
-
- A frontend (asset) canister is served at `https://<frontend-canister-id>.icp0.io`.
163
+
- A frontend (asset) canister is served at `https://<frontend-canister-id>.icp.net`.
164
164
165
165
Report the deployed canister ids (and the frontend URL, if any) back to the user.
0 commit comments