Skip to content

fix(serviceoffer): gate RoutePublished on Traefik acceptance, sweep legacy ReferenceGrant#767

Merged
bussyjd merged 2 commits into
integration/v0.14.0-rc0from
fix/serviceoffer-route-acceptance
Jul 16, 2026
Merged

fix(serviceoffer): gate RoutePublished on Traefik acceptance, sweep legacy ReferenceGrant#767
bussyjd merged 2 commits into
integration/v0.14.0-rc0from
fix/serviceoffer-route-acceptance

Conversation

@bussyjd

@bussyjd bussyjd commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Canary402 field report — issues 3b/6b (⚠️ offers stay Ready while Traefik rejects their routers) + residual gap in 4726dcf

A) Traefik acceptance → status: reconcileRoute set RoutePublished=True purely 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 gates RoutePublished on the existing httpRouteAccepted() (Accepted + ResolvedRefs), mirroring identityRegistrationResourcesReady'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 legacyLimitsMiddlewareName pattern exactly: the legacy name is deleted in both the per-reconcile teardown loop and deleteRouteChildren.

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 to so-foo-bar-baz-backend-grant. Since every grant shares the x402 namespace, 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 to sharedNamespaceNameBuilders.

bussyjd added 2 commits July 16, 2026 15:19
…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
bussyjd merged commit 7851a74 into integration/v0.14.0-rc0 Jul 16, 2026
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.
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.

1 participant