From f59ed89023705ac8cd00907550b38e9055ce9654 Mon Sep 17 00:00:00 2001 From: Artem Korotkikh <105195369+artkorotkikh-dfinity@users.noreply.github.com> Date: Wed, 24 Jun 2026 10:25:32 +0200 Subject: [PATCH 1/2] feat: document app icon (__META_BASE_URL + __META_ICON_PATH) for the console MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The skill covered name/label/Open metadata but not the app icon. Add the icon pair, with behavior verified against the console source (the control-panel frontend that renders these), not guessed: - `__META_BASE_URL` — app 'Open' link and icon base; must be an absolute https URL (the console rejects non-https / data: / javascript:). - `__META_ICON_PATH` — icon path resolved against the base (new URL(iconPath, baseUrl)); rendered as . - Both are read ONLY from the main canister; the icon has no fallback, so a missing/invalid base or a path on a side canister means no icon. - The variable is `__META_ICON_PATH`, not `__META_ICON_LINK`/`_ICON`/`_LOGO` (a common wrong guess) — Pitfall 8 calls this out. - Env values are length-capped (~128 chars), so a data: URI does not fit; it is a served path by design. Pitfall 9 + the adversarial eval cover it. - Note the deploy-twice ordering: the frontend canister id is only known after the first deploy, so set the base URL then re-deploy. Evals: 'Give the app an icon' output eval, 'icon not showing (data URI / wrong canister)' adversarial, and a should_trigger icon query. Co-Authored-By: Claude Fable 5 --- evaluations/deploy-to-cloud-engine.json | 26 +++++++++++++++++++++++-- skills/deploy-to-cloud-engine/SKILL.md | 24 +++++++++++++++++++---- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/evaluations/deploy-to-cloud-engine.json b/evaluations/deploy-to-cloud-engine.json index 530cad4..95c379c 100644 --- a/evaluations/deploy-to-cloud-engine.json +++ b/evaluations/deploy-to-cloud-engine.json @@ -1,6 +1,6 @@ { "skill": "deploy-to-cloud-engine", - "description": "Evaluation cases for the deploy-to-cloud-engine skill. Tests whether agents drive the correct cloud-engine deploy flow (link the CLI identity against the console origin, target the engine's subnet with icp deploy), use the default console origin https://opencloud.org unless the user names another, ask for the subnet id instead of guessing, set the `__META_*` environment variables so the console shows a named app with labelled canisters, and avoid the documented pitfalls.", + "description": "Evaluation cases for the deploy-to-cloud-engine skill. Tests whether agents drive the correct cloud-engine deploy flow (link the CLI identity against the console origin, target the engine's subnet with icp deploy), use the default console origin https://opencloud.org unless the user names another, ask for the subnet id instead of guessing, set the `__META_*` environment variables so the console shows a named app with labelled canisters and an app icon (`__META_BASE_URL` + `__META_ICON_PATH` on the main canister), and avoid the documented pitfalls.", "output_evals": [ { @@ -105,6 +105,27 @@ "Tells the user to set it and re-deploy by explicitly naming the `icp deploy` command (not just saying 'redeploy')", "Does NOT attribute the missing button to the subnet, the identity, or the --auth origin" ] + }, + { + "name": "Give the app an icon in the console", + "prompt": "My app is already deployed to my cloud engine and shows up named in the console. How do I give it an icon? My frontend already serves favicon.svg.", + "expected_behaviors": [ + "Uses `__META_ICON_PATH` for the icon path (e.g. `/favicon.svg`), resolved against `__META_BASE_URL`", + "Sets `__META_BASE_URL` to an absolute https URL (the frontend canister URL like https://.icp.net or a custom domain)", + "Sets both on the main canister (the one marked `__META_MAIN_CANISTER: \"true\"`)", + "Re-runs `icp deploy` to apply the settings", + "Does NOT invent `__META_ICON`, `__META_LOGO`, or `__META_ICON_LINK`, and does NOT put an inline data: URI in the icon variable" + ] + }, + { + "name": "Adversarial: icon not showing (data URI / wrong canister)", + "prompt": "I set `__META_ICON_PATH` to a base64 data: URI of my logo on my backend canister and redeployed, but the console still shows no icon. Why?", + "expected_behaviors": [ + "Identifies that the icon must be set on the MAIN canister (marked `__META_MAIN_CANISTER: \"true\"`), not the backend", + "Identifies that `__META_ICON_PATH` is a path resolved against `__META_BASE_URL`, not an inline image — a data: URI does not belong there (and would exceed the engine env value length cap)", + "Tells the user to set `__META_BASE_URL` to an absolute https URL and `__META_ICON_PATH` to a served path (e.g. /favicon.svg), both on the main canister, then re-deploy", + "Does NOT attribute the missing icon to the subnet, the identity, or the --auth origin" + ] } ], @@ -119,7 +140,8 @@ "Ship this project to my cloud engine", "My agent should deploy straight to my engine", "Make my deployed app show up with a name in the cloud engine console", - "Label my backend and frontend canisters in the engine console instead of bare principal ids" + "Label my backend and frontend canisters in the engine console instead of bare principal ids", + "Give my deployed app an icon in the OpenCloud engine console" ], "should_not_trigger": [ "Package my app as a .icp archive so others can install it on their engines from the marketplace", diff --git a/skills/deploy-to-cloud-engine/SKILL.md b/skills/deploy-to-cloud-engine/SKILL.md index 892336d..5bcc18c 100644 --- a/skills/deploy-to-cloud-engine/SKILL.md +++ b/skills/deploy-to-cloud-engine/SKILL.md @@ -1,6 +1,6 @@ --- name: deploy-to-cloud-engine -description: "Deploys an already-built Internet Computer project to a user's own cloud engine (an OpenCloud / control-panel engine, administered from a web console). Covers verifying the icp CLI, linking the user's console identity to the CLI with `icp identity link web`, defaulting the console origin to https://opencloud.org (overridable when the user signs in to a different console), obtaining the engine's subnet id (asking the user when it is unknown), running `icp deploy` against that subnet, and tagging the canisters with `__META_*` environment variables so the engine console shows a named app with labelled backend/frontend canisters. Use when a developer wants to ship an app to their cloud engine, mentions a cloud engine, OpenCloud, an engine subnet id, linking the icp CLI to an engine console, or giving a deployed app a name in the console. Do NOT use for a general mainnet deploy with no specific engine or subnet (use the icp-cli skill) or for writing canister code." +description: "Deploys an already-built Internet Computer project to a user's own cloud engine (an OpenCloud / control-panel engine, administered from a web console). Covers verifying the icp CLI, linking the user's console identity to the CLI with `icp identity link web`, defaulting the console origin to https://opencloud.org (overridable when the user signs in to a different console), obtaining the engine's subnet id (asking the user when it is unknown), running `icp deploy` against that subnet, and tagging the canisters with `__META_*` environment variables so the engine console shows a named app with labelled backend/frontend canisters and an app icon. Use when a developer wants to ship an app to their cloud engine, mentions a cloud engine, OpenCloud, an engine subnet id, linking the icp CLI to an engine console, or giving a deployed app a name or icon in the console. Do NOT use for a general mainnet deploy with no specific engine or subnet (use the icp-cli skill) or for writing canister code." license: Apache-2.0 compatibility: "icp-cli >= 0.3.0 (commands verified against 0.3.0), a cloud engine console account, a browser for the Internet Identity sign-in" metadata: @@ -77,13 +77,17 @@ icp identity default # prints the active identity name icp identity principal # prints the principal the deploy will sign as ``` -## Step 2 — Name the app in the console (recommended) +## Step 2 — Name the app (and give it an icon) in the console (recommended) -By default, CLI-deployed canisters appear on the engine console's Applications page as bare rows labelled only by their principal id. Three **canister environment variables** make the console group them into a single named application with readable per-canister labels and an "Open" button. Set them once in your project config: +By default, CLI-deployed canisters appear on the engine console's Applications page as bare rows labelled only by their principal id. A set of **canister environment variables** makes the console group them into a single named application with readable per-canister labels, an "Open" button, and an icon. Set them once in your project config: - `__META_PROJECT` — the application name. Canisters that share the **same** value are grouped into one named app, so set an identical value on every canister of the app. - `__META_NAME` — the per-canister display label (e.g. `Backend`, `Frontend`). - `__META_MAIN_CANISTER` — the literal string `"true"` on the single entry-point canister (usually the frontend/asset canister). That canister gets the "Open" button and the app's public URL. +- `__META_BASE_URL` — the app's public base, as an **absolute `https://` URL** (e.g. the frontend canister's URL `https://.icp.net`, or a custom domain). It is the app's "Open" link **and** the base that the icon path resolves against. Set it on the **main** canister. +- `__META_ICON_PATH` — the path to the app icon, resolved against `__META_BASE_URL` to form the icon the console renders (e.g. `/favicon.svg` → `https:///favicon.svg`). Set it on the **main** canister, alongside `__META_BASE_URL`. + +The icon and "Open" link are read **only from the main canister** (the one marked `__META_MAIN_CANISTER: "true"`) — `__META_BASE_URL` / `__META_ICON_PATH` on any other canister are ignored. Set them under each canister's `settings.environment_variables` — this is valid alongside a recipe. With per-canister `canister.yaml` files: @@ -102,6 +106,8 @@ settings: __META_PROJECT: "My App" __META_NAME: "Frontend" __META_MAIN_CANISTER: "true" + __META_BASE_URL: "https://.icp.net" + __META_ICON_PATH: "/favicon.svg" ``` ```yaml @@ -129,6 +135,8 @@ canisters: __META_PROJECT: "My App" __META_NAME: "Frontend" __META_MAIN_CANISTER: "true" + __META_BASE_URL: "https://.icp.net" + __META_ICON_PATH: "/favicon.svg" - name: backend recipe: # … as in the canister.yaml example above settings: @@ -142,6 +150,12 @@ Notes: - All values are strings; `__META_MAIN_CANISTER` must be the exact string `"true"`. - They are applied during `icp deploy` (the "Setting environment variables" step). After deploy, confirm with `icp canister settings show -e ic`. +Icon specifics (the console builds the icon as `__META_BASE_URL` + `__META_ICON_PATH`): +- **Both** must be present and **on the main canister** for an icon to appear — there is no fallback. `__META_ICON_PATH` alone does nothing. +- `__META_BASE_URL` must parse as an absolute **`https://`** URL. A bare host, an `http://` URL, or a `data:` / `javascript:` value is rejected, and then *neither* the icon *nor* the "Open" link renders. (The console validates the scheme before using it.) +- `__META_ICON_PATH` is a **path to an asset your frontend actually serves** (e.g. `/favicon.svg`), not an inline image. The resolved URL is rendered as an `` `src`, so it must return an image. Do **not** put a `data:` URI here: engine env values are length-capped (≤128 chars observed), so it would not fit, and the field is a path by design. +- The frontend canister's id is only known **after** the first deploy. The usual flow is: deploy once, read the frontend canister id from the output, set `__META_BASE_URL` to `https://.icp.net` (and `__META_ICON_PATH`), then re-deploy to apply. If you control a custom domain for the app, you can set it up front instead. + ## Step 3 — Deploy to the engine's subnet From the project root: @@ -159,7 +173,7 @@ icp deploy -e ic --subnet - The `icp deploy` output reports the deployed canister ids. - The canisters appear on the engine's **Applications** page in the console; each canister's detail view offers an "Open in browser" link. -- 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. +- 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. With `__META_BASE_URL` + `__META_ICON_PATH` set, the app also shows its icon (allow for a short console cache delay). - A frontend (asset) canister is served at `https://.icp.net`. Report the deployed canister ids (and the frontend URL, if any) back to the user. @@ -173,6 +187,8 @@ Report the deployed canister ids (and the frontend URL, if any) back to the user 5. **Using `dfx`.** This ecosystem uses `icp`, never `dfx`. The correct sequence is `icp identity link web --auth ` (Step 1), then `icp deploy -e ic --subnet ` (Step 3). See the `icp-cli` skill. 6. **Skipping the app metadata.** Without `__META_PROJECT` (Step 2), the canisters still deploy and work but render as bare, unnamed principal rows in the console. Setting `__META_*` is what produces a named app with labelled canisters and an "Open" button. 7. **Wrong `__META_MAIN_CANISTER` value.** It is matched as the exact string `"true"`. A boolean, `"True"`, or marking more than one canister means no (or the wrong) "Open" button. Mark exactly one entry-point canister. +8. **Inventing an icon variable.** The icon variable is `__META_ICON_PATH` (a path resolved against `__META_BASE_URL`). Do not guess `__META_ICON`, `__META_LOGO`, or `__META_ICON_LINK` — they are ignored, so the icon silently never appears. +9. **Icon set on the wrong canister, or without a base URL.** The icon is read only from the **main** canister and needs **both** `__META_BASE_URL` (a valid absolute `https://` URL) and `__META_ICON_PATH`. Setting the icon path on a side canister, omitting the base URL, or giving a non-https / `data:` base means no icon renders (and a bad base URL also drops the "Open" link). ## Related Skills From 56cea2d0cd9b2681a981072a66fedbb8ded29464 Mon Sep 17 00:00:00 2001 From: Artem Korotkikh <105195369+artkorotkikh-dfinity@users.noreply.github.com> Date: Wed, 24 Jun 2026 11:48:23 +0200 Subject: [PATCH 2/2] docs(deploy-to-cloud-engine): clarify main-canister vs base-url and Open-link fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address Copilot review on Step 2: separate which canister is the metadata source (__META_MAIN_CANISTER) from the Open-button URL value (__META_BASE_URL). Also correct the claim that an invalid/missing base URL drops the "Open" link — per control-panel's grouping logic it falls back to the main canister's gateway URL, so only the icon is lost. Co-Authored-By: Claude Opus 4.8 --- skills/deploy-to-cloud-engine/SKILL.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/skills/deploy-to-cloud-engine/SKILL.md b/skills/deploy-to-cloud-engine/SKILL.md index 5bcc18c..55de3d1 100644 --- a/skills/deploy-to-cloud-engine/SKILL.md +++ b/skills/deploy-to-cloud-engine/SKILL.md @@ -83,8 +83,8 @@ By default, CLI-deployed canisters appear on the engine console's Applications p - `__META_PROJECT` — the application name. Canisters that share the **same** value are grouped into one named app, so set an identical value on every canister of the app. - `__META_NAME` — the per-canister display label (e.g. `Backend`, `Frontend`). -- `__META_MAIN_CANISTER` — the literal string `"true"` on the single entry-point canister (usually the frontend/asset canister). That canister gets the "Open" button and the app's public URL. -- `__META_BASE_URL` — the app's public base, as an **absolute `https://` URL** (e.g. the frontend canister's URL `https://.icp.net`, or a custom domain). It is the app's "Open" link **and** the base that the icon path resolves against. Set it on the **main** canister. +- `__META_MAIN_CANISTER` — the literal string `"true"` on exactly one canister (the entry point, usually the frontend/asset canister). This marks the app's **main canister**: the console reads `__META_BASE_URL` and `__META_ICON_PATH` only from it, and the "Open" button targets it. +- `__META_BASE_URL` — an **absolute `https://` URL**, set on the main canister (e.g. the frontend canister's URL `https://.icp.net`, or a custom domain). When present and valid, it is the URL the "Open" button opens; when absent or not `https`, the "Open" button falls back to the main canister's gateway URL. It is also the base that `__META_ICON_PATH` resolves against. - `__META_ICON_PATH` — the path to the app icon, resolved against `__META_BASE_URL` to form the icon the console renders (e.g. `/favicon.svg` → `https:///favicon.svg`). Set it on the **main** canister, alongside `__META_BASE_URL`. The icon and "Open" link are read **only from the main canister** (the one marked `__META_MAIN_CANISTER: "true"`) — `__META_BASE_URL` / `__META_ICON_PATH` on any other canister are ignored. @@ -152,7 +152,7 @@ Notes: Icon specifics (the console builds the icon as `__META_BASE_URL` + `__META_ICON_PATH`): - **Both** must be present and **on the main canister** for an icon to appear — there is no fallback. `__META_ICON_PATH` alone does nothing. -- `__META_BASE_URL` must parse as an absolute **`https://`** URL. A bare host, an `http://` URL, or a `data:` / `javascript:` value is rejected, and then *neither* the icon *nor* the "Open" link renders. (The console validates the scheme before using it.) +- `__META_BASE_URL` must parse as an absolute **`https://`** URL. A bare host, an `http://` URL, or a `data:` / `javascript:` value is rejected: the icon then does not render, and the "Open" button falls back to the main canister's gateway URL (it does not disappear). (The console validates the scheme before using it.) - `__META_ICON_PATH` is a **path to an asset your frontend actually serves** (e.g. `/favicon.svg`), not an inline image. The resolved URL is rendered as an `` `src`, so it must return an image. Do **not** put a `data:` URI here: engine env values are length-capped (≤128 chars observed), so it would not fit, and the field is a path by design. - The frontend canister's id is only known **after** the first deploy. The usual flow is: deploy once, read the frontend canister id from the output, set `__META_BASE_URL` to `https://.icp.net` (and `__META_ICON_PATH`), then re-deploy to apply. If you control a custom domain for the app, you can set it up front instead. @@ -188,7 +188,7 @@ Report the deployed canister ids (and the frontend URL, if any) back to the user 6. **Skipping the app metadata.** Without `__META_PROJECT` (Step 2), the canisters still deploy and work but render as bare, unnamed principal rows in the console. Setting `__META_*` is what produces a named app with labelled canisters and an "Open" button. 7. **Wrong `__META_MAIN_CANISTER` value.** It is matched as the exact string `"true"`. A boolean, `"True"`, or marking more than one canister means no (or the wrong) "Open" button. Mark exactly one entry-point canister. 8. **Inventing an icon variable.** The icon variable is `__META_ICON_PATH` (a path resolved against `__META_BASE_URL`). Do not guess `__META_ICON`, `__META_LOGO`, or `__META_ICON_LINK` — they are ignored, so the icon silently never appears. -9. **Icon set on the wrong canister, or without a base URL.** The icon is read only from the **main** canister and needs **both** `__META_BASE_URL` (a valid absolute `https://` URL) and `__META_ICON_PATH`. Setting the icon path on a side canister, omitting the base URL, or giving a non-https / `data:` base means no icon renders (and a bad base URL also drops the "Open" link). +9. **Icon set on the wrong canister, or without a base URL.** The icon is read only from the **main** canister and needs **both** `__META_BASE_URL` (a valid absolute `https://` URL) and `__META_ICON_PATH`. Setting the icon path on a side canister, omitting the base URL, or giving a non-https / `data:` base means no icon renders. (The "Open" button still works — it falls back to the main canister's gateway URL — so a bad base URL costs the icon and the custom Open URL, not the button.) ## Related Skills