Skip to content

feat: add hlx6 support for api.aem.live content routing - #242

Draft
kptdobe wants to merge 29 commits into
mainfrom
feat/hlx6-support
Draft

feat: add hlx6 support for api.aem.live content routing#242
kptdobe wants to merge 29 commits into
mainfrom
feat/hlx6-support

Conversation

@kptdobe

@kptdobe kptdobe commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Description

This change routes source GET, HEAD, and POST requests to api.aem.live for source-bus sites.
Legacy sites continue through the da-admin service binding.

Each source operation sends an unauthenticated GET to admin.hlx.page/ping/{org}/{site}.
The x-api-upgrade-available: true header selects the source bus.
The worker does not cache the result.
helix-admin#3687 sets this header for source-bus sites.
It checks whether content.source.url starts with https://api.aem.live/.
helix-admin#3689 added the /ping route.

If the probe fails, reads use da-admin.
If the probe fails before a write, the worker sends parallel HEAD requests to both backends.
The backend that answers 200 wins.
Two 404 responses select da-admin for a new page.
Two 200 responses select api.aem.live and log a warning.
A rejected HEAD request selects da-admin.

Legacy sites are the majority and do not need admin.hlx.page.
Failing them closed on a ping error would cause the larger outage.

Source responses with 401, 403, or 5xx pass through unchanged.
Only 404 uses the new-page template.

Related Issue

Motivation and Context

Helix 6 stores media in the source bus until preview.
The preview proxy had no source-bus read path, so source-bus media returned 404.
DA Live uses the preview domain to keep IMS credentials out of page scripts.

Known limits

  • Each source request adds a /ping round trip.
  • Measured from Frankfurt: 0.483s, 0.498s, 0.519s.
  • The response has cache-control: no-store, private, must-revalidate, so CDN caching does not help.
  • The endpoint advertises x-ratelimit-limit: 10.
  • Source-bus sites still load site config from da-admin through getSiteConfig.
  • An unlikely DA to hlx6 source change between a GET and its POST is not handled in this PR. Closing that race needs coordinated conditional-write support across da-live, da-admin, and this worker.

How Has This Been Tested?

  • Unit: 236 tests pass, and npm run lint reports no errors.
  • Before on main: the preview image request returned 404 with 398 bytes.
  • After on hlx6-ping: local preview returned 200 with 3,669,975 bytes.
  • The local payload matched the direct source response by SHA-256.
  • admin.hlx.page/ping/benpeter/hlx6-test returned 200 with x-api-upgrade-available: true.
  • Authenticated source GET for benpeter/hlx6-test/index.html returned 200 with 150 bytes.

Screenshots

Not applicable.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature which changes existing functionality)

Checklist

  • I have signed the Adobe Open Source CLA.
  • My code follows the code style of this project.
  • My change requires a documentation change.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@tripodsan tripodsan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

wrong test

Comment thread src/routes/da-admin.js Outdated
const cached = hlx6Cache.get(key);
if (cached !== undefined) return cached;

const resp = await fetch(aemApiSourceUrl(org, site, '/'), {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

no, this is the wrong "ping". every helix5 project is automatically a helix6 project....

you need to request: https://admin.hlx.page/ping/{org}/{site} and then check if the

x-api-upgrade-available header is true

@tripodsan tripodsan Jun 30, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

and even then, it is not guaranteed that the respective site has the source bus configured.
we probably need a better way to detect this.

@bosschaert what about returning 405 for /{org}/sites/{site}/source/ if the content source is not correct?

The HTTP 405 Method Not Allowed client error response status code indicates that the server knows the request method, but the target resource doesn't support this method.

if we would implement this, you're check would also need to allow 404

@tripodsan tripodsan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

wrong check

Comment thread src/routes/da-admin.js Outdated
const cached = hlx6Cache.get(key);
if (cached !== undefined) return cached;

const resp = await fetch(aemApiSourceUrl(org, site, '/'), {

@tripodsan tripodsan Jun 30, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

and even then, it is not guaranteed that the respective site has the source bus configured.
we probably need a better way to detect this.

@bosschaert what about returning 405 for /{org}/sites/{site}/source/ if the content source is not correct?

The HTTP 405 Method Not Allowed client error response status code indicates that the server knows the request method, but the target resource doesn't support this method.

if we would implement this, you're check would also need to allow 404

@andreituicu

Copy link
Copy Markdown
Contributor

we probably need a better way to detect this.

Would it not be easier to send out two fetches in parallel?

  1. hlx6 API: api.aem.live/${org}/sites/${site}/source/cat.jpg or document.html
  2. DA Admin API: admin.da.live/source/${org}/${site}/cat.jpg or document.html

waiting on their response and then doing a fallback.
If 1. hlx 6 API is 200 -> it is a hlx6 project -> return the content
else fallback to the DA Admin response (200, or 404, it doesn't matter, if it is not there, it is nowhere else)

That way we don't need a special detection mechanism. Would work even for projects that may end up with mixed content between DA Admin and HLX6 (for whatever edge case).

@benpeter

benpeter commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Would it not be easier to send out two fetches in parallel?

@andreituicu, with helix-admin#3687 and #3689, /ping now returns 200 for every site, and helix-admin adds x-api-upgrade-available for source-bus sites. I think using that signal is safer than requesting every resource from both backends.

benpeter and others added 3 commits July 28, 2026 11:10
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@benpeter

Copy link
Copy Markdown
Contributor

rebased on main and adjusted to changes in #240

@benpeter
benpeter force-pushed the feat/hlx6-support branch from e285b78 to 1eb95c9 Compare July 28, 2026 09:40
@benpeter
benpeter marked this pull request as draft July 28, 2026 09:45
benpeter and others added 3 commits July 28, 2026 13:50
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@andreituicu

Copy link
Copy Markdown
Contributor

sure, not at all against the probing solution. it is the faster and less wasteful solution with cache, so clearly more optiomal.

in terms of safest, in my mind the getting from both places was the safest option, because the content is either there in one of the two places or its a 404. there is no possibility of the probe being wrong, the content being mixed between Helix and DA, site being in migration, or any of the multiple edge cases, or in-between states that might be encountered during the Helix 6 transition.

that's just to explain why I proposed it, but still, nothing against if go the probe route.
at this time, nothing works, so having a solution in place is clearly the most important thing and issues, bugs, edge cases can be fixed from there forward.

@benpeter

Copy link
Copy Markdown
Contributor

@andreituicu Agreed on simplicity. my concern was rollback: if a site moves back to DA but the migrated copy is still in source bus, an H6-first read would keep returning it even if content.source.url no longer points to api.aem.live. same for deletions: a stale copy could turn a 404 into a 200.

I have also removed the routing cache locally. probably YAGNI, and it is per CF isolate anyway, so different isolates could route the same site differently during cutover.

benpeter and others added 10 commits July 28, 2026 14:38
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@benpeter
benpeter marked this pull request as ready for review July 28, 2026 14:44
@benpeter
benpeter requested a review from tripodsan July 28, 2026 14:44
Comment thread src/routes/da-admin.js
Comment thread src/routes/da-admin.js Outdated
Comment thread src/routes/da-admin.js
Comment thread src/routes/da-admin.js

@tripodsan tripodsan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

several issues.

Comment thread src/routes/da-admin.js
Comment thread src/routes/da-admin.js Outdated
Comment thread src/routes/da-admin.js Outdated
Comment thread src/routes/da-admin.js Outdated
Comment thread src/routes/da-admin.js Outdated
Comment thread src/routes/da-admin.js
const daPath = `${path}.${ext}`;
let hlx6 = await probeHlx6(org, site);
if (hlx6 === undefined) {
hlx6 = await resolveUncertainWriteBackend({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't understand this check. if it's not hlx6, then it's not hlx6...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

undefined means that the probe failed, in which case resolveUncertainWriteBackend will HEAD both backends and use api.aem.live if the document exists there, otherwise da-admin. I'll add JSDoc to explain, yes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

but if the probe fails, why should the HEAD request then be a good alternative?

and for a new file, resolveUncertainWriteBackend it will also return false, because its a 404.

benpeter and others added 6 commits July 29, 2026 14:42
Co-authored-by: Tobias Bocanegra <tripod@bocanegra.ch>
Co-authored-by: Tobias Bocanegra <tripod@bocanegra.ch>
Co-authored-by: Tobias Bocanegra <tripod@bocanegra.ch>
Co-authored-by: Tobias Bocanegra <tripod@bocanegra.ch>
@benpeter
benpeter requested a review from tripodsan July 29, 2026 13:28
Comment thread src/routes/da-admin.js
);
let sourceResp;
if (hlx6) {
const sourceUrl = aemApiSourceUrl(org, site, `${path}.${ext}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

but path already has the ext, no?

@tripodsan tripodsan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the entire daCtx path / ext / html / ... handling is uninuitive and not documented.
I would first (in a different PR) harmonize this.

@benpeter

Copy link
Copy Markdown
Contributor

the entire daCtx path / ext / html / ... handling is uninuitive and not documented. I would first (in a different PR) harmonize this.

sketched the harmonization in #256

@benpeter

benpeter commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

the entire daCtx path / ext / html / ... handling is uninuitive and not documented. I would first (in a different PR) harmonize this.

PR is #261

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.

4 participants