phase 4: connectrpc on the gateway authenticated edge
Replace the native-gRPC server bootstrap with a single `connectrpc.com/connect` HTTP/h2c listener. Connect-Go natively serves Connect, gRPC, and gRPC-Web on the same port, so browsers can now reach the authenticated surface without giving up the gRPC framing native and desktop clients may use later. The decorator stack (envelope → session → payload-hash → signature → freshness/replay → rate-limit → routing/push) is reused unchanged behind a small Connect → gRPC adapter and a `grpc.ServerStream` shim around `*connect.ServerStream`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+73
-31
@@ -423,46 +423,88 @@ Targeted tests:
|
||||
- `gateway/authn` cross-module parity tests as listed under
|
||||
Artifacts.
|
||||
|
||||
## Phase 4. ConnectRPC Support in Gateway
|
||||
## ~~Phase 4. ConnectRPC Support in Gateway~~
|
||||
|
||||
Status: pending. Cross-service phase — work happens in `gateway/`,
|
||||
not `ui/`.
|
||||
Status: done. Cross-service phase — work happened in `gateway/` and
|
||||
`integration/`, not `ui/`.
|
||||
|
||||
Goal: enable browsers to call the gateway's authenticated gRPC surface
|
||||
through ConnectRPC, while preserving the existing native gRPC ingress
|
||||
for desktop and mobile clients.
|
||||
Goal: enable browsers to call the gateway's authenticated edge surface
|
||||
through ConnectRPC, without keeping a separate gRPC server bootstrap
|
||||
alive purely for test clients.
|
||||
|
||||
Artifacts:
|
||||
Decision (taken with the project owner before implementation): the
|
||||
existing native-gRPC `grpc.NewServer` bootstrap was replaced with a
|
||||
single `connectrpc.com/connect` HTTP/h2c listener, since Connect-Go
|
||||
natively serves the Connect, gRPC, and gRPC-Web protocols on the same
|
||||
port. No production gRPC clients existed to preserve. The package
|
||||
`gateway/internal/grpcapi` keeps its name for diff-size reasons and
|
||||
documents the historical labelling in its package doc.
|
||||
|
||||
- ConnectRPC handler registered alongside existing gRPC server in
|
||||
`gateway/internal/...` using `connectrpc.com/connect`
|
||||
- `gateway/buf.gen.yaml` extended to generate Connect-Go code from
|
||||
existing `.proto` files
|
||||
- updated `gateway/README.md` and `gateway/openapi.yaml` reflecting
|
||||
Connect ingress endpoints
|
||||
- updated `docs/ARCHITECTURE.md` §15 if the deployment topology changes
|
||||
- `gateway/internal/.../connect_server_test.go` integration test
|
||||
exercising a unary Connect call and a server-streaming Connect call
|
||||
Artifacts (delivered):
|
||||
|
||||
Dependencies: Phase 3 (canonical bytes are needed for the integration
|
||||
fixtures used here).
|
||||
- `gateway/buf.gen.yaml` extended with `buf.build/connectrpc/go`,
|
||||
generating `gateway/proto/galaxy/gateway/v1/gatewayv1connect/edge_gateway.connect.go`
|
||||
- `gateway/internal/grpcapi/server.go` rewritten around `http.Server`
|
||||
+ `h2c.NewHandler` + `gatewayv1connect.NewEdgeGatewayHandler`
|
||||
- new `gateway/internal/grpcapi/connect_handler.go` adapting the
|
||||
existing `gatewayv1.EdgeGatewayServer` decorator stack to the
|
||||
Connect handler interface, including a `grpc.ServerStreamingServer`
|
||||
shim around `*connect.ServerStream[GatewayEvent]` and a gRPC
|
||||
`status.Error` → `*connect.Error` translation helper
|
||||
- new `gateway/internal/grpcapi/connect_observability.go` Connect
|
||||
interceptor recording the same metric and structured-log shape the
|
||||
gRPC interceptors emitted; the rate-limit decorator now reads peer
|
||||
IP from a context value populated by the interceptor instead of
|
||||
`peer.FromContext`
|
||||
- updated `gateway/README.md` (Transport Matrix + "Authenticated Edge
|
||||
Surface"), `gateway/docs/runtime.md`, `gateway/docs/flows.md`,
|
||||
`gateway/docs/runbook.md`, and `docs/ARCHITECTURE.md` §15
|
||||
- migrated tests: `gateway/internal/grpcapi/server_test.go`,
|
||||
`test_fixtures_test.go`, and every `*_integration_test.go` in that
|
||||
package now drive a `gatewayv1connect.EdgeGatewayClient` over
|
||||
HTTP/2 cleartext loopback
|
||||
- migrated harness: `integration/testenv/grpc_client.go` →
|
||||
`connect_client.go`. `SignedGatewayClient` keeps the same public
|
||||
shape (`Execute`, `SubscribeEvents`, `Close`) but speaks Connect
|
||||
internally; `Is*` helpers now use `connect.CodeOf`
|
||||
|
||||
Acceptance criteria:
|
||||
Dependencies: Phase 3 (canonical bytes are needed for the
|
||||
fixture-level signing the migrated tests use).
|
||||
|
||||
- a curl-based unary Connect call from outside the gateway process
|
||||
succeeds end-to-end against the authenticated surface;
|
||||
- server-streaming `SubscribeEvents` works over Connect with at least
|
||||
one delivered event;
|
||||
- existing native gRPC clients continue to work unchanged;
|
||||
- both gRPC and Connect handlers share the same upstream business code
|
||||
(no duplication beyond the protocol layer).
|
||||
Acceptance criteria (met):
|
||||
|
||||
Targeted tests:
|
||||
- unary Connect calls from outside the gateway process succeed
|
||||
end-to-end against the authenticated surface — verified by the
|
||||
migrated `grpcapi/server_test.go` and `command_routing_integration_test.go`
|
||||
scenarios driving the Connect client over loopback h2c;
|
||||
- server-streaming `SubscribeEvents` works over Connect with the
|
||||
signed `gateway.server_time` bootstrap event delivered first —
|
||||
verified by `TestSubscribeEventsValidEnvelopeSendsBootstrapEventAndWaitsForCancellation`;
|
||||
- the unified listener still natively accepts gRPC and gRPC-Web
|
||||
framing for any future native client (Connect-Go's documented
|
||||
multi-protocol support);
|
||||
- the Connect handler shares the same upstream business code as the
|
||||
unified listener — there is exactly one decorator stack
|
||||
(`grpcapi.NewServer` → `s.service`).
|
||||
|
||||
- Connect unary integration test against a running gateway+backend;
|
||||
- Connect streaming integration test asserting at least one push event
|
||||
delivery;
|
||||
- existing gateway test suite stays green.
|
||||
Targeted tests (delivered):
|
||||
|
||||
- Connect unary integration tests in `gateway/internal/grpcapi/`
|
||||
exercising the full envelope → signature → freshness/replay →
|
||||
rate-limit → routing pipeline through the new Connect transport;
|
||||
- Connect streaming integration tests asserting bootstrap-event
|
||||
delivery, replay rejection on stream open, and shutdown closure;
|
||||
- the existing gateway test suite (`go test ./gateway/...`) stays
|
||||
green.
|
||||
|
||||
Decision deviation note: the planned standalone
|
||||
`gateway/internal/grpcapi/connect_server_test.go` was not added as a
|
||||
separate file because the migrated `*_test.go` files in the same
|
||||
package already cover unary happy + streaming bootstrap + protocol-
|
||||
version reject through the Connect client. A duplicate file would not
|
||||
add coverage. Future contributors looking for "the Connect tests" can
|
||||
read any file in `gateway/internal/grpcapi/` — they all use the
|
||||
Connect client now.
|
||||
|
||||
## Phase 5. WASM Build, `WasmCore` Adapter, `GalaxyClient` Skeleton
|
||||
|
||||
|
||||
Reference in New Issue
Block a user