carlos / platform Public

domains: fix wildcard route-collision asymmetry + add cap test

Review findings on the prior commit:
- A named-Route wildcard claim whose own Host is a real local route under a
  DIFFERENT tenant was accepted (the plain-alias half already refused this
  shape; the wildcard half didn't). Now refused with a log, checked right
  after primaryHost resolves and before the primary-route lookup; the
  empty-Route (first-route) path is unaffected since there primaryHost ==
  c.Host already.
- Added TestReconcileWildcardHostCollisionMismatchedTenant covering the new
  gate, and TestReconcileWildcardCap (33 claims, same primary, cap asserted
  at exactly 32 with the lexicographically-last host dropped).
Paul Campbell 4472d7f39ebeeaf116b39ccbfe81a50549d79288 parent fc85b65
2 files changed, +87 −0
  • internal/domains/aliases.go +16 −0
  • internal/domains/aliases_test.go +71 −0
diff --git a/internal/domains/aliases.go b/internal/domains/aliases.go
index 9db796216..27e0e7c61 100644
--- a/internal/domains/aliases.go
+++ b/internal/domains/aliases.go
@@ -94,6 +94,22 @@ func ReconcileAliases(ctx context.Context, s release.Store, reg *registry.Regist
if primaryHost == "" {
primaryHost = c.Host // first-route claim: its own materialized primary
}
+ // A named-Route claim's own Host can itself be a local route (e.g. a
+ // domain already routed AND also carrying a wildcard cert opt-in for
+ // its subdomains). That's fine when the tenant matches; a mismatched
+ // tenant means a different app/account already owns this hostname
+ // directly, and the wildcard claim must not shadow it. The
+ // empty-Route (first-route) case is safe by construction: there
+ // primaryHost == c.Host, so this is the same route checked below.
+ if c.Route != "" {
+ if hostRt, taken := byHost[c.Host]; taken {
+ if hostRt.App != c.App || release.RouteAccount(hostRt) != c.Account {
+ log.Printf("domains: wildcard %s refused: a real route owns that host under %s/%s",
+ c.Host, release.RouteAccount(hostRt), hostRt.App)
+ continue
+ }
+ }
+ }
rt, ok := byHost[primaryHost]
if !ok {
continue // primary not on this box — another box's claim
diff --git a/internal/domains/aliases_test.go b/internal/domains/aliases_test.go
index 9cf52a5d1..fc96b90f1 100644
--- a/internal/domains/aliases_test.go
+++ b/internal/domains/aliases_test.go
@@ -3,6 +3,7 @@ package domains
import (
"context"
"encoding/json"
+ "fmt"
"path/filepath"
"strings"
"testing"
@@ -324,6 +325,76 @@ func TestReconcileDerivesWildcardState(t *testing.T) {
}
}
+func TestReconcileWildcardHostCollisionMismatchedTenant(t *testing.T) {
+ // A named-Route wildcard claim whose own Host is itself a REAL local
+ // route owned by a DIFFERENT tenant: the wildcard row (and any
+ // catch-all) must be refused, same spirit as the plain-alias half
+ // already refusing to shadow a real route. The claim's Route names a
+ // primary that DOES belong to the claim's own tenant, so the
+ // primary-tenancy check alone would pass — the collision on c.Host
+ // itself is the only gate that can catch this case.
+ ctx := context.Background()
+ s := release.NewDirStore(t.TempDir())
+ reg := aliasRegWith(t)
+ addRoute(t, reg, "primary.bab.oncarlos.com", "bab", "appA")
+ addRoute(t, reg, "hostx.example", "bad", "appB") // real route, different tenant
+
+ putClaim(t, s, Claim{Host: "hostx.example", Account: "bab", App: "appA",
+ Route: "primary.bab.oncarlos.com", CreatedAt: "2026-08-13T00:00:00Z",
+ Wildcard: true, Catchall: true})
+
+ if err := ReconcileAliases(ctx, s, reg, func() time.Time { return time.Unix(1000, 0) }); err != nil {
+ t.Fatal(err)
+ }
+ wds, err := reg.ListWildcardDomains()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(wds) != 0 {
+ t.Fatalf("wildcard_domains = %+v, want none (real route under a different tenant owns the host)", wds)
+ }
+ aliases, err := reg.ListAliases()
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, a := range aliases {
+ if strings.HasPrefix(a.Alias, "*.") {
+ t.Fatalf("no catch-all should derive from a refused wildcard claim: %+v", a)
+ }
+ }
+}
+
+func TestReconcileWildcardCap(t *testing.T) {
+ ctx := context.Background()
+ s := release.NewDirStore(t.TempDir())
+ reg := aliasRegWith(t)
+ addRoute(t, reg, "primary.bab.oncarlos.com", "bab", "appA")
+
+ // 33 valid wildcard claims sharing one local primary, with deterministic
+ // zero-padded hosts so bucket-list (lexicographic) order matches numeric
+ // order: w00.example .. w32.example.
+ for i := 0; i < 33; i++ {
+ putClaim(t, s, Claim{Host: fmt.Sprintf("w%02d.example", i), Account: "bab", App: "appA",
+ Route: "primary.bab.oncarlos.com", CreatedAt: "2026-08-13T00:00:00Z", Wildcard: true})
+ }
+
+ if err := ReconcileAliases(ctx, s, reg, func() time.Time { return time.Unix(1000, 0) }); err != nil {
+ t.Fatal(err)
+ }
+ wds, err := reg.ListWildcardDomains()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(wds) != 32 {
+ t.Fatalf("got %d wildcard_domains, want 32 (per-box cap): %+v", len(wds), wds)
+ }
+ for _, w := range wds {
+ if w.Host == "w32.example" {
+ t.Fatalf("w32.example (33rd claim, lexicographically last) should have been dropped over the cap: %+v", wds)
+ }
+ }
+}
+
func TestReconcileWildcardEmptyRouteClaim(t *testing.T) {
// First-route claim (#82's shape): empty Route, the claim's own host IS
// the primary once materialized. Catchall aliases that primary.