1{2 "$schema": "node_modules/wrangler/config-schema.json",3 "name": "bindws-relay",4 "main": "src/index.ts",5 "compatibility_date": "2026-08-01",6 "compatibility_flags": ["nodejs_compat"],7 // Logs and traces go to Workers Observability, and to any OTLP endpoint8 // named in `destinations` once it is created in the dashboard9 // (docs/11-hosting-bindws.md, Observe). Each relay writes one JSON meter10 // line per usage flush; traces cover the Worker's own fetches and11 // binding calls. Sampling keeps the trace bill in check.12 "observability": {13 "enabled": true,14 "logs": { "enabled": true },15 "traces": { "enabled": true, "head_sampling_rate": 0.1 }16 },17 // Metrics: uncomment to write the same meter numbers as Analytics Engine18 // data points, queried with SQL or the Grafana plugin. Workers Paid only.19 // "analytics_engine_datasets": [{ "binding": "METRICS", "dataset": "bindws_relays" }],20 // Production: the apex as a custom domain, every subdomain routed by the21 // wildcard. The wildcard needs proxied placeholder DNS records for *.bind.ws.22 // The third route catches custom domains: with Cloudflare for SaaS a23 // customer's hostname enters this zone, and only a route matching any24 // host sends it to the Worker (docs/11-hosting-bindws.md, Custom domains).25 "routes": [26 { "pattern": "bind.ws", "custom_domain": true },27 { "pattern": "*.bind.ws/*", "zone_id": "491aaf5e1c12976e9828199686a81733" },28 // The catch-all brings custom hostnames to the Worker. It needs Cloudflare29 // for SaaS enabled on the zone first (docs/11-hosting-bindws.md).30 { "pattern": "*/*", "zone_id": "491aaf5e1c12976e9828199686a81733" }31 ],32 "vars": {33 // Relays live at wss://<name>.<DOMAIN>; in `wrangler dev` use <name>.localhost.34 // Requests for any other host fall back to the relay named DEV_RELAY.35 "DOMAIN": "bind.ws",36 "DEV_RELAY": "dev",37 "PUSH_CALLBACK_ORIGINS": "[]",38 // Custom domains: the zone custom hostnames are created in, and the name39 // owners point their CNAME at. CF_API_TOKEN is a secret, set with40 // `wrangler secret put CF_API_TOKEN`; without it the feature is off.41 "ZONE_ID": "491aaf5e1c12976e9828199686a81733",42 "CNAME_TARGET": "customers.bind.ws",43 // Fuel: free allowances per relay per month, and the sats charged beyond44 // them, paid by zaps to LIGHTNING_ADDRESS naming SERVICE_PUBKEY. Leave45 // the address empty to run without top-ups (allowances still apply).46 "LIGHTNING_ADDRESS": "bindws@getalby.com",47 "SERVICE_PUBKEY": "6b559c0816c3b9ec03c2f0cedb8a56efe83746f8c56e543e36e1973c04ef0384",48 "FREE_EVENTS_MB": "100",49 "FREE_MEDIA_MB": "1024",50 "FREE_ACTIVE_HOURS": "100",51 "FREE_ROWS_WRITTEN": "250000",52 // Prices follow what Cloudflare bills us for the same thing, at about53 // 1.5x, converted at 77,000 USD per bitcoin. `node scripts/margin.mjs`54 // checks them against the rate of the day (docs/15-costs-and-margins.md).55 // SQLite storage 0.20 USD/GB-month -> 260 sats56 // R2 storage 0.015 USD/GB-month -> 20 sats57 // DO duration 12.50 USD/M GB-s at 128 MB -> 7.3 sats per hour awake58 // rows written 1.00 USD/M -> 1,300 sats59 // egress free -> not priced60 "SATS_PER_GB_MONTH_EVENTS": "400",61 "SATS_PER_GB_MONTH_MEDIA": "30",62 "SATS_PER_ACTIVE_HOUR": "11",63 "SATS_PER_MILLION_ROWS": "2000"64 },65 "durable_objects": {66 "bindings": [{ "name": "RELAY", "class_name": "Relay" }]67 },68 // Blossom media for every relay, keyed <name>/<sha256> in one bucket.69 "r2_buckets": [{ "binding": "MEDIA", "bucket_name": "bindws-media" }],70 // Custom domains: hostname -> relay name, read by the Worker for any71 // request whose host is outside DOMAIN.72 "kv_namespaces": [{ "binding": "HOSTS", "id": "77f19f73b8e44ee28085991137feb162" }],73 // POST /lease creates objects for anyone, so it is capped per address and overall.74 "ratelimits": [75 { "name": "LEASE_LIMIT_IP", "namespace_id": "1001", "simple": { "limit": 5, "period": 60 } },76 { "name": "LEASE_LIMIT_ALL", "namespace_id": "1002", "simple": { "limit": 60, "period": 60 } }77 ],78 "migrations": [{ "tag": "v1", "new_sqlite_classes": ["Relay"] }],79 // `wrangler dev --env dev`: no routes, so local hostnames (<name>.localhost)80 // reach the Worker unchanged instead of being rewritten to the route host.81 // vars and bindings are not inherited by environments, hence the repeat.82 "env": {83 "dev": {84 "routes": [],85 "vars": {86 "DOMAIN": "bind.ws",87 "DEV_RELAY": "dev",88 "PUSH_CALLBACK_ORIGINS": "[]",89 "ZONE_ID": "",90 "CNAME_TARGET": "customers.bind.ws",91 "LIGHTNING_ADDRESS": "",92 "SERVICE_PUBKEY": "",93 "FREE_EVENTS_MB": "100",94 "FREE_MEDIA_MB": "1024",95 "FREE_ACTIVE_HOURS": "100",96 "FREE_ROWS_WRITTEN": "250000",97 "SATS_PER_GB_MONTH_EVENTS": "400",98 "SATS_PER_GB_MONTH_MEDIA": "30",99 "SATS_PER_ACTIVE_HOUR": "11",100 "SATS_PER_MILLION_ROWS": "2000"101 },102 "durable_objects": {103 "bindings": [{ "name": "RELAY", "class_name": "Relay" }]104 },105 "r2_buckets": [{ "binding": "MEDIA", "bucket_name": "bindws-media" }],106 "kv_namespaces": [{ "binding": "HOSTS", "id": "77f19f73b8e44ee28085991137feb162" }],107 "ratelimits": [108 { "name": "LEASE_LIMIT_IP", "namespace_id": "1001", "simple": { "limit": 5, "period": 60 } },109 { "name": "LEASE_LIMIT_ALL", "namespace_id": "1002", "simple": { "limit": 60, "period": 60 } }110 ]111 }112 }113}