feat: allow ingress to share a single port with pull_api (#183) - #184
Merged
Conversation
ingress.listen may now equal pull_api.listen (and, transitively, admin_api.listen) so inbound webhooks and the Pull/Admin APIs can be served on one prefix-muxed listener, mirroring the existing pull_api==admin_api shared listener. Ingress serves its bare route paths as the default handler; the API servers serve under their prefixes. Strictly opt-in via matching listen addresses -- the multi-port topology remains the default and recommended posture. - config: generalized shared-listener validation (validateSharedListeners) requiring non-empty/distinct/non-overlapping API prefixes, rejecting any ingress route path that collides with a co-listening API prefix, and enforcing identical TLS across a shared address; pull_api.grpc_listen and observability.metrics.listen stay dedicated. New IngressShared compiled flag (restart-required on toggle), surfaced as ingress_shared in the MCP config summaries. - runtime: group HTTP components by listen address and mux shared groups via a generalized prefixMux (replaces sharedPrefixMux). - tests: 5 config validation cases + single-port e2e round-trip (TestBinaryE2E_SinglePortIngressPull). - docs: configuration.md, DESIGN.md, CHANGELOG.md, BACKLOG.md, STATUS.md. Motivation: single-port deployments behind reverse-proxy path-routing and single-published-port orchestrators. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CONTRIBUTING: development setup now says Go 1.26.x to match go.mod (go 1.26.4); the 1.25.x note was stale after the 2.8.1 toolchain bump. - CHANGELOG: promote the ingress single-port shared-listener entry from [Unreleased] to [2.9.0] - 2026-07-01. - STATUS: current release -> v2.9.0. Co-Authored-By: Claude Opus 4.8 <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.
Summary
Lets the
ingresslistener share a single port withpull_api(and, transitively,admin_api) via path-prefix muxing — the same mechanism that already backs thepull_api == admin_apishared listener. Previouslyingress.listensharing an address with pull/admin was a hard compile error, forcing multi-port deployments even when a single published port would be simpler (reverse-proxy path-routing, single-published-port orchestrators).When
ingress.listenequalspull_api.listen(and/oradmin_api.listen), the components are served on onenet.Listener:/webhooks/...) as the default handler;prefixvalues (/pull,/admin);Strictly opt-in (inferred from equal
listenaddresses); the multi-port topology remains the default and recommended posture. No new DSL keyword.Related Issues
Closes #183
Scope
What changed
Config (
internal/config/compile.go)ingress.listen must not share …guard.validateSharedListenersgeneralizes shared-listener validation across ingress/pull/admin grouped by address. For every shared address it enforces:prefixvalues (reuseshasPathPrefix);pull_api.grpc_listenandobservability.metrics.listenkeep their existing dedicated-listener guards.Compiled.IngressSharedflag; surfaced asingress_sharedin the MCPconfig_compilesummary + runtime-control status; added to the restart-required reload set.Runtime (
internal/app/run.go)http.Server. Multi-member groups are muxed by a generalizedprefixMux(replacessharedPrefixMux) with ingress as the default handler. Per-component tracing/access-log wrapping preserved.Validation
go test ./...passed locally (Go 1.26.4)CHANGELOG.mdBACKLOG.md/STATUS.mdTests added:
internal/config: valid single-port config compiles and sets the flag; all-three-shared; ingress-route-path/prefix collision rejected; empty prefix rejected; TLS mismatch rejected. Repurposed the obsoleteIngressMustNotShareListenertest intoIngressShareWithoutPrefixRejected.internal/e2e:TestBinaryE2E_SinglePortIngressPull— POST to/webhooks/...and dequeue/ack via the Pull API on the same port.Manual check against a real binary:
Security and Operations
pull_api == admin_apishared listener behavior are unchanged; the feature only activates on explicitly equallistenaddresses.Notes for Reviewers
SharedListener/else into address-based grouping. It reproduces the previous topology for the default andpull_api == admin_apicases (verified by the existing e2e/unit suites) and adds the ingress-sharing cases.IngressSharedin the reload gate).🤖 Generated with Claude Code