Repository navigation
fix(hop): one service's gRPC and Connect endpoints are one destination - #31
Merged
Merged
Conversation
Since #26 a Connect endpoint also claims its procedures for gRPC, so a module that still publishes the same service on a grpc and a connect endpoint (annotations, model, robin, tracing) made the hop refuse its own routes at startup: "ambiguous destination for annotations /annotations.v1.AnnotationsService/Annotate". Routing now treats that pair as one destination when both endpoints carry the same contract (package, version, digest): gRPC calls go to the Connect endpoint, which serves gRPC, gRPC-Web and Connect; the gRPC endpoint keeps only procedures Connect does not publish, and a gRPC-only module routes as before. Every other second claim is still refused, now naming both listeners: two services, two endpoints of one transport, an HTTP route over a Connect procedure, or one service's endpoints under different contracts. Resolve no longer returns the first exact match of an unvalidated snapshot; it applies the same rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
antoinetoussaint-byte
added a commit
that referenced
this pull request
Oct 26, 2026
…p-binds-method Kept both: #31's rule that a service's gRPC and Connect endpoints are one destination, and this branch's refusal of a REST face transcoded into a procedural listener; both routing test sets stay. #31's dual-publication proxy test signs with the hop's Signer, as every proxy test here does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
What we are trying to do
Let the hop start on a composition whose modules still publish one service on both a
grpcand aconnectendpoint. Today it refuses its own routes at startup (platform-core#114 at792d82c, scopecmp114):Since #26 (
3acb2c3), a Connect endpoint also claims its procedures for gRPC. annotations, model, robin and tracing each publish the same contract on both endpoints (same package, version and digest in their catalogs), so each procedure looked like two destinations.The fix, in routing (the owner of the guarantee)
libraries/hop/go/routing:sameDestinationaccepts exactly one case: the same audience and service, onegrpcendpoint and oneconnectendpoint, under the same package, version and digest. That pair is one destination. Every other second claim is still refused, and the error now names both listeners (published by module/first/connect (connect) and module/second/grpc (grpc)). That covers two services, two endpoints of one transport, an HTTP route over a Connect procedure, and one service's endpoints under different contracts (… publish it under different contracts …, so their policies may differ).The policy name passed to authority Check does not change: it is the procedure in both cases.
Classification: fix, at the place that owns the behaviour. No module is edited and no route is authored. The CLI's route producer (
cli/pkg/orchestration/hop_configurations.go) calls this sameValidate, so a CLI that bumps itslibraries/hop/gopin past #26 has to take this commit, or it refuses these compositions at plan time.Reuse: the existing
Validate/Resolvewalk and the existing proxy upstream table. No new mechanism. The hop still dials the gRPC endpoint (unused for shared procedures), and I deliberately left that alone.Parity: routing exists only in Go.
libraries/authority-client/{python,typescript}select no routes, so they have nothing to mirror.Tests
libraries/hop/go/routing/routes_test.go:TestOneServiceOnGRPCAndConnectIsOneDestination: both endpoints on one service give one destination (Connect for gRPC and Connect requests, the gRPC endpoint for a gRPC-only procedure), in both orders.TestGRPCOnlyModuleStillRoutesTestTwoServicesClaimingOneProcedureRefuseByName: connect+grpc, connect+connect and grpc+grpc across two services are refused, the error names both, and Resolve on the unvalidated snapshot refuses too.TestSameServiceWithDifferentContractsRefuses: a different digest, version or package is refused.On the parent commit, three of the four fail. The existing refusals (
TestAmbiguousPublicationAndTemplatesRefuse,TestConnectGRPCOverlapRefusesInEitherOrder) are unchanged and pass.modules/authority/services/hop/code/pkg/infra/proxy/connect_listener_test.go,TestDualPublishedServiceSendsGRPCToItsConnectListener: a hop with both listeners live sends the gRPC call to the Connect backend (connect=1, grpc=0).Local:
go vet ./...andgo test -race -count=1 ./...pass on all 11 Go modules.pytest: 300 passed.npm test(authority-client/typescript): 158 passed.Run evidence
Local run of platform-core#114 at
792d82c(merged pins), in its own naming scopehoprq3, with the CLI at cli maine4de4a8candauthorityresolved to this branch's checkout. Roots:saas/auth-gateway authority/hop annotations/annotations, with the gate's own converter exclusions. Two local-only edits toconfigurations/local/hop.env, neither committed anywhere:MODULES=model,robin,tracing,annotations: documents is left unrouted because it panics on its own Connect carrier (no carrier for … documents/documents/connect; owner service-go-grpc or module-document-store, as #114 records). The derived routes still carry all four dual-published modules.ENFORCEMENT=warn, only for the call below. The reason is in the next section.The hop starts. In enforce mode, and again in warn mode:
Negative control. The same run with
libraries/hop/go/routingput back tomainpanics as #114 reported:An Annotate call routes to annotations' Connect listener. A probe signs
dev-adminin at saas accounts (Connect face), is issued a Work Context at audienceannotations(the token is not printed), and calls through the hop's gRPC face:annotations answered the Annotate call itself:
Before the calls the hop holds no connection to annotations. Afterwards (
lsofon the hop process) it holds exactly one, to the Connect port 47350 and none to the gRPC port 46583:Why warn mode, and a gap that is not this PR's. In enforce mode the route resolves and the hop asks the authority, but saas accounts refuses every annotations method with
annotations's delivery declares no method policy. This run delivers manifests through accounts' legacy REST delivery route (host.deliverystayssaas/accounts/rest; the CLI refuses accounts' Connect face as a delivery endpoint, per #114). In this run, that route applied the manifests without an authority document. The owners are codefly-dev/cli and module-saas-starter, not routing. For the same reason Annotate cannot succeed end to end here: annotations makes no change on an advisory. Also seen in the run, and also not routing's: annotations' owner probe to robin is refused because robin publishes no/annotations.v1.TargetAuthorityService/ListKindsdestination.Not verified: an enforce-mode Annotate that succeeds, which needs the delivery gap above closed; the composed CI gate (#114's CI stops earlier, on a token's access to module-annotations); a CLI release carrying this routing. The CLI's route producer calls this same
Validate, and it still pinslibraries/hop/gobefore #26.🤖 Generated with Claude Code