diff --git a/modules/kubernetes/x402_instance/main.tf b/modules/kubernetes/x402_instance/main.tf deleted file mode 100644 index cf60839f..00000000 --- a/modules/kubernetes/x402_instance/main.tf +++ /dev/null @@ -1,297 +0,0 @@ -terraform { - required_providers { - kubernetes = { - source = "hashicorp/kubernetes" - } - } -} - -# Per-site x402 payment gateway. Sits in FRONT of Anubis: -# -# ingress -> x402- Service:8080 -> x402 pod:8923 -> -# anubis- Service:8080 -> anubis pod -> backend -# -# Behaviour: -# - X-PAYMENT header present → validate via Coinbase facilitator, -# forward to Anubis on success, 402 on fail. -# - User-Agent matches AI bot → return 402 with payment requirements. -# - Everything else → forward transparently to Anubis (browsers -# still solve the JS PoW gate as today). -# -# When `wallet_address` is empty, the gateway runs in DRY_RUN mode — every -# request is forwarded transparently. This lets us drop the pod into the -# request path without changing live behaviour while we wait for the -# wallet to be configured. Flip live by setting `wallet_address`. - -variable "name" { - type = string - description = "Short logical name (e.g. \"blog\"). Used to derive Service/Deployment/Secret names as x402-." -} - -variable "namespace" { - type = string - description = "Namespace to deploy into — typically the same as the Anubis instance for the same backend." -} - -variable "target_url" { - type = string - description = "Upstream URL the gateway forwards to. Usually the anubis- Service's cluster DNS." -} - -variable "wallet_address" { - type = string - default = "" - description = "EVM wallet address (0x…) that receives USDC. Empty = DRY_RUN, no 402s issued." -} - -variable "price_label" { - type = string - default = "$0.01" - description = "Human-readable price displayed in payment requirements." -} - -variable "price_usdc_micros" { - type = number - default = 10000 - description = "Price in USDC base units (6 decimals). Default 10_000 = $0.01." -} - -variable "network" { - type = string - default = "base" - description = "x402 network identifier. \"base\", \"base-sepolia\", or any custom paired with USDC_ASSET." -} - -variable "facilitator_url" { - type = string - default = "https://x402.org/facilitator" - description = "Coinbase / community facilitator endpoint that verifies and settles X-PAYMENT headers." -} - -variable "image_tag" { - type = string - default = "ce333419" - description = "forgejo.viktorbarzin.me/viktor/x402-gateway tag. Pin to a release SHA, never :latest." -} - -variable "replicas" { - type = number - default = 1 - description = "Replica count. The gateway is stateless so >1 is fine, but 1 is enough for low-traffic sites." -} - -variable "memory" { - type = string - default = "64Mi" - description = "requests==limits memory. The Go binary idles at ~10MiB." -} - -variable "cpu_request" { - type = string - default = "10m" - description = "CPU request. Per-request work is just an HTTP call to the facilitator." -} - -variable "bot_ua_regex" { - type = string - default = "" - description = "Override for the AI-bot User-Agent regex. Empty = use the gateway's default (ClaudeBot|GPTBot|…)." -} - -locals { - full_name = "x402-${var.name}" - labels = { - "app" = local.full_name - "app.kubernetes.io/name" = "x402-gateway" - "app.kubernetes.io/instance" = local.full_name - "app.kubernetes.io/component" = "payment-gateway" - "app.kubernetes.io/managed-by" = "terraform" - } -} - -resource "kubernetes_deployment" "x402" { - metadata { - name = local.full_name - namespace = var.namespace - labels = local.labels - } - - spec { - replicas = var.replicas - - selector { - match_labels = { app = local.full_name } - } - - strategy { - type = "RollingUpdate" - rolling_update { - max_surge = 1 - max_unavailable = 0 - } - } - - template { - metadata { - labels = local.labels - } - - spec { - image_pull_secrets { - name = "registry-credentials" - } - - container { - name = "x402-gateway" - image = "forgejo.viktorbarzin.me/viktor/x402-gateway:${var.image_tag}" - - port { - name = "http" - container_port = 8923 - } - port { - name = "metrics" - container_port = 9090 - } - - env { - name = "BIND" - value = ":8923" - } - env { - name = "METRICS_BIND" - value = ":9090" - } - env { - name = "TARGET" - value = var.target_url - } - env { - name = "WALLET_ADDRESS" - value = var.wallet_address - } - env { - name = "PRICE_LABEL" - value = var.price_label - } - env { - name = "PRICE_USDC_MICROS" - value = tostring(var.price_usdc_micros) - } - env { - name = "NETWORK" - value = var.network - } - env { - name = "FACILITATOR_URL" - value = var.facilitator_url - } - dynamic "env" { - for_each = var.bot_ua_regex == "" ? [] : [1] - content { - name = "BOT_UA_REGEX" - value = var.bot_ua_regex - } - } - - resources { - requests = { - cpu = var.cpu_request - memory = var.memory - } - limits = { - memory = var.memory - } - } - - liveness_probe { - http_get { - path = "/healthz" - port = "metrics" - } - initial_delay_seconds = 5 - period_seconds = 30 - failure_threshold = 3 - } - readiness_probe { - http_get { - path = "/healthz" - port = "metrics" - } - initial_delay_seconds = 1 - period_seconds = 5 - failure_threshold = 2 - } - - security_context { - run_as_non_root = true - run_as_user = 65532 - run_as_group = 65532 - allow_privilege_escalation = false - read_only_root_filesystem = true - capabilities { - drop = ["ALL"] - } - } - } - } - } - } - - lifecycle { - # KYVERNO_LIFECYCLE_V1: Kyverno admission webhook mutates dns_config with ndots=2 - ignore_changes = [spec[0].template[0].spec[0].dns_config] - } -} - -resource "kubernetes_service" "x402" { - metadata { - name = local.full_name - namespace = var.namespace - labels = local.labels - annotations = { - "prometheus.io/scrape" = "true" - "prometheus.io/path" = "/metrics" - "prometheus.io/port" = "9090" - } - } - - spec { - selector = { app = local.full_name } - port { - name = "http" - port = 8080 - target_port = 8923 - protocol = "TCP" - } - port { - name = "metrics" - port = 9090 - target_port = 9090 - protocol = "TCP" - } - } -} - -resource "kubernetes_pod_disruption_budget_v1" "x402" { - metadata { - name = local.full_name - namespace = var.namespace - } - spec { - min_available = "1" - selector { - match_labels = { app = local.full_name } - } - } -} - -output "service_name" { - value = kubernetes_service.x402.metadata[0].name - description = "ClusterIP service name. Pass this to ingress_factory `service_name` so Traefik routes through the gateway." -} - -output "service_port" { - value = 8080 - description = "Service port — same as the Anubis service for drop-in replacement in ingress_factory." -} diff --git a/stacks/blog/main.tf b/stacks/blog/main.tf index 4afabf3b..d074441f 100644 --- a/stacks/blog/main.tf +++ b/stacks/blog/main.tf @@ -122,20 +122,13 @@ module "anubis" { target_url = "http://${kubernetes_service.blog.metadata[0].name}.${kubernetes_namespace.website.metadata[0].name}.svc.cluster.local" } -# x402 payment gateway in front of Anubis. DRY_RUN until wallet_address is set. -module "x402" { - source = "../../modules/kubernetes/x402_instance" - name = "blog" - namespace = kubernetes_namespace.website.metadata[0].name - target_url = "http://${module.anubis.service_name}.${kubernetes_namespace.website.metadata[0].name}.svc.cluster.local:${module.anubis.service_port}" -} - module "ingress" { - source = "../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.website.metadata[0].name - name = "blog" - service_name = module.x402.service_name - port = module.x402.service_port + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.website.metadata[0].name + name = "blog" + service_name = module.anubis.service_name + port = module.anubis.service_port + extra_middlewares = ["traefik-x402@kubernetescrd"] full_host = "viktorbarzin.me" dns_type = "proxied" tls_secret_name = var.tls_secret_name @@ -151,11 +144,12 @@ module "ingress" { } module "ingress-www" { - source = "../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.website.metadata[0].name - name = "blog-www" - service_name = module.x402.service_name - port = module.x402.service_port + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.website.metadata[0].name + name = "blog-www" + service_name = module.anubis.service_name + port = module.anubis.service_port + extra_middlewares = ["traefik-x402@kubernetescrd"] full_host = "www.viktorbarzin.me" tls_secret_name = var.tls_secret_name anti_ai_scraping = false diff --git a/stacks/cyberchef/main.tf b/stacks/cyberchef/main.tf index aebc7a7d..6b4e71ae 100644 --- a/stacks/cyberchef/main.tf +++ b/stacks/cyberchef/main.tf @@ -111,20 +111,14 @@ module "anubis" { target_url = "http://${kubernetes_service.cyberchef.metadata[0].name}.${kubernetes_namespace.cyberchef.metadata[0].name}.svc.cluster.local" } -module "x402" { - source = "../../modules/kubernetes/x402_instance" - name = "cc" - namespace = kubernetes_namespace.cyberchef.metadata[0].name - target_url = "http://${module.anubis.service_name}.${kubernetes_namespace.cyberchef.metadata[0].name}.svc.cluster.local:${module.anubis.service_port}" -} - module "ingress" { - source = "../../modules/kubernetes/ingress_factory" - dns_type = "proxied" - namespace = kubernetes_namespace.cyberchef.metadata[0].name - name = "cc" - service_name = module.x402.service_name - port = module.x402.service_port + source = "../../modules/kubernetes/ingress_factory" + dns_type = "proxied" + namespace = kubernetes_namespace.cyberchef.metadata[0].name + name = "cc" + service_name = module.anubis.service_name + port = module.anubis.service_port + extra_middlewares = ["traefik-x402@kubernetescrd"] tls_secret_name = var.tls_secret_name anti_ai_scraping = false extra_annotations = { diff --git a/stacks/f1-stream/main.tf b/stacks/f1-stream/main.tf index 3e27dbef..f219bd79 100644 --- a/stacks/f1-stream/main.tf +++ b/stacks/f1-stream/main.tf @@ -268,23 +268,17 @@ module "anubis" { EOT } -module "x402" { - source = "../../modules/kubernetes/x402_instance" - name = "f1" - namespace = kubernetes_namespace.f1-stream.metadata[0].name - target_url = "http://${module.anubis.service_name}.${kubernetes_namespace.f1-stream.metadata[0].name}.svc.cluster.local:${module.anubis.service_port}" -} - module "ingress" { - source = "../../modules/kubernetes/ingress_factory" - dns_type = "non-proxied" - namespace = kubernetes_namespace.f1-stream.metadata[0].name - name = "f1" - service_name = module.x402.service_name - port = module.x402.service_port - tls_secret_name = var.tls_secret_name - exclude_crowdsec = true - anti_ai_scraping = false + source = "../../modules/kubernetes/ingress_factory" + dns_type = "non-proxied" + namespace = kubernetes_namespace.f1-stream.metadata[0].name + name = "f1" + service_name = module.anubis.service_name + port = module.anubis.service_port + tls_secret_name = var.tls_secret_name + exclude_crowdsec = true + anti_ai_scraping = false + extra_middlewares = ["traefik-x402@kubernetescrd"] extra_annotations = { "gethomepage.dev/enabled" = "true" "gethomepage.dev/name" = "F1 Stream" diff --git a/stacks/homepage/main.tf b/stacks/homepage/main.tf index 8494cbf0..d4c84c68 100644 --- a/stacks/homepage/main.tf +++ b/stacks/homepage/main.tf @@ -144,21 +144,15 @@ module "anubis" { target_url = "http://${kubernetes_service.cache_proxy.metadata[0].name}.${kubernetes_namespace.homepage.metadata[0].name}.svc.cluster.local" } -module "x402" { - source = "../../modules/kubernetes/x402_instance" - name = "homepage" - namespace = kubernetes_namespace.homepage.metadata[0].name - target_url = "http://${module.anubis.service_name}.${kubernetes_namespace.homepage.metadata[0].name}.svc.cluster.local:${module.anubis.service_port}" -} - module "ingress" { - source = "../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.homepage.metadata[0].name - name = "homepage" - host = "home" - dns_type = "proxied" - service_name = module.x402.service_name - port = module.x402.service_port + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.homepage.metadata[0].name + name = "homepage" + host = "home" + dns_type = "proxied" + service_name = module.anubis.service_name + port = module.anubis.service_port + extra_middlewares = ["traefik-x402@kubernetescrd"] tls_secret_name = var.tls_secret_name anti_ai_scraping = false extra_annotations = { diff --git a/stacks/jsoncrack/main.tf b/stacks/jsoncrack/main.tf index 6bdc7087..c53c6bbc 100644 --- a/stacks/jsoncrack/main.tf +++ b/stacks/jsoncrack/main.tf @@ -91,20 +91,14 @@ module "anubis" { target_url = "http://${kubernetes_service.jsoncrack.metadata[0].name}.${kubernetes_namespace.jsoncrack.metadata[0].name}.svc.cluster.local" } -module "x402" { - source = "../../modules/kubernetes/x402_instance" - name = "json" - namespace = kubernetes_namespace.jsoncrack.metadata[0].name - target_url = "http://${module.anubis.service_name}.${kubernetes_namespace.jsoncrack.metadata[0].name}.svc.cluster.local:${module.anubis.service_port}" -} - module "ingress" { - source = "../../modules/kubernetes/ingress_factory" - dns_type = "proxied" - namespace = kubernetes_namespace.jsoncrack.metadata[0].name - name = "json" - service_name = module.x402.service_name - port = module.x402.service_port + source = "../../modules/kubernetes/ingress_factory" + dns_type = "proxied" + namespace = kubernetes_namespace.jsoncrack.metadata[0].name + name = "json" + service_name = module.anubis.service_name + port = module.anubis.service_port + extra_middlewares = ["traefik-x402@kubernetescrd"] tls_secret_name = var.tls_secret_name anti_ai_scraping = false extra_annotations = { diff --git a/stacks/kms/main.tf b/stacks/kms/main.tf index b8302e4f..127568a2 100644 --- a/stacks/kms/main.tf +++ b/stacks/kms/main.tf @@ -110,20 +110,14 @@ module "anubis" { target_url = "http://${kubernetes_service.kms-web-page.metadata[0].name}.${kubernetes_namespace.kms.metadata[0].name}.svc.cluster.local" } -module "x402" { - source = "../../modules/kubernetes/x402_instance" - name = "kms" - namespace = kubernetes_namespace.kms.metadata[0].name - target_url = "http://${module.anubis.service_name}.${kubernetes_namespace.kms.metadata[0].name}.svc.cluster.local:${module.anubis.service_port}" -} - module "ingress" { - source = "../../modules/kubernetes/ingress_factory" - dns_type = "non-proxied" - namespace = kubernetes_namespace.kms.metadata[0].name - name = "kms" - service_name = module.x402.service_name - port = module.x402.service_port + source = "../../modules/kubernetes/ingress_factory" + dns_type = "non-proxied" + namespace = kubernetes_namespace.kms.metadata[0].name + name = "kms" + service_name = module.anubis.service_name + port = module.anubis.service_port + extra_middlewares = ["traefik-x402@kubernetescrd"] tls_secret_name = var.tls_secret_name anti_ai_scraping = false extra_annotations = { diff --git a/stacks/privatebin/main.tf b/stacks/privatebin/main.tf index 9145693e..cd8f7130 100644 --- a/stacks/privatebin/main.tf +++ b/stacks/privatebin/main.tf @@ -138,21 +138,15 @@ module "anubis" { target_url = "http://${kubernetes_service.privatebin.metadata[0].name}.${kubernetes_namespace.privatebin.metadata[0].name}.svc.cluster.local" } -module "x402" { - source = "../../modules/kubernetes/x402_instance" - name = "privatebin" - namespace = kubernetes_namespace.privatebin.metadata[0].name - target_url = "http://${module.anubis.service_name}.${kubernetes_namespace.privatebin.metadata[0].name}.svc.cluster.local:${module.anubis.service_port}" -} - module "ingress" { source = "../../modules/kubernetes/ingress_factory" namespace = kubernetes_namespace.privatebin.metadata[0].name name = "privatebin" host = "pb" dns_type = "proxied" - service_name = module.x402.service_name - port = module.x402.service_port + service_name = module.anubis.service_name + port = module.anubis.service_port + extra_middlewares = ["traefik-x402@kubernetescrd"] anti_ai_scraping = false tls_secret_name = var.tls_secret_name custom_content_security_policy = "script-src 'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'" diff --git a/stacks/real-estate-crawler/main.tf b/stacks/real-estate-crawler/main.tf index 678fc44c..b5e0b79a 100644 --- a/stacks/real-estate-crawler/main.tf +++ b/stacks/real-estate-crawler/main.tf @@ -339,20 +339,14 @@ module "anubis" { target_url = "http://realestate-crawler-ui.${kubernetes_namespace.realestate-crawler.metadata[0].name}.svc.cluster.local" } -module "x402" { - source = "../../modules/kubernetes/x402_instance" - name = "wrongmove" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - target_url = "http://${module.anubis.service_name}.${kubernetes_namespace.realestate-crawler.metadata[0].name}.svc.cluster.local:${module.anubis.service_port}" -} - module "ingress" { - source = "../../modules/kubernetes/ingress_factory" - dns_type = "proxied" - namespace = kubernetes_namespace.realestate-crawler.metadata[0].name - name = "wrongmove" - service_name = module.x402.service_name - port = module.x402.service_port + source = "../../modules/kubernetes/ingress_factory" + dns_type = "proxied" + namespace = kubernetes_namespace.realestate-crawler.metadata[0].name + name = "wrongmove" + service_name = module.anubis.service_name + port = module.anubis.service_port + extra_middlewares = ["traefik-x402@kubernetescrd"] anti_ai_scraping = false tls_secret_name = var.tls_secret_name extra_annotations = { diff --git a/stacks/traefik/modules/traefik/main.tf b/stacks/traefik/modules/traefik/main.tf index 4edea94b..8ee65407 100644 --- a/stacks/traefik/modules/traefik/main.tf +++ b/stacks/traefik/modules/traefik/main.tf @@ -10,6 +10,11 @@ variable "auth_fallback_htpasswd" { description = "htpasswd-format string for emergency basicAuth fallback when Authentik is down" sensitive = true } +variable "x402_wallet_address" { + type = string + default = "" + description = "EVM wallet (Base mainnet, 0x…) that receives USDC from x402 payments. Empty = DRY_RUN, gateway always returns 200 to forwardAuth so traffic is unaffected." +} resource "kubernetes_namespace" "traefik" { metadata { @@ -459,6 +464,177 @@ resource "kubernetes_service" "bot_block_proxy" { } } +# x402 payment gateway — shared forwardAuth target for every ingress that +# wants to issue HTTP 402 to declared AI-bot UAs / accept X-PAYMENT for paid +# access. One deployment serves all hosts; each consumer ingress just adds +# `traefik-x402@kubernetescrd` to its middleware chain. +# +# DRY_RUN until `var.x402_wallet_address` is set. While dry-run, every +# auth call returns 200 (allow) so traffic is unaffected. +resource "kubernetes_deployment" "x402_gateway" { + metadata { + name = "x402-gateway" + namespace = kubernetes_namespace.traefik.metadata[0].name + labels = { app = "x402-gateway" } + } + + spec { + replicas = 2 # Stateless; HA across two pods is cheap. + selector { + match_labels = { app = "x402-gateway" } + } + strategy { + type = "RollingUpdate" + rolling_update { + max_surge = 1 + max_unavailable = 0 + } + } + template { + metadata { + labels = { app = "x402-gateway" } + } + spec { + image_pull_secrets { + name = "registry-credentials" + } + topology_spread_constraint { + max_skew = 1 + topology_key = "kubernetes.io/hostname" + when_unsatisfiable = "ScheduleAnyway" + label_selector { + match_labels = { app = "x402-gateway" } + } + } + container { + name = "x402-gateway" + image = "forgejo.viktorbarzin.me/viktor/x402-gateway:f4804d62" + port { + name = "http" + container_port = 8923 + } + port { + name = "metrics" + container_port = 9090 + } + env { + name = "MODE" + value = "forwardauth" + } + env { + name = "BIND" + value = ":8923" + } + env { + name = "METRICS_BIND" + value = ":9090" + } + env { + name = "WALLET_ADDRESS" + value = var.x402_wallet_address + } + env { + name = "PRICE_LABEL" + value = "$0.01" + } + env { + name = "PRICE_USDC_MICROS" + value = "10000" + } + env { + name = "NETWORK" + value = "base" + } + env { + name = "FACILITATOR_URL" + value = "https://x402.org/facilitator" + } + resources { + requests = { + cpu = "10m" + memory = "64Mi" + } + limits = { + memory = "128Mi" + } + } + liveness_probe { + http_get { + path = "/healthz" + port = "metrics" + } + initial_delay_seconds = 5 + period_seconds = 30 + } + readiness_probe { + http_get { + path = "/healthz" + port = "metrics" + } + initial_delay_seconds = 1 + period_seconds = 5 + } + security_context { + run_as_non_root = true + run_as_user = 65532 + run_as_group = 65532 + allow_privilege_escalation = false + read_only_root_filesystem = true + capabilities { + drop = ["ALL"] + } + } + } + } + } + } + + lifecycle { + # KYVERNO_LIFECYCLE_V1: Kyverno admission webhook mutates dns_config with ndots=2 + ignore_changes = [spec[0].template[0].spec[0].dns_config] + } +} + +resource "kubernetes_service" "x402_gateway" { + metadata { + name = "x402-gateway" + namespace = kubernetes_namespace.traefik.metadata[0].name + labels = { app = "x402-gateway" } + annotations = { + "prometheus.io/scrape" = "true" + "prometheus.io/path" = "/metrics" + "prometheus.io/port" = "9090" + } + } + + spec { + selector = { app = "x402-gateway" } + port { + name = "http" + port = 8080 + target_port = 8923 + } + port { + name = "metrics" + port = 9090 + target_port = 9090 + } + } +} + +resource "kubernetes_pod_disruption_budget_v1" "x402_gateway" { + metadata { + name = "x402-gateway" + namespace = kubernetes_namespace.traefik.metadata[0].name + } + spec { + min_available = "1" + selector { + match_labels = { app = "x402-gateway" } + } + } +} + # Resilience proxy for Authentik ForwardAuth # Falls back to basicAuth when Authentik is unreachable resource "kubernetes_secret" "auth_proxy_htpasswd" { diff --git a/stacks/traefik/modules/traefik/middleware.tf b/stacks/traefik/modules/traefik/middleware.tf index 2c8ae8c4..632b3639 100644 --- a/stacks/traefik/modules/traefik/middleware.tf +++ b/stacks/traefik/modules/traefik/middleware.tf @@ -322,6 +322,31 @@ resource "kubernetes_manifest" "middleware_ai_bot_block" { depends_on = [helm_release.traefik] } +# x402 payment-required middleware. Traefik calls the shared x402-gateway +# in this namespace; the gateway returns 200 (allow) to browsers and curl, +# 402 with x402 PaymentRequiredResponse to declared AI-bot UAs (or to any +# request whose X-PAYMENT header fails facilitator validation). +# DRY_RUN until WALLET_ADDRESS is set on the gateway, in which case the +# gateway always returns 200. +resource "kubernetes_manifest" "middleware_x402" { + manifest = { + apiVersion = "traefik.io/v1alpha1" + kind = "Middleware" + metadata = { + name = "x402" + namespace = kubernetes_namespace.traefik.metadata[0].name + } + spec = { + forwardAuth = { + address = "http://x402-gateway.traefik.svc.cluster.local:8080/auth" + trustForwardHeader = true + } + } + } + + depends_on = [helm_release.traefik, kubernetes_service.x402_gateway] +} + # X-Robots-Tag header to discourage compliant AI crawlers resource "kubernetes_manifest" "middleware_anti_ai_headers" { manifest = { diff --git a/stacks/travel_blog/main.tf b/stacks/travel_blog/main.tf index a66d5c15..01fbbb15 100644 --- a/stacks/travel_blog/main.tf +++ b/stacks/travel_blog/main.tf @@ -109,20 +109,14 @@ module "anubis" { target_url = "http://${kubernetes_service.travel-blog.metadata[0].name}.${kubernetes_namespace.travel-blog.metadata[0].name}.svc.cluster.local" } -module "x402" { - source = "../../modules/kubernetes/x402_instance" - name = "travel" - namespace = kubernetes_namespace.travel-blog.metadata[0].name - target_url = "http://${module.anubis.service_name}.${kubernetes_namespace.travel-blog.metadata[0].name}.svc.cluster.local:${module.anubis.service_port}" -} - module "ingress" { - source = "../../modules/kubernetes/ingress_factory" - namespace = kubernetes_namespace.travel-blog.metadata[0].name - name = "travel" - tls_secret_name = var.tls_secret_name - service_name = module.x402.service_name - port = module.x402.service_port + source = "../../modules/kubernetes/ingress_factory" + namespace = kubernetes_namespace.travel-blog.metadata[0].name + name = "travel" + tls_secret_name = var.tls_secret_name + service_name = module.anubis.service_name + port = module.anubis.service_port + extra_middlewares = ["traefik-x402@kubernetescrd"] anti_ai_scraping = false extra_annotations = { "gethomepage.dev/enabled" = "true"