diff --git a/CHANGELOG.md b/CHANGELOG.md
index 69b1f20..7637ccb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,23 @@ See the fragment files in the [changelog.d/ directory](./changelog.d).
+
+## 22.0.0 — 2026-05-18
+
+### Added
+
+- Support to verawood release
+
+* Extend BY_PATH hosting for **frontend-site apps** (e.g. `instructor-dashboard`)
+
+* Route enabled frontend apps through LMS Caddy when `MFE_EXTENSIONS_BY_PATH` is on.
+
+* Proxy frontend-site webpack assets served at the LMS domain root (`runtime.*.js`,
+ `*-main.*.js/css`) to the MFE container, fixing blank instructor dashboard pages.
+
+* Set `INSTRUCTOR_MICROFRONTEND_URL`, `CATALOG_MICROFRONTEND_URL`, and LMS
+ `MFE_CONFIG` / `FRONTEND_SITE_CONFIG` base URLs for BY_PATH on the LMS host.
+
## 21.0.2 — 2026-05-14
diff --git a/pyproject.toml b/pyproject.toml
index 34f78be..8ece4de 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "tutor-contrib-mfe-extensions"
-version = "21.0.2"
+version = "22.0.0"
description = "mfe_extensions plugin for Tutor"
readme = "README.md"
requires-python = ">=3.10"
@@ -16,10 +16,11 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
]
dependencies = [
- "tutor~=21.0",
- "tutor-mfe~=21.0",
+ "tutor~=22.0",
+ "tutor-mfe~=22.0",
]
[project.urls]
@@ -45,6 +46,11 @@ lint.select = [
"UP",
]
+# TODO: remove before merging
+[tool.uv.sources]
+tutor = {git = "https://github.com/overhangio/tutor", rev = "f7e7a4c6316bc6e42ce68f8e916a72a7ca15811e"} # Verawood
+tutor-mfe = {git = "https://github.com/overhangio/tutor-mfe", rev = "1547ec2787535f9b340ba87909fef2ec32a806c9"} # Verawood
+
[tool.scriv]
version = "literal: pyproject.toml: project.version"
categories = "Added, Changed, Fixed, Removed"
diff --git a/tutormfe_extensions/patches/caddyfile-frontend-site-assets b/tutormfe_extensions/patches/caddyfile-frontend-site-assets
new file mode 100644
index 0000000..117ee3b
--- /dev/null
+++ b/tutormfe_extensions/patches/caddyfile-frontend-site-assets
@@ -0,0 +1,12 @@
+{%- if MFE_EXTENSIONS_BY_PATH and get_frontend_apps() %}
+# Frontend-site bundles (e.g. runtime.*.js) are served from /openedx/dist/site at the
+# MFE root. When hosting by path on the LMS domain, proxy those asset paths to mfe.
+@mfe_frontend_site_assets {
+ path_regexp assets ^/(runtime|app|cssLayerOrder|[0-9]+|[a-z0-9-]+-main)\.[a-f0-9]+\.(js|css)$
+}
+handle @mfe_frontend_site_assets {
+ reverse_proxy mfe:8002 {
+ header_up Host {host}
+ }
+}
+{%- endif %}
diff --git a/tutormfe_extensions/patches/caddyfile-lms b/tutormfe_extensions/patches/caddyfile-lms
index bd9ceb7..fbcb461 100644
--- a/tutormfe_extensions/patches/caddyfile-lms
+++ b/tutormfe_extensions/patches/caddyfile-lms
@@ -1 +1,2 @@
{{ patch("caddyfile-mfe-by-path") }}
+{{ patch("caddyfile-frontend-site-assets") }}
diff --git a/tutormfe_extensions/patches/openedx-lms-production-settings b/tutormfe_extensions/patches/openedx-lms-production-settings
index c7de309..098f1f3 100644
--- a/tutormfe_extensions/patches/openedx-lms-production-settings
+++ b/tutormfe_extensions/patches/openedx-lms-production-settings
@@ -1,4 +1,7 @@
{% if MFE_EXTENSIONS_BY_PATH %}
+MFE_CONFIG["BASE_URL"] = "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}"
+FRONTEND_SITE_CONFIG["baseUrl"] = "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}"
+
LEARNING_MICROFRONTEND_URL = "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}/learning"
MFE_CONFIG["LEARNING_BASE_URL"] = "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}/learning"
@@ -7,7 +10,7 @@ ADMIN_CONSOLE_MICROFRONTEND_URL = "{% if ENABLE_HTTPS %}https://{% else %}http:/
MFE_CONFIG["ADMIN_CONSOLE_URL"] = ADMIN_CONSOLE_MICROFRONTEND_URL
{% endif %}
-{%- if get_mfe("authn") %}
+{%- if get_mfe("authn") or is_frontend_app_enabled("authn") %}
AUTHN_MICROFRONTEND_URL = "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}/authn"
AUTHN_MICROFRONTEND_DOMAIN = "{{ LMS_HOST }}/authn"
{%- endif %}
@@ -26,7 +29,7 @@ MFE_CONFIG["DISCUSSIONS_MFE_BASE_URL"] = DISCUSSIONS_MICROFRONTEND_URL
WRITABLE_GRADEBOOK_URL = "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}/gradebook"
{% endif %}
-{% if get_mfe("learner-dashboard") %}
+{% if get_mfe("learner-dashboard") or is_frontend_app_enabled("learner-dashboard") %}
LEARNER_HOME_MICROFRONTEND_URL = "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}/learner-dashboard/"
{% endif %}
@@ -43,6 +46,14 @@ MFE_CONFIG["ACCOUNT_PROFILE_URL"] = "{% if ENABLE_HTTPS %}https{% else %}http{%
COMMUNICATIONS_MICROFRONTEND_URL = "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}/communications"
{% endif %}
+{% if get_mfe("catalog") %}
+CATALOG_MICROFRONTEND_URL = "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}/catalog"
+{% endif %}
+
+{% if is_frontend_app_enabled("instructor-dashboard") %}
+INSTRUCTOR_MICROFRONTEND_URL = "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ LMS_HOST }}/instructor-dashboard"
+{% endif %}
+
{% if get_mfe("authoring") %}
MFE_CONFIG["COURSE_AUTHORING_MICROFRONTEND_URL"] = "{% if ENABLE_HTTPS %}https://{% else %}http://{% endif %}{{ LMS_HOST }}/authoring"
{% endif %}
diff --git a/tutormfe_extensions/plugin.py b/tutormfe_extensions/plugin.py
index bd07c05..1560b2f 100644
--- a/tutormfe_extensions/plugin.py
+++ b/tutormfe_extensions/plugin.py
@@ -9,7 +9,7 @@
from tutor import hooks
from tutor.types import Config
from tutormfe.hooks import MFE_APPS
-from tutormfe.plugin import CORE_MFE_APPS
+from tutormfe.plugin import CORE_MFE_APPS, get_frontend_apps, is_frontend_app_enabled
from .__about__ import __version__
@@ -72,17 +72,30 @@ def iter_mfes_per_service(service: str = "") -> Iterable[str]:
Return the list of MFEs that should be hosted via path in the
same domain as each service.
+ Includes legacy MFEs from MFE_APPS and frontend-base apps that are enabled
+ (e.g. instructor-dashboard), which are not registered in MFE_APPS.
+
"""
active_mfes = MFE_APPS.apply({})
cms_mfes = {"authoring"}
lms_mfes = set(CORE_MFE_APPS) - cms_mfes
+ seen: set[str] = set()
for mfe in active_mfes:
if service == "lms" and mfe in lms_mfes:
+ seen.add(mfe)
yield mfe
if service == "cms" and mfe in cms_mfes:
+ seen.add(mfe)
yield mfe
+ if service == "lms":
+ for app_name in get_frontend_apps():
+ if app_name in seen or app_name in cms_mfes:
+ continue
+ if is_frontend_app_enabled(app_name):
+ yield app_name
+
hooks.Filters.ENV_TEMPLATE_ROOTS.add_items(
[
diff --git a/uv.lock b/uv.lock
index 3af790e..f3770a3 100644
--- a/uv.lock
+++ b/uv.lock
@@ -812,8 +812,8 @@ wheels = [
[[package]]
name = "tutor"
-version = "21.0.6"
-source = { registry = "https://pypi.org/simple" }
+version = "22.0.0"
+source = { git = "https://github.com/overhangio/tutor?rev=59264ca9f9c6a8e67622ae72080684ffc6ec7e2a#59264ca9f9c6a8e67622ae72080684ffc6ec7e2a" }
dependencies = [
{ name = "appdirs" },
{ name = "click" },
@@ -827,11 +827,10 @@ dependencies = [
{ name = "pyyaml" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/26/d8/3d93bc551ba2e5035e4fc107a0d89d0e6b70addc1a7134c5d4c5b92ff406/tutor-21.0.6.tar.gz", hash = "sha256:36655840644289becdbad642d1b70677475f03eebf9925ebcd394f686f9ed25f", size = 112956, upload-time = "2026-05-06T12:07:53.473Z" }
[[package]]
name = "tutor-contrib-mfe-extensions"
-version = "21.0.2"
+version = "22.0.0"
source = { editable = "." }
dependencies = [
{ name = "tutor" },
@@ -847,8 +846,8 @@ dev = [
[package.metadata]
requires-dist = [
- { name = "tutor", specifier = "~=21.0" },
- { name = "tutor-mfe", specifier = "~=21.0" },
+ { name = "tutor", git = "https://github.com/overhangio/tutor?rev=59264ca9f9c6a8e67622ae72080684ffc6ec7e2a" },
+ { name = "tutor-mfe", git = "https://github.com/overhangio/tutor-mfe?rev=d0804c2590431780d3a0ed6d24701c20eef5a86d" },
]
[package.metadata.requires-dev]
@@ -860,12 +859,11 @@ dev = [
[[package]]
name = "tutor-mfe"
-version = "21.0.0"
-source = { registry = "https://pypi.org/simple" }
+version = "22.0.0"
+source = { git = "https://github.com/overhangio/tutor-mfe?rev=d0804c2590431780d3a0ed6d24701c20eef5a86d#d0804c2590431780d3a0ed6d24701c20eef5a86d" }
dependencies = [
{ name = "tutor" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/29/51/591e2608026176c10a4b01fdd6e637324d1158e35f3b54aabe3214cb9d33/tutor_mfe-21.0.0.tar.gz", hash = "sha256:2a266d9554cbeeb2ac01391071308c14cd5a78b39f9407f088e28373a3870b17", size = 44352, upload-time = "2026-01-16T11:55:17.916Z" }
[[package]]
name = "ty"