Skip to content

feat(hosting): the endpoint-contribution hook — modules map HTTP routes (#1655) - #1663

Merged
rbuergi merged 2 commits into
mainfrom
feat/1655-endpoint-hook
Aug 16, 2026
Merged

feat(hosting): the endpoint-contribution hook — modules map HTTP routes (#1655)#1663
rbuergi merged 2 commits into
mainfrom
feat/1655-endpoint-hook

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Implements #1655 (maintainer-approved). New MeshWeaver.Hosting.AspNetCore with the assembly-level MeshEndpointProviderAttribute + app.MapMeshModuleEndpoints() wired into StartMemexApplication beside the other Map* calls.

  • Layering: separate attribute, not a MeshNodeProviderAttribute hook — the mesh contract never references ASP.NET, and endpoints apply at endpoint-mapping time (after auth middleware), not mesh build.
  • Auth by default: contributions map inside a RequireAuthorization() group; a route is anonymous only via explicit per-route AllowAnonymous().
  • Loud collisions: duplicate (verb, pattern) across the WHOLE endpoint table refuses the app at ApplicationStarted naming both endpoints (module-vs-module AND module-vs-platform); same pattern on different verbs stays legitimate. ASP.NET alone only surfaces ambiguity at request time.
  • Security model documented: a module is trusted code the deployment listed — unlike UiContribution mesh data, which still cannot contribute routes.
  • Docs: Modules.md + UiExtensibility (the 'HTTP endpoints' wall retired). DocumentationLinkIntegrityTest green.

Verification: Hosting.AspNetCore / Portal.Shared / Portal.Monolith clean -c Release -warnaserror; ModuleEndpointContributionTest 2/2 (discovery + auth default + anonymous opt-out; collision predicate with both parties named). First consumers land as separate PRs: Social (#59), the python/node gRPC link (#60), then speech/log-incidents/og.

No What's New entry: platform capability, no user-visible change.

🤖 Generated with Claude Code

…es (#1655)

New MeshWeaver.Hosting.AspNetCore: an assembly-level
MeshEndpointProviderAttribute a module carries, applied by the host's
app.MapMeshModuleEndpoints() alongside its other Map* calls. Deliberately
separate from MeshNodeProviderAttribute — layering (the mesh contract
never references ASP.NET) and timing (endpoints map after auth).

Guardrails per the #1655 design: every contribution maps inside an
authenticated-by-default group (per-route AllowAnonymous is the only
escape), and duplicate (verb, pattern) registrations refuse the app
loudly at ApplicationStarted — a silently shadowed route is the #683
trapdoor class. Pinned: discovery over InstalledModuleAssembly, the auth
default + opt-out metadata, and the collision predicate (both parties
named; verb-distinct same-pattern legitimate).

Docs: Modules.md gains the endpoint hook; UiExtensibility's 'HTTP
endpoints' wall is retired (mesh DATA still cannot contribute routes —
trusted compiled code only). First consumers queued: Social, the
python/node gRPC link, then the compiled optional-service endpoints
(speech/log-incidents/og).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 16, 2026 09:23

Copilot AI 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.

Pull request overview

Adds an ASP.NET Core hosting integration point so boot-installed modules can contribute HTTP endpoints via an assembly-level attribute, with host-side mapping and startup-time collision detection. This extends the “module lane” without coupling the mesh contract to ASP.NET surfaces.

Changes:

  • Introduces MeshWeaver.Hosting.AspNetCore with MeshEndpointProviderAttribute and app.MapMeshModuleEndpoints() to map module-contributed routes under an authenticated-by-default group.
  • Adds collision detection over the full endpoint table at startup (duplicate (verb, pattern) pairs) plus an integration test covering discovery/auth defaults and the collision predicate.
  • Updates architecture docs to reflect that module code (not mesh data) can now contribute HTTP endpoints.

Reviewed changes

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

Show a summary per file
File Description
test/MeshWeaver.Hosting.Monolith.Test/ModuleEndpointContributionTest.cs Adds tests for module endpoint discovery, auth-by-default behavior, and collision predicate behavior.
src/MeshWeaver.Hosting.AspNetCore/MeshWeaver.Hosting.AspNetCore.csproj New hosting integration project for ASP.NET Core endpoint contributions.
src/MeshWeaver.Hosting.AspNetCore/MeshModuleEndpointExtensions.cs Implements MapMeshModuleEndpoints() and route-collision detection logic.
src/MeshWeaver.Hosting.AspNetCore/MeshEndpointProviderAttribute.cs Defines the assembly-level attribute modules use to contribute endpoints.
src/MeshWeaver.Documentation/Data/Architecture/UiExtensibility.md Updates extensibility “walls” to reflect the new module endpoint seam.
src/MeshWeaver.Documentation/Data/Architecture/Modules.md Documents the endpoint contribution attribute, auth defaults, and collision behavior.
MeshWeaver.slnx Adds the new hosting project to the solution.
memex/Memex.Portal.Shared/MemexConfiguration.cs Wires app.MapMeshModuleEndpoints() into StartMemexApplication.
memex/Memex.Portal.Shared/Memex.Portal.Shared.csproj References the new MeshWeaver.Hosting.AspNetCore project.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/MeshWeaver.Hosting.AspNetCore/MeshModuleEndpointExtensions.cs Outdated
@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 0)

918 tests  ±0   917 ✅ ±0   11m 29s ⏱️ +26s
 10 suites ±0     1 💤 ±0 
 10 files   ±0     0 ❌ ±0 

Results for commit 89f9add. ± Comparison against base commit 20b793b.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 5)

1 367 tests  ±0   1 366 ✅ ±0   5m 10s ⏱️ -49s
   11 suites ±0       1 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 89f9add. ± Comparison against base commit 20b793b.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 4)

1 542 tests  ±0   1 536 ✅ ±0   6m 16s ⏱️ -58s
   11 suites ±0       6 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 89f9add. ± Comparison against base commit 20b793b.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 3)

   11 files  ±0     11 suites  ±0   4m 23s ⏱️ - 1m 46s
2 196 tests ±0  2 005 ✅ ±0  191 💤 ±0  0 ❌ ±0 
2 556 runs  ±0  2 365 ✅ ±0  191 💤 ±0  0 ❌ ±0 

Results for commit 89f9add. ± Comparison against base commit 20b793b.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 2)

2 322 tests   - 62   2 318 ✅  - 62   7m 16s ⏱️ -28s
   11 suites ± 0       4 💤 ± 0 
   11 files   ± 0       0 ❌ ± 0 

Results for commit 89f9add. ± Comparison against base commit 20b793b.

This pull request removes 187 and adds 125 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.MonolithRouterAnswerOnceAfterPackagingTest ‑ PackagedFireAndForgetAndNacks_AreNotAnswered_WhileOrdinaryTrafficIs
MeshWeaver.Hosting.Monolith.Test.NextLevelNavigationTest ‑ NextLevel_AddingNearerNode_CollapsesFrontier
MeshWeaver.Hosting.Monolith.Test.NextLevelNavigationTest ‑ NextLevel_SkipsEmptyNamespaces_AndSuppressesDeeperNodes
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_FromMcpShapedSessionHubWithoutNodeOperationHandlers
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_OverwritesExistingWhenForced
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_PreservesContent
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_SkipsExistingWhenNotForced
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_ThrowsWhenSourceNotFound
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_ToSpaceNamespace
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_WithDescendants
…
MeshWeaver.Hosting.Monolith.Test.MonolithMeshTest ‑ PingPong
MeshWeaver.Hosting.Monolith.Test.MonolithMeshTest ‑ PingToNonExistentHub_ThrowsDeliveryFailure
MeshWeaver.Hosting.Monolith.Test.MonolithMeshTest ‑ StreamToNonExistentHub_ThrowsDeliveryFailure
MeshWeaver.Hosting.Monolith.Test.MonotonicWriteGuardWindowTest ‑ StaleWriteInsideTheHighWaterWindow_IsRefused_AndTheStoreNeverMovesBackward
MeshWeaver.Hosting.Monolith.Test.NodeAlcUnloadTeardownOrderingTest ‑ NodeAssemblyContexts_AreUnloaded_Immediately_WhenTheMeshKeepsRunning
MeshWeaver.Hosting.Monolith.Test.NodeAlcUnloadTeardownOrderingTest ‑ NodeAssemblyContexts_AreUnloaded_OnlyAfterTheIoPoolsAreDrained
MeshWeaver.Hosting.Monolith.Test.NodeOperationIssuingHubTest ‑ WorkIssuedWhileHoldingTheRouter_NeverMakesTheRouterAnEnd
MeshWeaver.Hosting.Monolith.Test.NodeOperationTargetTest ‑ ChildOfAnExecutionHub_WalksUpToThatHub_NotPastItToTheRouter
MeshWeaver.Hosting.Monolith.Test.NodeOperationTargetTest ‑ HubWithHandlersButNoExecutionOptIn_StillFallsBackToTheSharedExecutionHub
MeshWeaver.Hosting.Monolith.Test.NodeOperationTargetTest ‑ HubWithoutTheOptIn_FallsBackToTheDedicatedExecutionHub_NeverTheRouter
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 1)

2 242 tests  +64   2 139 ✅ +64   7m 28s ⏱️ +7s
   11 suites ± 0     103 💤 ± 0 
   11 files   ± 0       0 ❌ ± 0 

Results for commit 89f9add. ± Comparison against base commit 20b793b.

This pull request removes 125 and adds 189 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.MonolithMeshTest ‑ PingPong
MeshWeaver.Hosting.Monolith.Test.MonolithMeshTest ‑ PingToNonExistentHub_ThrowsDeliveryFailure
MeshWeaver.Hosting.Monolith.Test.MonolithMeshTest ‑ StreamToNonExistentHub_ThrowsDeliveryFailure
MeshWeaver.Hosting.Monolith.Test.MonotonicWriteGuardWindowTest ‑ StaleWriteInsideTheHighWaterWindow_IsRefused_AndTheStoreNeverMovesBackward
MeshWeaver.Hosting.Monolith.Test.NodeAlcUnloadTeardownOrderingTest ‑ NodeAssemblyContexts_AreUnloaded_Immediately_WhenTheMeshKeepsRunning
MeshWeaver.Hosting.Monolith.Test.NodeAlcUnloadTeardownOrderingTest ‑ NodeAssemblyContexts_AreUnloaded_OnlyAfterTheIoPoolsAreDrained
MeshWeaver.Hosting.Monolith.Test.NodeOperationIssuingHubTest ‑ WorkIssuedWhileHoldingTheRouter_NeverMakesTheRouterAnEnd
MeshWeaver.Hosting.Monolith.Test.NodeOperationTargetTest ‑ ChildOfAnExecutionHub_WalksUpToThatHub_NotPastItToTheRouter
MeshWeaver.Hosting.Monolith.Test.NodeOperationTargetTest ‑ HubWithHandlersButNoExecutionOptIn_StillFallsBackToTheSharedExecutionHub
MeshWeaver.Hosting.Monolith.Test.NodeOperationTargetTest ‑ HubWithoutTheOptIn_FallsBackToTheDedicatedExecutionHub_NeverTheRouter
…
MeshWeaver.Hosting.Monolith.Test.ModuleEndpointContributionTest ‑ ModuleEndpoints_AreDiscovered_AndAuthenticatedByDefault
MeshWeaver.Hosting.Monolith.Test.ModuleEndpointContributionTest ‑ RouteCollisions_AreDetected_WithBothPartiesNamed
MeshWeaver.Hosting.Monolith.Test.MonolithRouterAnswerOnceAfterPackagingTest ‑ PackagedFireAndForgetAndNacks_AreNotAnswered_WhileOrdinaryTrafficIs
MeshWeaver.Hosting.Monolith.Test.NextLevelNavigationTest ‑ NextLevel_AddingNearerNode_CollapsesFrontier
MeshWeaver.Hosting.Monolith.Test.NextLevelNavigationTest ‑ NextLevel_SkipsEmptyNamespaces_AndSuppressesDeeperNodes
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_FromMcpShapedSessionHubWithoutNodeOperationHandlers
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_OverwritesExistingWhenForced
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_PreservesContent
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_SkipsExistingWhenNotForced
MeshWeaver.Hosting.Monolith.Test.NodeCopyHelperTest ‑ CopyNodeTree_ThrowsWhenSourceNotFound
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results

    65 files  ±0      65 suites  ±0   42m 4s ⏱️ - 3m 27s
10 587 tests +2  10 281 ✅ +2  306 💤 ±0  0 ❌ ±0 
10 947 runs  +2  10 641 ✅ +2  306 💤 ±0  0 ❌ ±0 

Results for commit 89f9add. ± Comparison against base commit 20b793b.

♻️ This comment has been updated with latest results.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rbuergi

rbuergi commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

Review addressed: the collision-refusal comment now states the actual intent (log + StopApplication + throw — belt and braces). The using MeshWeaver.Hosting.Monolith.Test; in the test file is kept deliberately: the assembly-level [assembly: TestModuleEndpoints] sits OUTSIDE the namespace declaration, so resolving the attribute type (declared inside the namespace) requires it — removing it breaks the build.

@rbuergi
rbuergi merged commit 0a8f869 into main Aug 16, 2026
22 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.

2 participants