Skip to content

Add host mode routing from realm.json#4709

Merged
backspace merged 31 commits into
mainfrom
routing-from-config-cs-10054
May 20, 2026
Merged

Add host mode routing from realm.json#4709
backspace merged 31 commits into
mainfrom
routing-from-config-cs-10054

Conversation

@backspace
Copy link
Copy Markdown
Contributor

@backspace backspace commented May 7, 2026

With this branch deployed to staging, when a realm config has hostRoutingRules:

CleanShot 2026-05-20 at 11 23 57@2x

The published realm resolves custom routes:

CleanShot 2026-05-20 at 11 24 22@2x

backspace and others added 2 commits May 7, 2026 09:42
CS-10054: assert getHostRoutingMap returns [{ path, id }] pairs read
from the indexed RealmConfig card. The test fails today (method does
not exist) and pins the desired shape for the implementation pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Preview deployments

Host Test Results

    1 files  ±0      1 suites  ±0   1h 47m 36s ⏱️ + 13m 38s
2 712 tests ±0  2 697 ✅ ±0  15 💤 ±0  0 ❌ ±0 
2 731 runs  ±0  2 716 ✅ ±0  15 💤 ±0  0 ❌ ±0 

Results for commit 0d7b009. ± Comparison against earlier commit 60aa1ba.

Realm Server Test Results

    1 files  ±0      1 suites  ±0   8m 18s ⏱️ -1s
1 454 tests ±0  1 454 ✅ ±0  0 💤 ±0  0 ❌ ±0 
1 545 runs  ±0  1 545 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 0d7b009. ± Comparison against earlier commit 60aa1ba.

@backspace backspace marked this pull request as ready for review May 19, 2026 21:49
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8e42fa0b0c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/realm-server/handlers/serve-index.ts
Comment thread packages/base/realm-config.gts Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds “host mode” routing based on hostRoutingRules stored in a realm’s realm.json (RealmConfig card), allowing published realms to serve custom bare paths (e.g. /whitepaper) that render a target card both server-side (SSR head/isolated/scoped CSS) and client-side (SPA hydration/navigation).

Changes:

  • Add Realm.getHostRoutingMap() to read routing rules from the indexed RealmConfig card and return a {path,id} map.
  • Update realm-server serve-index to (a) rewrite SSR fetch target based on a matched routing rule and (b) inject hostRoutingMap into the host environment meta tag per request.
  • Add unit + e2e coverage for routing behavior and adjust host app to resolve routed paths before building the default card URL.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/runtime-common/realm.ts Adds getHostRoutingMap() to read and validate routing rules from the indexed RealmConfig card.
packages/realm-server/handlers/serve-index.ts Applies routing rules for SSR rendering and injects hostRoutingMap into the environment meta tag; tweaks meta-tag rewrite regex.
packages/host/config/environment.js Adds default hostRoutingMap to the host config surface for realm-server injection.
packages/host/app/services/host-mode-service.ts Exposes hostRoutingMap from config and adds resolveRoutedPath() helper.
packages/host/app/routes/index.gts Uses routing resolution to pick the routed target card ID before default URL construction in host mode.
packages/base/realm-config.gts Changes routing rule instance field to a flat string (instead of a JSON:API relationship) so it indexes as attributes.
packages/realm-server/tests/realm-routing-test.ts Adds a QUnit test covering Realm.getHostRoutingMap() behavior.
packages/realm-server/tests/index.ts Registers the new routing test in the realm-server test suite.
packages/matrix/tests/host-mode.spec.ts Adds an end-to-end test that publishes routing rules and verifies bare-path resolution renders the target card in host mode.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/runtime-common/realm.ts
Comment thread packages/runtime-common/realm.ts Outdated
Comment thread packages/realm-server/handlers/serve-index.ts Outdated
Comment thread packages/realm-server/handlers/serve-index.ts Outdated
Comment thread packages/host/app/services/host-mode-service.ts
Comment thread packages/realm-server/tests/realm-routing-test.ts Outdated
backspace and others added 2 commits May 20, 2026 05:15
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@backspace backspace marked this pull request as draft May 20, 2026 12:23
backspace and others added 6 commits May 20, 2026 07:51
The project spec restricts routing rules to cards within the same
realm, so the non-URL reference forms accepted by resolveRRI
(`$REALM/...`, `@cardstack/<package>/...`) are over-engineering
here. `getHostRoutingMap` now resolves via plain
`new URL(instance, realmBase)` and returns absolute URLs only,
matching the project doc's flat `{ "/path": "id" }` example shape.

Same-realm guard drops to a plain `id.startsWith(this.url)` since
both sides are URL form; same end behavior, smaller surface.

Unit test trimmed to the two cases that exercise the now-supported
input forms: a relative ref (resolved to URL) and a cross-realm
absolute URL (dropped by the guard).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`new URL(instance, realmBase).href` already normalizes dot-segments,
so the obvious `https://host/realm/../private/card` bypass doesn't
actually fire here — but plain `startsWith` is still fragile around
trailing-slash variance and neighbouring-prefix realms
(`/realm-evil/`). `RealmPaths.inRealm` handles both correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the handler called findOrMountRealm twice on the hot HTML
path — once inside hasPublicPermissions(cardURL, ...) and once again
in the routing-map block. Both can fall back to a DB probe when the
in-memory registry is cold.

Resolve the realm once at the top and pass it directly into the
permissions check. hasPublicPermissions now accepts a Realm | undefined
(undefined → false, preserving the prior "no realm = no public read"
semantics) instead of doing its own realm lookup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous comment said `path` was "within the realm", but both
sides of the comparison are host-relative paths: the server prefixes
each rule's path with the realm's mount pathname before injecting,
and the route passes Ember's `/*path` catch-all value.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lukemelia lukemelia marked this pull request as ready for review May 20, 2026 15:42
The contains(StringField) experiment got the realm.json file flatter
(no relationships split) but lost typed linking, broken-link
visibility, and parity with how the rest of the system addresses
cards. The flat shape is recoverable in tooling; the lost type
information is not. Switching back.

getHostRoutingMap now reads searchDoc.hostRoutingRules[i].instance.id
— the flattened searchDoc form that linksTo produces. No
relative-URL resolution needed at read time; ids arrive as absolute
URLs. The RealmPaths.inRealm same-realm guard stays unchanged.

Test fixtures (realm-routing-test, host-mode.spec, local routing
realm) move the link target from `instance: './foo'` in attributes
to `relationships['hostRoutingRules.<i>.instance'].links.self`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@lukemelia lukemelia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we may want to cache the host routing map, but looks good for now.

@backspace
Copy link
Copy Markdown
Contributor Author

Seems like we may want to cache the host routing map, but looks good for now.

👍🏻 recorded as CS-11206, thanks

@backspace backspace merged commit 3c01ee3 into main May 20, 2026
97 of 98 checks passed
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