Skip to content

docs(custom-domains): name the ordering trap in the II pitfall - #341

Merged
marc0olo merged 1 commit into
mainfrom
docs/custom-domains-derivation-origin
Aug 10, 2026
Merged

docs(custom-domains): name the ordering trap in the II pitfall#341
marc0olo merged 1 commit into
mainfrom
docs/custom-domains-derivation-origin

Conversation

@yhabib

@yhabib yhabib commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Motivation

Pitfall 9 described the symptom — II principals depend on the origin, so a custom domain changes them — and pointed at internet-identity for the fix. It left out the part that decides whether the fix is still available: it has to happen before the domain collects sign-ins. Once accounts exist under the new domain, repointing it orphans them. That ordering is what the engine console now warns about on the add-domain form (dfinity/control-panel#3394).

Changes

  • Rewrote Pitfall 9 to lead with the case that makes it expensive, an app that already has users.
  • Named the canister address as the origin to pin, so the reader does not have to pick.
  • Added the ordering constraint and that it cannot be undone.
  • Dropped the "switching from a canister URL to a custom domain" framing — nothing is switched, the canister address stays reachable, which is why it is the right derivation origin.
  • Added one eval case.
Eval 9 — Adversarial: custom domain on an app that already has II users (WITH 4/4 | WITHOUT 2/4)
━━━ Adversarial: custom domain on an app that already has II users ━━━

  WITH skill: 4/4 passed
    ✅ Warns that II principals are per-origin, so existing users would get brand-new accounts at the custom domain
    ✅ Says to pin the canister address as the derivation origin and list the custom domain in .well-known/ii-alternative-origins BEFORE the domain goes live
    ✅ Notes that a domain which has already collected sign-ins cannot be repointed without orphaning those accounts
    ✅ Does NOT present the custom domain switch as principal-neutral or a pure DNS change

  WITHOUT skill: 2/4 passed
    ✅ Warns that II principals are per-origin, so existing users would get brand-new accounts at the custom domain
    ❌ Says to pin the canister address as the derivation origin and list the custom domain in .well-known/ii-alternative-origins BEFORE the domain goes live
       → The output mentions setting up ii-alternative-origins before switching but never mentions configuring derivationOrigin in the app's login call to pin the canister address, which is the other required half of the mechanism.
    ❌ Notes that a domain which has already collected sign-ins cannot be repointed without orphaning those accounts
       → The output only advises keeping the old canister URL live alongside the new domain; it never states the general principle that a domain with existing sign-ins can't be repointed elsewhere without orphaning those accounts.
    ✅ Does NOT present the custom domain switch as principal-neutral or a pure DNS change

The baseline knows principals are per-origin but gives half the fix — ii-alternative-origins without derivationOrigin — which on its own does nothing.


Prev. #339

Pitfall 9 described the symptom (II principals change with the origin)
and pointed at internet-identity for the fix, but omitted the part that
decides whether the fix is still available: it has to happen before the
domain collects sign-ins. Once accounts exist under the new domain,
repointing it at a derivation origin orphans them.

Also names the canister address as the origin to pin, so the reader is
not left to pick, and drops the "switching from a canister URL" framing —
the canister address does not go away when a custom domain is added.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@yhabib
yhabib requested review from a team and JoshDFN as code owners August 7, 2026 16:53
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Skill Validation Report

Validating skill: /home/runner/work/icskills/icskills/skills/custom-domains

Structure

  • Pass: SKILL.md found

Frontmatter

  • Pass: name: "custom-domains" (valid)
  • Pass: description: (652 chars)
  • Pass: license: "Apache-2.0"
  • Pass: compatibility: (45 chars)
  • Pass: metadata: (2 entries)

Markdown

  • Pass: no unclosed code fences found

Tokens

File Tokens
SKILL.md body 2,684
Total 2,684

Content Analysis

Metric Value
Word count 1,511
Code block ratio 0.19
Imperative ratio 0.15
Information density 0.17
Instruction specificity 0.81
Sections 15
List items 23
Code blocks 13

Contamination Analysis

Metric Value
Contamination level low
Contamination score 0.20
Primary language category shell
Scope breadth 4
  • Warning: Language mismatch: config, javascript (2 categories differ from primary)

Result: passed

Project Checks


✓ Project checks passed for 1 skills (0 warnings)

marc0olo added a commit that referenced this pull request Aug 10, 2026
… origin (#339)

# Motivation

The skill only mentioned `derivationOrigin` and `ii-alternative-origins`
in Pitfall 8, to say don't use them. That is right for the `icp0.io` vs
`ic0.app` case it covers, but it was the whole coverage, so an agent
adding a custom domain to an II app found a prohibition and no recipe.
`custom-domains` Pitfall 9 already pointed here for one. The engine
console started surfacing this in dfinity/control-panel#3394 and
dfinity/control-panel#3398.

# Changes

- Added a "Serving an app at more than one origin" section: pick the
canister address as the derivation origin, set `derivationOrigin` on the
alternative origin only, and serve `.well-known/ii-alternative-origins`
from the derivation origin's canister.
- Added the `_headers` block the `@dfinity/static-site` recipe needs.
The [official
docs](https://docs.internetcomputer.org/building-apps/authentication/alternative-origins)
say to use `.ic-assets.json5`, which is the legacy asset canister's
config — static-site does not read or upload it. The file also has no
extension, so it will not serve as `application/json` without a
`_headers` entry, and certified-assets sets no CORS header by default.
- Scoped Pitfall 8 to the `icp0.io`/`ic0.app` case and pointed it at the
new section.
- Added one eval case. Dropped a second one I drafted ("which origin
gets `derivationOrigin`") because the baseline scored 3/3 on it — it
tested common knowledge, not the skill.

<details>
<summary>Eval 14 — Adversarial: alternative origins on the static-site
recipe (WITH 3/3 | WITHOUT 2/3)</summary>

```
━━━ Adversarial: alternative origins on the static-site recipe ━━━

  WITH skill: 3/3 passed
    ✅ Serves .well-known/ii-alternative-origins containing {"alternativeOrigins": [...]}
    ✅ Adds a _headers entry setting Content-Type: application/json AND Access-Control-Allow-Origin: * for /.well-known/ii-alternative-origins
    ✅ Does NOT create a .ic-assets.json5 file

  WITHOUT skill: 2/3 passed
    ✅ Serves .well-known/ii-alternative-origins containing {"alternativeOrigins": [...]}
    ❌ Adds a _headers entry setting Content-Type: application/json AND Access-Control-Allow-Origin: * for /.well-known/ii-alternative-origins
       → No _headers file or entry is included anywhere in the output.
    ✅ Does NOT create a .ic-assets.json5 file
```

The baseline writes the JSON file and stops, which leaves it served with
the wrong content type and no CORS header.

</details>

---

Next #340, #341

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Marco Walz <marco.walz@dfinity.org>
@marc0olo
marc0olo merged commit 5ee70ad into main Aug 10, 2026
6 checks passed
@marc0olo
marc0olo deleted the docs/custom-domains-derivation-origin branch August 10, 2026 10:39
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.

2 participants