x402: deploy payment gateway in front of Anubis on all 9 public sites

Adds modules/kubernetes/x402_instance/ — a small Go reverse proxy
(forgejo.viktorbarzin.me/viktor/x402-gateway:ce333419) that selectively
issues HTTP 402 Payment Required to declared AI-bot User-Agents and
validates X-PAYMENT headers against a Coinbase x402 facilitator.
Browsers are forwarded transparently to Anubis (which then handles the
JS PoW gate as before).

Wired into all nine Anubis-fronted sites:
  ingress -> x402-X -> anubis-X -> backend

While `wallet_address` is empty the gateway runs in DRY_RUN — every
request is transparent-proxied, no 402s issued. This lets the pod sit
in the request path with zero behavioural impact today; flipping the
wallet variable in the per-stack module call activates payment-required
mode for AI-bot UAs.

Default config: Base mainnet USDC, $0.01/req, x402.org/facilitator,
catch-all UA list (ClaudeBot|GPTBot|Bytespider|meta-externalagent|
PerplexityBot|GoogleOther|cohere-ai|Diffbot|Amazonbot|
Applebot-Extended|FacebookBot|ImagesiftBot|YouBot|anthropic-ai|
Claude-Web|petalbot|spawning-ai|scrapy|python-requests).

Verified post-apply: 9/9 pods Running, all 9 sites still serve the
Anubis challenge to plain curl with identical TTFB, x402 logs confirm
"dry_run":true on every instance.
This commit is contained in:
Viktor Barzin 2026-05-10 02:25:57 +00:00
parent a1b659de2a
commit ce4a75d79a
10 changed files with 381 additions and 20 deletions

View file

@ -122,12 +122,20 @@ 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.anubis.service_name
port = module.anubis.service_port
service_name = module.x402.service_name
port = module.x402.service_port
full_host = "viktorbarzin.me"
dns_type = "proxied"
tls_secret_name = var.tls_secret_name
@ -146,8 +154,8 @@ module "ingress-www" {
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
service_name = module.x402.service_name
port = module.x402.service_port
full_host = "www.viktorbarzin.me"
tls_secret_name = var.tls_secret_name
anti_ai_scraping = false

View file

@ -111,13 +111,20 @@ 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.anubis.service_name
port = module.anubis.service_port
service_name = module.x402.service_name
port = module.x402.service_port
tls_secret_name = var.tls_secret_name
anti_ai_scraping = false
extra_annotations = {

View file

@ -268,13 +268,20 @@ 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.anubis.service_name
port = module.anubis.service_port
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

View file

@ -144,14 +144,21 @@ 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.anubis.service_name
port = module.anubis.service_port
service_name = module.x402.service_name
port = module.x402.service_port
tls_secret_name = var.tls_secret_name
anti_ai_scraping = false
extra_annotations = {

View file

@ -91,13 +91,20 @@ 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.anubis.service_name
port = module.anubis.service_port
service_name = module.x402.service_name
port = module.x402.service_port
tls_secret_name = var.tls_secret_name
anti_ai_scraping = false
extra_annotations = {

View file

@ -110,13 +110,20 @@ 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.anubis.service_name
port = module.anubis.service_port
service_name = module.x402.service_name
port = module.x402.service_port
tls_secret_name = var.tls_secret_name
anti_ai_scraping = false
extra_annotations = {

View file

@ -138,14 +138,21 @@ 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.anubis.service_name
port = module.anubis.service_port
service_name = module.x402.service_name
port = module.x402.service_port
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'"

View file

@ -339,13 +339,20 @@ 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.anubis.service_name
port = module.anubis.service_port
service_name = module.x402.service_name
port = module.x402.service_port
anti_ai_scraping = false
tls_secret_name = var.tls_secret_name
extra_annotations = {

View file

@ -109,13 +109,20 @@ 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.anubis.service_name
port = module.anubis.service_port
service_name = module.x402.service_name
port = module.x402.service_port
anti_ai_scraping = false
extra_annotations = {
"gethomepage.dev/enabled" = "true"