Reverse proxy that lets clients pay for requests beyond a free rate limit using Cashu, L402 and x402. It combines Caddy-style configuration and automatic HTTPS with payment handling at the proxy.
  • Rust 86.2%
  • Python 12.6%
  • Nix 0.9%
  • Just 0.3%
Find a file
tsk 170047eb1b
Some checks failed
Checks / checks (push) Failing after 39s
fix(proxy): bound connections and redirects before site admission
Cap connections across both listeners at 1,024 globally and 64 per client,
including TLS handshakes. Require HTTP/2 negotiation and the first request
within 10 seconds, then close idle connections after 30 seconds.

Give HTTPS redirects a separate 10/s, burst-20 allowance and aggregate
their logs without request paths. Preserve the normal HTTPS allowance.
Only count Authorization as a Lightning payment attempt when L402 is
enabled, preserving backend authentication on x402-only sites.

Add regression coverage for connection admission and release, startup
deadlines, backend authorization, and redirect limits and logging.
Document the transport limits and separate redirect allowance.
2026-10-04 10:19:42 +00:00
.github/workflows init 2026-09-14 21:05:12 +00:00
crates/geata fix(proxy): bound connections and redirects before site admission 2026-10-04 10:19:42 +00:00
docs fix(proxy): bound connections and redirects before site admission 2026-10-04 10:19:42 +00:00
examples fix(proxy): bound connections and redirects before site admission 2026-10-04 10:19:42 +00:00
tests fix(proxy): bound connections and redirects before site admission 2026-10-04 10:19:42 +00:00
.gitignore init 2026-09-14 21:05:12 +00:00
Cargo.lock fix(proxy): bound connections and redirects before site admission 2026-10-04 10:19:42 +00:00
Cargo.toml fix(proxy): bound connections and redirects before site admission 2026-10-04 10:19:42 +00:00
DEVELOPMENT.md fix(proxy): bound connections and redirects before site admission 2026-10-04 10:19:42 +00:00
flake.lock init 2026-09-14 21:05:12 +00:00
flake.nix fix(proxy)!: bound request resources and payment verification 2026-09-25 12:18:10 +00:00
justfile fix(proxy)!: bound request resources and payment verification 2026-09-25 12:18:10 +00:00
LICENSE init 2026-09-14 21:05:12 +00:00
README.md fix(proxy): bound connections and redirects before site admission 2026-10-04 10:19:42 +00:00
rust-toolchain.toml init 2026-09-14 21:05:12 +00:00

Geata

Repository note: The main repository is git.cashu.dev/thesimplekid/geata. github.com/thesimplekid/geata is a mirror.

Geata (Irish for “gate”) is a Rust reverse proxy that lets clients pay for requests using Cashu or Lightning (L402 and x402), with an optional free allowance. It combines Caddy-style configuration and automatic HTTPS with payment handling at the proxy.

For sites with payments enabled, every request requires payment unless a free allowance is configured. If configured, each client IP gets a free request allowance. When payment is needed, Geata returns 402 Payment Required with the price and configured payment options. The client can wait for a configured allowance to refill, or pay with a Cashu token or a Lightning invoice and retry with the payment proof. Geata verifies and settles the payment before forwarding the request, so your backend does not need payment logic.

lightning my_node {
    endpoint https://localhost:3536
    macaroon_file /var/lib/ldk-server/bitcoin/macaroons/geata.macaroon
    tls_cert_file /var/lib/ldk-server/tls.crt
    network mainnet
    pay_to YOUR_66_CHARACTER_COMPRESSED_NODE_PUBLIC_KEY
}

example.com {
    rate_limit 10/s burst 20
    pay 2 sat https://mint.example.com
    lightning_pay 2 sat my_node
    lightning_protocols x402 l402
    lightning_headers accept content-encoding content-type cookie range
    reverse_proxy localhost:3000
}

Here, each IP gets a bucket of 20 free requests that refills at 10 per second. Beyond that allowance, clients can pay with Cashu tokens, Lightning x402, or Lightning L402. Each method costs a fixed 2 sats per request attempt; Cashu also requires mint input fees, and Lightning routing fees may apply. The price does not rise with traffic.

Remove rate_limit to charge for every request. Use pay for Cashu, lightning_pay for Lightning, or both to offer either method. These replace the former pay_over_limit and lightning_over_limit directives; the old names are no longer accepted.

Try the hosted example endpoint, or use the Rust client example to walk through a Lightning payment.

The Lightning receiver requires a separate LDK Server with incoming liquidity. Replace its endpoint, credential paths, and node public key with your own values. See Lightning payments for setup and Cashu payments for token requirements and recovery.

Built on Pingora, Geata can also serve plain-text responses directly.

An early implementation for a single Linux server; see current scope.

Payment options

Cashu and Lightning can be offered together or on their own:

Payment method Receiver Where proceeds go
Cashu tokens Cashu mint Geata's Cashu wallet
Lightning L402 LDK Server or Cashu mint Your Lightning node or Geata's Cashu wallet
Lightning x402 LDK Server Your Lightning node

For Lightning, define a named receiver and reference it with lightning_pay. Select lightning_protocols x402 l402 to offer both protocols through LDK Server; the default is x402. To accept Lightning without running your own node, use a Cashu mint receiver with lightning_protocols l402.

Lightning payments are bound to the request and buy one request attempt. Enabling L402 reserves the Authorization header for payment credentials, including on free requests. See Lightning payments for complete configurations, client payment flows, and backend authentication options. To try either protocol against a URL, run the Rust client example.

Try it locally

Build with Nix and run the included Hello world example—no backend required:

nix build path:.
./result/bin/geata run --config examples/Geatafile.local \
  --http-listen 127.0.0.1:8080 --https-listen 127.0.0.1:8443

In another terminal, run curl http://localhost:8080. Other build options include Cargo without Nix.

Use your domain

Replace the example domain, mint URL, backend address, and LDK receiver settings above with your own, then save the configuration as Geatafile. Point your domain's DNS at the server and make ports 80 and 443 reachable. Then run:

./result/bin/geata validate
./result/bin/geata run

The process needs permission to bind those ports. Geata obtains and renews certificates automatically, redirects HTTP to HTTPS, and reloads valid config changes. See HTTPS and operation for deployment details.

Traffic demo

nix develop path:. -c just demo

Open http://127.0.0.1:9090 to watch request rates, generate traffic, and try Cashu payments. The demo guide explains how to choose a test or real mint and set the price.

Documentation