Skip to content

docs: cover the ii-app-metadata well-known file in the II guide - #347

Merged
marc0olo merged 3 commits into
mainfrom
docs/guides-authentication-ii-app-metadata
Aug 24, 2026
Merged

docs: cover the ii-app-metadata well-known file in the II guide#347
marc0olo merged 3 commits into
mainfrom
docs/guides-authentication-ii-app-metadata

Conversation

@aterga

@aterga aterga commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds an App metadata section to docs/guides/authentication/internet-identity.mdx, between Alternative origins and Common mistakes, covering the /.well-known/ii-app-metadata document that lets an app supply its own name, description, and logo for the Internet Identity sign-in screens. Nothing in the guide covered it: until now the only way to get branded screens was to be in the curated list shipped inside II, which this mechanism replaces.
  • Placement is deliberate. The section reuses the derivation origin the reader has just configured in Alternative origins (the document is fetched from the origin identities are derived for, so an app publishes it once and all of its alternative origins present the same way), and it extends the same .ic-assets.json5 with CORS entries for the document and the logo.
  • Content covers what an integrator has to get right: the field limits (40 / 120 code points), the raster-only logo rules (same origin, no SVG, 1 MiB, 4096 px per side, re-encoded by II at up to 512 px), the 8 KiB document cap, 200 with no redirect, and the 10 second budget. It also states that a single invalid field drops the whole document and that II names the offending field in the browser console, since that is the first thing someone debugging missing metadata needs to know.
  • Ends with a note that the metadata is exactly as trustworthy as the origin serving it and verifies nothing about the app, which is why II keeps the origin on screen next to it.
  • Links the specification by anchor (#app-metadata) rather than at the page root.

Scope

This PR is now a documentation change only: docs/guides/authentication/internet-identity.mdx, +56 / -1.

Earlier revisions of this branch also carried a bump of .sources/internetidentity to release-2026-08-21 and the regenerated spec files, because the mirrored specification did not yet have the section this guide links to. #350 has since landed the identical sync on main, so rebasing dropped those changes as already applied. The anchor works against main as it stands.

docs/references/internet-identity-spec.md on main now carries the App metadata section and its JSON Schema, and states the alternative origins limit as 100 (upstream dfinity/internet-identity#4221 and #4261, both in release-2026-08-21).

Relationship to #349

#349 (merged) raised the same limit in the guide prose, on line 572. This branch inserts a section further down, so the two do not overlap. With #350 also merged, the guide and the specification now agree on 100.

Structural decisions

  • Heading level. Added as an ## section so it sits as a sibling of Alternative origins and Common mistakes rather than nesting under either. The mechanism is independent of alternative origins: apps that never set a derivationOrigin use it too.

Verification

  • Rebased onto main (b21a283); the branch is two commits and merges cleanly.
  • npm run build passes from a clean dist/ (exit 0, 210 pages), and the built dist/references/internet-identity-spec/index.html contains id="app-metadata", so the guide's anchor resolves against main's mirrored spec.
  • node scripts/validate.js --all: no errors in the changed file. The 4 reported errors are pre-existing, in docs/guides/digital-assets/chain-key-tokens.mdx and docs/guides/backends/data-persistence.mdx, both untouched here.
  • Limits and behaviour in the guide text were taken from the merged implementation (src/frontend/src/lib/utils/appMetadata.ts in dfinity/internet-identity), not from memory, and match the mirrored spec section now on main.
  • The :::note in the new section renders as a plain div rather than a styled callout. So does the page's pre-existing note under Alternative origins: the built page contains no starlight-aside elements at all, so this is existing site behaviour and not something this change introduces. Left as is to match the page; happy to open a separate issue if the directive handling should be fixed.

claude added 2 commits August 24, 2026 13:17
Apps can now provide their own name, description, and logo for the
Internet Identity sign-in screens by serving /.well-known/ii-app-metadata
on the origin their identities are derived for, replacing the curated list
that used to be the only way to get branded. Nothing in the guide covered
it.

The new section sits between Alternative origins and Common mistakes,
which is where it belongs: it reuses the derivation origin the reader has
just configured, and extends the same .ic-assets.json5 with CORS entries
for the document and the logo. It carries the limits an integrator has to
respect (field lengths, the raster-only logo rules, the document cap), the
fact that one invalid field drops the whole document and why the browser
console is the place to look, and the reminder that this metadata proves
nothing about an app's identity, which is why the origin stays on screen
next to it.
main now mirrors the App metadata section (#350 synced
.sources/internetidentity to release-2026-08-21), so the guide can point
at #app-metadata instead of the specification page root.

This commit originally carried the submodule bump and the regenerated
spec files. The rebase dropped them: #350 landed the identical sync
first, so only the anchor change remains.
@aterga
aterga force-pushed the docs/guides-authentication-ii-app-metadata branch from 521a246 to bb5bdc4 Compare August 24, 2026 13:21
@aterga
aterga marked this pull request as ready for review August 24, 2026 13:33
@aterga
aterga requested review from a team as code owners August 24, 2026 13:33
@marc0olo

Copy link
Copy Markdown
Member

Review: App metadata section (docs/guides/authentication/internet-identity.mdx)

Must fix

  • Logo-failure semantics are misstated: the guide bundles the logo requirements right after "A field that fails validation invalidates the whole document," which implies a bad logo (wrong content-type, oversized, fails to decode, transient fetch error) also wipes out the name/description. That's not what happens. Per the spec (internet-identity-spec.md:366, synced from upstream) and the actual implementation (appMetadata.ts, fetchAppMetadata): the logo field's URL/same-origin shape is part of document-level validation, but once that passes, an asset-level failure (bad content-type, oversized image, decode failure, transient network error) drops only the logo — name and description still render. The implementation comment says so directly: "Losing just the logo is the better outcome here: the name and description still render." As written, a developer debugging an intermittent logo 500 would wrongly conclude their whole metadata document got rejected. Suggest adding a clause after the logo bullet, e.g.: "Unlike the name/description fields, a logo that fails to fetch or decode only costs the logo — the rest of the document still applies."

Suggestions

  • "By default... origin alone" / "falls back to showing your origin, exactly as they do without it": the spec notes II still falls back to a small legacy curated metadata list (the mechanism this feature supersedes) for apps that were in it, before falling back to origin-only. Not relevant for new integrators, but "exactly as they do without it" is a slightly stronger claim than the spec supports. Not blocking.
  • Minor Unicode omission: the guide says "control characters" are rejected, but the schema also explicitly blocks U+FEFF (BOM/ZWNBSP), and requires bidirectional isolates to be balanced. Both are edge cases already covered by the linked JSON schema, so this is optional polish.

Verified

  • Cross-checked every numeric/behavioral claim (40/120 char limits, code-point counting, whitespace collapsing, 1 MiB / 4096px / 512px re-encode, 8 KiB cap, 200-only, no redirect, no-credentials fetch, 10s timeout) directly against dfinity/internet-identity's src/frontend/src/lib/utils/appMetadata.ts at the pinned submodule commit — all correct, including the 10s timeout and credentials: "omit", which aren't spelled out in the spec doc but match the real implementation as the PR description claims.
  • Anchor link ../../references/internet-identity-spec.md#app-metadata verified against the actual heading id in the built site (built the exact current PR head with npm run build: 210 pages, no errors; confirmed <a href="/references/internet-identity-spec/#app-metadata"> resolves to the real id="app-metadata" heading).
  • #alternative-origins same-page anchor and the extended .ic-assets.json5 example are consistent with the existing Alternative origins section's pattern.
  • Heading level (##, sibling of Alternative origins / Common mistakes) is structurally correct.
  • Brand voice: no em-dashes, no banned vocabulary, sentence case.
  • Relationship to docs: raise alternative origins limit to 100 #349 (raise limit to 100) confirmed non-overlapping; chore: sync II spec to dfinity/internet-identity release-2026-08-21 #350 (spec sync) confirmed merged, and the spec now contains the App metadata section this PR links to.
  • Upstream: comment update is accurate.

Review feedback on #347.

The bullet stating that a failed field invalidates the whole document sat
directly above the logo requirements, which read as though a logo that
fails to fetch or decode takes the name and description down with it. It
does not: only the shape of `logo` is validated with the rest of the
document, and an asset-level failure costs the logo alone. Someone
debugging an intermittent 500 on their logo would have drawn the wrong
conclusion.

Also from the review, both optional:

- `U+FEFF` is rejected alongside the control characters, and isolates
  must be balanced.
- A missing or invalid document falls back to the curated entry II still
  ships for a small list of apps before falling back to the origin alone,
  so "exactly as they do without it" overstated it.

aterga commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Feedback addressed in f911521. All three points taken, including both marked optional.

  • Logo failure semantics (must fix): you are right, and the bullet order made it worse. Added a bullet directly after the logo requirements saying that only the shape of logo (a non-empty URL on the document's own origin) is validated with the rest of the document, and that once that passes, a logo which cannot be fetched or decoded, or which breaks the content type, size or dimension rules, costs the logo alone while the name and description still render. It also says why the two are treated differently: fetching a second resource can fail transiently, unlike a mistake in the document.
  • Curated fallback: reworded. A missing or invalid document now falls back to "the curated entry II still ships for a small list of apps (the mechanism this file supersedes), and to showing your origin alone otherwise", instead of claiming the screens behave exactly as without the file.
  • Unicode omissions: U+FEFF is now listed alongside the control characters, and the isolates sentence carries the balance requirement ("provided every isolate a field opens it also closes").

npm run build passes from a clean dist/ (exit 0, 210 pages). No em-dashes added.

Thanks for cross-checking the numbers against appMetadata.ts rather than the spec prose alone. On that note, one thing you may want to know: the same implementation has a bug where the metadata of an app served only on icp0.io or icp.net is never found, because the origin is remapped to its ic0.app twin before the fetch and that twin can answer 400 client_domain_canister_mismatch. Fix proposed in dfinity/internet-identity#4281. It does not change anything documented on this page.


Generated by Claude Code

@marc0olo
marc0olo merged commit 1a2e942 into main Aug 24, 2026
7 checks passed
@marc0olo
marc0olo deleted the docs/guides-authentication-ii-app-metadata branch August 24, 2026 14:44
aterga added a commit to dfinity/icskills that referenced this pull request Aug 25, 2026
## What

Documents Internet Identity's **app metadata** document
(`/.well-known/ii-app-metadata`) in the `internet-identity` skill, based
on
[dfinity/developer-docs#347](dfinity/developer-docs#347)
and the normative "App metadata" section of `docs/ii-spec.mdx` in
`dfinity/internet-identity`.

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.md`**

- New section **"Showing your app's name, description, and logo on the
sign-in screen"**, placed after "Serving an app at more than one origin"
since the two interact:
- the document shape, and that II reads it from the **derivation
origin** (so alternative origins inherit it — no per-origin copy)
- the field rules that decide whether the document is used at all:
40/120 code-point limits, at least one visible character, rejected
control/bidi-override characters, balanced isolates, and **one bad field
invalidating the whole document**
- `logo` requirements: raster only (SVG rejected), same origin, ≤ 1 MiB,
≤ 4096 px/axis, re-encoded by II at ≤ 512 px — and the split between
failures that lose the whole document and failures that cost only the
logo
- transport rules: ≤ 8 KiB, `200`, no redirects, no credentials, 10 s
timeout
- CORS headers for **both** the document and the logo, which fail
differently
- Two new pitfalls in "Mistakes That Break Your Build": serving the
document on the wrong origin or without CORS (13), and expecting a bad
field to be dropped / confusing a rejected logo with a rejected document
(14).
- Description mentions the document so queries about the sign-in
screen's name and logo route here.

**Deviation from the upstream docs PR:** the docs PR configures CORS
with `.ic-assets.json5`. This skill targets the `@dfinity/static-site`
recipe, whose canister does not read that file (it isn't even uploaded),
so the snippet uses the `_headers` file — matching the adjacent
alternative-origins section and the `static-site` skill. The
extension-less path also needs the bare `Content-Type:` form to get
`application/json`.

**`evaluations/internet-identity.json`** — three output evals
(static-site file layout, adversarial SVG + partial-validation, which
origin serves the document) and one trigger query.

## Evals

All three added cases run with baseline; trigger evals re-run because
the description changed.

<details>
<summary>Output evals — added cases (with skill vs baseline)</summary>

```
━━━ App metadata document on the static-site recipe ━━━

  WITH skill: 5/5 passed
    ✅ Serves a JSON document at .well-known/ii-app-metadata with name, description, and/or logo fields
    ✅ Adds a _headers entry setting Access-Control-Allow-Origin: * for /.well-known/ii-app-metadata AND for the logo asset
    ✅ Sets Content-Type: application/json for /.well-known/ii-app-metadata (the file has no extension)
    ✅ Uses a raster logo (PNG/JPEG/WebP/GIF/AVIF) on the same origin as the document — not an SVG and not a cross-origin URL
    ✅ Does NOT create a .ic-assets.json5 file

  WITHOUT skill: 1/5 passed
    ❌ Serves a JSON document at .well-known/ii-app-metadata with name, description, and/or logo fields
       → The assistant produced no files at all, only a request for WebFetch/WebSearch permission.
    ❌ Adds a _headers entry setting Access-Control-Allow-Origin: * for /.well-known/ii-app-metadata AND for the logo asset
       → No _headers file or any file content was output.
    ❌ Sets Content-Type: application/json for /.well-known/ii-app-metadata (the file has no extension)
       → No headers configuration was provided since no files were output.
    ❌ Uses a raster logo (PNG/JPEG/WebP/GIF/AVIF) on the same origin as the document
       → No logo file or reference to one was provided in the output.
    ✅ Does NOT create a .ic-assets.json5 file

━━━ Adversarial: SVG logo and partial validation of ii-app-metadata ━━━
(oracle sharpened after review — see the review thread on evaluations/internet-identity.json)

  WITH skill: 5/5 passed
    ✅ Says no — the over-long description is a field that fails validation, and one invalid field invalidates the WHOLE document rather than being dropped on its own
    ✅ States the description limit is 120 Unicode code points, so a 200-character description fails validation
    ✅ States that image/svg+xml is not accepted — the logo must be a raster image
    ✅ Distinguishes the two failure modes: an SVG logo on its own costs only the logo, so it is the description — not the SVG — that takes the whole document down
    ✅ Does NOT claim sign-in itself breaks, and does not assert the screen necessarily shows the bare origin

  WITHOUT skill: 0/5 passed
    ❌ Says no — description failure invalidates the whole document rather than being dropped alone
       → The claim is framed throughout as unverified speculation ('likely', 'best recollection') rather than a confident, correct statement of fact.
    ❌ States the description limit is 120 Unicode code points
       → No specific numeric limit is ever given anywhere in the output.
    ❌ States that image/svg+xml is not accepted and raster formats are required
       → The rejection of SVG is presented as a speculative guess and only PNG/JPEG are mentioned, not the full accepted raster set.
    ❌ Distinguishes the two failure modes
       → The output instead claims the SVG issue 'alone being sufficient to break' all branding, conflating it with the whole-document failure.
    ❌ Does not claim sign-in breaks or that the screen necessarily shows the bare origin
       → The output asserts the bare-origin fallback without mentioning the curated-entry fallback for known apps.

━━━ Which origin serves ii-app-metadata with alternative origins ━━━

  WITH skill: 3/3 passed
    ✅ Says to publish it only on the derivation origin, because II fetches the document from the origin identities are derived for
    ✅ Explains that the alternative origins that derivation origin lists are then presented with the same name, description, and logo — no per-origin copy to keep in sync
    ✅ Does NOT tell the user to publish a copy on each alternative origin

  WITHOUT skill: 0/3 passed
    ❌ Says to publish it only on the derivation origin
       → The output instructs publishing on both origins and claims II reads from the relying party origin.
    ❌ Explains that alternative origins inherit the same metadata
       → The output instead describes two separate documents serving different purposes.
    ❌ Does NOT tell the user to publish a copy on each alternative origin
       → The output explicitly tells the user to publish the metadata file on both origins.

━━━ Summary ━━━
    App metadata document on the static-site recipe:                  WITH 5/5 | WITHOUT 1/5
    Adversarial: SVG logo and partial validation of ii-app-metadata:  WITH 5/5 | WITHOUT 0/5
    Which origin serves ii-app-metadata with alternative origins:     WITH 3/3 | WITHOUT 0/3
```

</details>

<details>
<summary>Trigger evals (description changed)</summary>

```
  Should trigger: 7/7 correct
    ✅ "Add login to my ICP frontend app"
    ✅ "How do I integrate Internet Identity?"
    ✅ "I need passkey authentication for my dapp"
    ✅ "Set up sign-in with Internet Identity"
    ✅ "How does delegation work with II?"
    ✅ "Add auth to my canister frontend"
    ✅ "How do I get my app's name and logo to show on the Internet Identity sign-in screen?"

  Should NOT trigger: 6/6 correct
    ✅ "Connect a wallet to my dapp"
    ✅ "How do I deploy my canister?"
    ✅ "Implement ICRC-1 token transfers"
    ✅ "Set up inter-canister calls"
    ✅ "How does stable memory work?"
    ✅ "Add a frontend to my canister"

  Trigger evals: should-trigger 7/7 | should-not-trigger 6/6
```

</details>

Existing eval cases were not re-run: none of them cover content this PR
touched (the alternative-origins case, #14, is unchanged and its section
was not edited).

`npm run validate` passes — 29 skills, no errors. The skill now trips
the validator's "body > 500 lines" advisory warning (547 lines); the
token-count advisory was already tripped before this change.

## Review round (1bc611e)

Three findings from the Copilot review, all valid and all fixed:

- Pitfall 13 conflated the two CORS failure modes. An unreadable
**document** means none of your metadata is used; an unreadable **logo**
costs only the logo.
- "The app is shown with no metadata" skipped the curated fallback. Now:
none of *your document* is applied, then the fallback order (curated
entry for a known app, origin alone otherwise).
- The adversarial eval's oracle did not test the distinction the section
introduces. Sharpened, and the delta improved from 4/4 vs 2/4 to **5/5
vs 0/5**.

Pitfall 14 also now splits `logo` across both modes explicitly: 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.

## Notes

- The link to the II specification points at the spec page without an
anchor: the App metadata section is on `main` in
`dfinity/internet-identity` but has not yet been published to the docs
site, so an `#app-metadata` fragment would currently be dead.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants