fix(serviceoffer): gate RoutePublished on Traefik acceptance, sweep legacy ReferenceGrant#767
Merged
bussyjd merged 2 commits intoJul 16, 2026
Conversation
…egacy ReferenceGrant reconcileRoute flipped RoutePublished=True as soon as the HTTPRoute apply Patch was admitted by the k8s API server, which only checks CRD schema — it never read back Traefik's own status.parents Accepted/ResolvedRefs conditions, so a route Traefik silently rejected still looked Ready. Mirror identityRegistrationResourcesReady's existing httpRouteAccepted() gate for both the main and hostname routes, riding the existing 5s !ready requeue while pending. Also sweep the legacy (pre-4726dcfe) non-namespace-qualified ReferenceGrant name on every reconcile, not just on offer deletion — deletion-only would never clear a live collision between two offers still using the old shared name. Addresses a Canary402 field report. Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
…, name) The namespace-qualified grant name from 4726dcf joins namespace and name with a dash, but both are DNS subdomains that may contain internal dashes, so the join is not injective: (ns=foo-bar, name=baz) and (ns=foo, name=bar-baz) both produce so-foo-bar-baz-backend-grant. Since every grant shares the x402 namespace, those two live offers fight over one ReferenceGrant — the same HTTP-500 collision the namespace-qualification was meant to end, just narrowed rather than closed. Disambiguate with a hash of the exact tuple (namespace+"/"+name, using the DNS-illegal "/" as a collision-free encoding). Sweep the superseded dash-joined name alongside the pre-4726dcfe name so upgrades tear down both stale forms. Found by a new property-based name-injectivity oracle (name_injectivity_test.go) built to catch this whole bug class, not just this instance — part of the Canary402 proactive-hunt follow-up. Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
bussyjd
added a commit
that referenced
this pull request
Jul 16, 2026
…/v0.14.0-rc0 Resolved with #767 hostRouteRules + chat widget: keep hostRouteRules helper, add /.well-known/agent-registration.json as fourth discovery Exact rule, and retain both chat-widget and upstream-OpenAPI test suites.
This was referenced Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Canary402 field report — issues 3b/6b (⚠️ offers stay Ready while Traefik rejects their routers) + residual gap in 4726dcf
A) Traefik acceptance → status:
reconcileRoutesetRoutePublished=Truepurely on apply success — a route Traefik rejected (e.g. invalid Middleware ref) still showed Ready, and only Traefik logs revealed the failure (the reporter's exact experience with the combined rate-limit middleware). The controller now Gets each applied HTTPRoute back and gatesRoutePublishedon the existinghttpRouteAccepted()(Accepted + ResolvedRefs), mirroringidentityRegistrationResourcesReady's proven pattern, for both the main and hostname routes. Not-yet-accepted →RoutePublished=False/WaitingForTraefikAcceptance, re-checked by the existing 5s requeue. No RBAC change needed.B) Legacy ReferenceGrant sweep: 4726dcf namespace-qualified ReferenceGrant names but left old-named grants orphaned forever — live colliding deployments would never self-heal. This mirrors the
legacyLimitsMiddlewareNamepattern exactly: the legacy name is deleted in both the per-reconcile teardown loop anddeleteRouteChildren.Tests: RoutePublished gating on unaccepted route status (with an apply-patch reactor for the fake dynamic client, test-only scaffolding), legacy grant deletion during reconcile.
go test ./internal/serviceoffercontroller/...green.Part of the Canary402 field-report sweep (6 PRs). Commit unsigned — batch re-sign before merge if required.
https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
Follow-up commit: injective ReferenceGrant name (proactive-hunt finding)
A property-based name-injectivity oracle (
name_injectivity_test.go, added here) found that 4726dcf's namespace-qualified grant name —safeName("so-", namespace+"-"+name, "-backend-grant")— is still not injective. Namespace and name are both DNS subdomains that may contain internal dashes, so(ns=foo-bar, name=baz)and(ns=foo, name=bar-baz)both collapse toso-foo-bar-baz-backend-grant. Since every grant shares thex402namespace, that's the same HTTP-500 collision the original field report described, merely narrowed instead of closed (the oracle found 4 colliding pairs in an 11-element fragment set).Fixed by disambiguating with a hash of the exact
(namespace, name)tuple (encoded with the DNS-illegal/so the hash input is collision-free); the superseded dash-joined name is swept alongside the pre-4726dcfe name so upgrades tear down both stale forms. The oracle is written to generalize — any future shared-namespace child resource is covered by adding its builder tosharedNamespaceNameBuilders.