carlos / platform Public

console: attach-door host policy — shape + never platform space

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 7013bbafba3e4a6967a0afe5c516fddac8d2563c parent 0848d75
2 files changed, +57 −0
  • internal/console/domainpolicy.go +30 −0
  • internal/console/domainpolicy_test.go +27 −0
diff --git a/internal/console/domainpolicy.go b/internal/console/domainpolicy.go
new file mode 100644
index 000000000..c29025cf0
--- /dev/null
+++ b/internal/console/domainpolicy.go
@@ -0,0 +1,30 @@
+// Attach-door host policy (#82). #151 exports the shared validation spot
+// for the domains door; when the branches merge these checks land there
+// (Task 8) — the rules themselves are this file's contract:
+// - hostname shape only (ValidHostShape — apexes allowed, lowercase),
+// - never platform space: an attached domain is a foreign domain by
+// definition, so anything equal to or under the deployment's apps
+// domain is refused before any claim is written.
+//
+// First-writer-wins is NOT here: the claim's create-only write is the
+// entire uniqueness check, and its refusal is the door's neutral
+// "host unavailable".
+package console
+
+import (
+ "strings"
+
+ "github.com/carlosframework/platform/internal/provision"
+)
+
+func attachHostRefusal(host, appsDomain string) string {
+ if !provision.ValidHostShape(host) {
+ return "not a valid hostname"
+ }
+ if appsDomain != "" {
+ if host == appsDomain || strings.HasSuffix(host, "."+appsDomain) {
+ return "hosts under the platform's apps domain are assigned by the platform, not attached"
+ }
+ }
+ return ""
+}
diff --git a/internal/console/domainpolicy_test.go b/internal/console/domainpolicy_test.go
new file mode 100644
index 000000000..de91a39ce
--- /dev/null
+++ b/internal/console/domainpolicy_test.go
@@ -0,0 +1,27 @@
+// internal/console/domainpolicy_test.go
+package console
+
+import "testing"
+
+func TestAttachHostRefusal(t *testing.T) {
+ cases := []struct {
+ name, host, appsDomain string
+ wantAllowed bool
+ }{
+ {"plain custom fqdn", "www.example.com", "oncarlos.com", true},
+ {"apex", "example.com", "oncarlos.com", true},
+ {"uppercase rejected", "WWW.example.com", "oncarlos.com", false},
+ {"empty host", "", "oncarlos.com", false},
+ {"traversal shape", "../control/serial", "oncarlos.com", false},
+ {"apps domain itself", "oncarlos.com", "oncarlos.com", false},
+ {"under apps domain", "story.bdj.oncarlos.com", "oncarlos.com", false},
+ {"apps domain unset allows customs", "www.example.com", "", true},
+ {"suffix but not label boundary", "notoncarlos.com", "oncarlos.com", true},
+ }
+ for _, c := range cases {
+ got := attachHostRefusal(c.host, c.appsDomain)
+ if (got == "") != c.wantAllowed {
+ t.Errorf("%s: attachHostRefusal(%q,%q) = %q, want allowed=%v", c.name, c.host, c.appsDomain, got, c.wantAllowed)
+ }
+ }
+}