docs(internet-identity): document the ii-app-metadata document - #362
Conversation
Internet Identity now lets any app publish its own name, description and logo for the sign-in screens by serving /.well-known/ii-app-metadata — permissionlessly, superseding the curated list II ships for a handful of known apps. Adds a section covering which origin serves the document (the derivation origin, so alternative origins inherit it), the field rules that decide whether it is used at all, the raster-only same-origin logo requirements, and the CORS headers both resources need. The header snippet uses the static-site recipe's _headers file rather than the legacy asset canister's .ic-assets.json5, matching the adjacent alternative-origins section. Adds two pitfalls (wrong origin / missing CORS; whole-document invalidation and SVG logos), mentions the document in the skill description, and adds three eval cases plus a trigger query. Source: dfinity/developer-docs#347, normative rules from the App metadata section of dfinity/internet-identity docs/ii-spec.mdx. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qzx3V9y4eYVNjqnvFrhDHU
Skill Validation ReportValidating skill: /home/runner/work/icskills/icskills/skills/internet-identityStructure
Frontmatter
Tokens
Markdown
Tokens
Content Analysis
Contamination Analysis
Result: 2 warnings Project Checks |
There was a problem hiding this comment.
Pull request overview
Documents Internet Identity app metadata for customizing sign-in screens.
Changes:
- Adds metadata format, validation, origin, CORS, and logo guidance.
- Adds pitfalls and evaluation cases for common mistakes.
- Expands skill-trigger coverage.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
skills/internet-identity/SKILL.md |
Documents app metadata behavior and configuration. |
evaluations/internet-identity.json |
Adds output and trigger evaluations. |
Suppressed comments (1)
skills/internet-identity/SKILL.md:62
- This wording groups SVG rejection with document-field validation and can imply that an SVG invalidates all metadata. The MIME type is checked while fetching the resource, so per line 211 an SVG costs only the logo; it does not remove valid name/description. The “no metadata at all” statement also omits the curated fallback documented at line 227.
14. **Assuming a bad field in `ii-app-metadata` is just dropped, or pointing `logo` at an SVG.** One field that fails validation invalidates the **whole document** — the app is then shown with no metadata at all, not with the remaining fields. `logo` must be a raster image (`image/png`, `image/jpeg`, `image/webp`, `image/gif`, `image/avif`) on the **same origin** as the document; `image/svg+xml` is rejected outright. `name` is capped at 40 Unicode code points and `description` at 120, counted on the value as served.
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…ures Review feedback: the two pitfalls and one section bullet conflated a field that fails document validation with a logo asset that cannot be used. They are different failures — an unusable logo costs only the logo, while an invalid field discards the whole document — and "shown with no metadata" skipped the curated fallback II still ships for known apps. Also splits `logo` across both modes explicitly: a cross-origin URL fails document validation (whole document lost), while an SVG, an oversized image, or one that cannot be fetched costs only the logo. Sharpens the adversarial eval to test that distinction rather than assuming the bare-origin fallback: 5/5 with the skill, 0/5 baseline (was 4/4 vs 2/4). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qzx3V9y4eYVNjqnvFrhDHU
marc0olo
left a comment
There was a problem hiding this comment.
Reviewed this against two sources rather than the one in the description: the normative docs/ii-spec.mdx §App metadata, and the implementation in src/frontend/src/lib/utils/appMetadata.ts / lib/stores/app-metadata.store.ts on dfinity/internet-identity@main.
Verdict: sound, and accurate to an unusual degree. Every numeric and behavioural claim checks out against the code — the 40/120 code-point limits counted pre-normalization (validateTextField measures [...value].length before the \s+ collapse), the exact forbidden-character class, hasBalancedIsolates, the all-or-nothing INVALID short-circuit, APP_LOGO_CONTENT_TYPES, MAX_APP_LOGO_SIZE/MAX_APP_LOGO_DIMENSION/APP_LOGO_RENDER_SIZE, MAX_APP_METADATA_SIZE, redirect: "error", credentials: "omit", and APP_METADATA_FETCH_TIMEOUT_MILLIS. The _headers deviation from the docs PR is the right call for this repo — static-site pitfalls 1, 6, and 12 all back it, and Access-Control-Allow-Origin: * is valid here precisely because II omits credentials. The three Copilot findings were all real and correctly fixed in 1bc611e.
Worth calling out: the code check mattered
The document-vs-logo split this PR asserts is not settled by the spec text. ii-spec.mdx puts the same-origin requirement in the same bullet as the content-type/size/dimension requirements, then says a logo that "does not meet the asset requirements above costs the application only its logo" — which reads the other way. It also claims the published JSON Schema expresses every rule except isolate balancing, while the schema's logo is just {type: string, minLength: 1}, implying same-origin isn't document validation.
The implementation settles it in this PR's favour: validateLogoUrl returns the document-level INVALID sentinel for a cross-origin URL, and INVALID short-circuits the whole document in fetchAppMetadataFrom, whereas fetchLogoObjectUrl returns undefined and only drops metadata.logo. So the skill is right and the spec prose is the weaker of the two — probably worth a separate nit upstream on dfinity/internet-identity.
Two must-fixes and one content gap
See the inline comments on lines 229 and 210. Summary:
- The spec link 301s to the docs homepage, not the spec — and the validator's link check follows the redirect, sees 200, and passes, so CI can't catch it.
- The "no anchor yet" note in the description is stale;
#app-metadatais live. - The gateway-twin fallback isn't covered, which makes an absolute
logoURL a trap under this skill's own recommended derivation-origin pattern.
Non-blocking nits
Content-Type: application/jsonon the document is hygiene, not a requirement.fetchCappedsendsapplication/jsonas anAcceptheader only and never inspects the response's content-type; the body is decoded andJSON.parsed regardless. Only the logo's content-type is enforced. Setting it is still right and matches the block above — but that block says "II needs both", and a reader may carry the "needs" over. Only worth touching if you're editing that paragraph anyway.credentials: omitand the 10 s timeout live only in the implementation, not in the normative §App metadata. Fine to document; just the likeliest claims to drift.- Size advisory. The skill goes 510 → 555 lines and now trips the >500-line warning alongside the pre-existing token warning. Warnings are acceptable per
.claude/CLAUDE.mdand the check is green — flagging only as a signal that the next addition to this skill probably belongs inreferences/.
Evals
Structurally valid, 14 → 17 output evals, should_trigger 6 → 7. The adversarial case is a genuinely strong regression test after the Copilot round, and its oracle is correct against the implementation: an SVG logo passes validateLogoUrl (it's same-origin) and fails only at the content-type check in fetchLogoObjectUrl, so it really does cost only the logo — exactly what the oracle demands. Skipping a re-run of the untouched cases is correct per .claude/CLAUDE.md.
Everything other than the three items above is approve-worthy as written.
… logo trap Review feedback, both verified against dfinity/internet-identity@main: The spec link 301s to the docs homepage rather than the spec, and the validator's link check follows the redirect and sees 200, so CI cannot catch it. Switched to the docs.internetcomputer.org/<path> form used everywhere else under skills/, with the now-published #app-metadata anchor. II normalizes a canister gateway origin onto ic0.app and tries its icp0.io and icp.net twins in turn (appMetadataOrigins), and validateLogoUrl checks same-origin against whichever twin answered — returning the document-level INVALID sentinel, not just dropping the logo. So an absolute logo URL pinned to one gateway domain loses the whole document when a sibling served it, which the section's own recommended derivation origin makes likely. The logo bullet now says to write the URL relative and why, and pitfall 14 no longer reads as being only about third-party hosts. Adds an adversarial eval for it: 4/4 with the skill, 0/4 baseline — the baseline answers "yes, that's fine". Evals 15 and 16 re-checked, both still pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qzx3V9y4eYVNjqnvFrhDHU
marc0olo
left a comment
There was a problem hiding this comment.
Re-reviewed 05fbf01. All three items addressed, and the mechanism is stated accurately. I re-checked the new prose against dfinity/internet-identity@main rather than the description:
- Spec link →
docs.internetcomputer.org/references/internet-identity-spec/#app-metadata, verified 200 with the anchor live. Stale Notes paragraph gone. - Gateway twins — "normalizes onto
ic0.appand tries itsicp0.ioandicp.nettwins in turn" matchesGATEWAY_DOMAINS = ["ic0.app", "icp0.io", "icp.net"]exactly, order included; scoping it to a canister gateway origin is right, sincegatewayOriginTwinsreturns[]for custom domains and localhost; and "the same-origin check runs against whichever one answered" is right because the loop hands eachcandidatetofetchAppMetadataFrom, which passes that same origin intovalidateLogoUrl. Fixed in both places it needed to be — thelogobullet and pitfall 14. - New adversarial eval — 18 output evals, JSON valid, and oracle 1 hedging with "says no, or flags it as a real risk" is the right calibration, since whether it actually breaks depends on which gateway answers first. A baseline that opens with "Yes, that's fine" is exactly the failure the case exists to catch.
npm run validate on the branch: 29 skills, all passed, warnings only. Re-checking evals 15 and 16 was the right call, and I did not re-run them — the logo-bullet edit is purely additive and can only reinforce both oracles, so I'd have skipped it too.
One punctuation nit inline, non-blocking. Otherwise this is ready to merge.
…ash pair Review nit: the gateway aside landed after the closing em-dash, so the pair no longer bracketed the whole-document failure mode and separated "while" from the mode it contrasts against. Punctuation only, no wording change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qzx3V9y4eYVNjqnvFrhDHU
What
Documents Internet Identity's app metadata document (
/.well-known/ii-app-metadata) in theinternet-identityskill, based on dfinity/developer-docs#347, the normative "App metadata" section of the Internet Identity specification, and the implementation ondfinity/internet-identity@main.Any app can now publish its own name, description, and logo for the II sign-in screens — permissionlessly, superseding the curated list II ships for a handful of known apps.
Changes
skills/internet-identity/SKILL.mdlogorequirements: raster only (SVG rejected), same origin, ≤ 1 MiB, ≤ 4096 px/axis, re-encoded by II at ≤ 512 px — plus write the URL relative, because II may fetch the document from a sibling gateway domain of the same canister and the same-origin check runs against whichever one answered200, no redirects, no credentials, 10 s timeoutDeviation from the upstream docs PR: the docs PR configures CORS with
.ic-assets.json5. This skill targets the@dfinity/static-siterecipe, whose canister does not read that file (it isn't even uploaded), so the snippet uses the_headersfile — matching the adjacent alternative-origins section and thestatic-siteskill. The extension-less path also needs the bareContent-Type:form to getapplication/json.evaluations/internet-identity.json— four output evals (static-site file layout, adversarial SVG + partial-validation, which origin serves the document, adversarial absolute logo URL on a gateway domain) and one trigger query.Evals
All four added cases run with baseline; trigger evals re-run because the description changed.
Output evals — added cases (with skill vs baseline)
Trigger evals (description changed)
Evals 15 and 16 were re-checked after the logo-bullet edit in 05fbf01 (both still 5/5). The other pre-existing cases were not re-run — none of them cover content this PR touched.
npm run validatepasses — 29 skills, no errors. The skill trips the validator's "body > 500 lines" advisory (547 lines) alongside the pre-existing token advisory; per.claude/CLAUDE.mdwarnings are acceptable, and the next addition to this skill should go inreferences/.Review rounds
Copilot (fixed in 1bc611e) — three findings, all valid:
Pitfall 14 also now splits
logoacross both modes: a cross-origin URL fails document validation and takes the whole document down, while an SVG, an oversized image, or one that cannot be fetched costs only the logo.@marc0olo (fixed in 05fbf01) — two must-fixes, both verified against
dfinity/internet-identity@mainbefore applying:200, so CI could not catch it. Switched to thedocs.internetcomputer.org/<path>form used by every other IC-docs link underskills/, with the now-published#app-metadataanchor.logoURL a whole-document failure under this skill's own recommended derivation origin:appMetadataOriginstries theic0.app/icp0.io/icp.nettwins in turn, andvalidateLogoUrlchecks same-origin against whichever answered, returning the document-levelINVALID. The logo bullet now says to write the URL relative and why; pitfall 14 no longer reads as being only about third-party hosts. New adversarial eval: 4/4 with the skill, 0/4 baseline — the baseline opens with "Yes, that's fine".Non-blocking nits from that review are answered in the thread:
Content-Type: application/jsonon the document is hygiene rather than an II requirement (fetchCappedsends it asAcceptand never inspects the response), left as-is since the text does not mis-state it;credentials: omitand the 10 s timeout are implementation-only but load-bearing when debugging a fetch.