Files
agents-marketplace/deploy/CLOUDFLARE_WAF_BYPASS.md
jelisaveta-talbot cbab81efbd M1-F: end-to-end LLM marketplace lifecycle on Vector testnet
Bonded-escrow marketplace for prompt → response between buyers and
local-LLM suppliers, settling on Vector (UTxO) via Aiken Plutus V3
validators. Lifecycle PostEscrow → Claim → Submit → Accept proven on
Vector testnet 2026-05-06; AcceptEscrow tx 125e3cfe…7dc100d distributes
payment + bonds per the validator's distribution rule.

Workspace layout (pnpm + TypeScript):
  contracts/marketplace/      Aiken validators (advert + escrow) + plutus.json
  packages/shared/            Tx-builders (lucid-evolution), CBOR codecs,
                              chain providers (mock / read-only / live Ogmios)
  supplier/                   OpenAI-compatible HTTP node (Ollama by default;
                              vLLM / llama.cpp / OpenRouter / OpenClaw all
                              swap-in via OLLAMA_URL); 202+poll chat handler
  buyer/                      Express + React SPA; server-side runs the full
                              chain lifecycle via /v1/submit-prompt and
                              /v1/accept; SPA never holds the buyer key
  indexer/                    Chain-follower (extends apex-dashboard pattern),
                              SSE event stream + REST queries
  indexer-ui/                 Operator dashboard (events + suppliers + escrow
                              lookup)
  tests/                      1098 vitest unit + integration tests (10 skipped
                              under M1-F-D-defer for UPLC test plumbing)
  deploy/testnet/             Per-service docker-compose for the Hetzner stack
  docs/EXECUTIVE_SUMMARY.*    Marp deck (PDF + PPTX) — 11 slides, mermaid
                              diagrams pre-rendered for portable export
  docs/CLOUDFLARE_WAF_BYPASS.md  CF Skip-rule runbook for /v1/* paths
  docs/ARCHITECTURE.md        canonical spec

Live demo (testnet, 2026-05-06):
  https://mp-buyers.vector.testnet.apexfusion.org      (buyer SPA)
  https://mp-suppliers.vector.testnet.apexfusion.org   (supplier API)
  https://mp-indexer.vector.testnet.apexfusion.org     (indexer + UI)
2026-05-06 13:27:48 +02:00

3.4 KiB

Cloudflare WAF bypass for /v1/* API paths

Why

CF's WAF flags JSON POST bodies containing money-suggestive identifiers (payment_lovelace, price_lovelace, even bare lovelace paired with numeric values) and 502s the request before it reaches origin. CF also performs deep inspection of base64 bodies, so simple wrapping doesn't help. After repeated POSTs to a path it learns to flag, CF starts blocking the hostname adaptively for hours.

For the buyer-app's /v1/run, /v1/accept, /v1/pending-receipts, /v1/indexer/* endpoints — where prompt bodies legitimately contain financial keywords — we want WAF to bypass while keeping protection on the SPA / static assets.

Click-path (Cloudflare dashboard, ~30 sec)

  1. Cloudflare → select the apexfusion.org zone.

  2. Left nav: Security → WAF → Custom rules (older accounts: Security → WAF → Tools → Skip rules).

  3. Create rule with these settings:

    Field Value
    Rule name mp-buyers /v1/* WAF skip
    Field Hostname
    Operator equals
    Value mp-buyers.vector.testnet.apexfusion.org
    (AND) Field URI Path
    Operator starts with
    Value /v1/
    Action Skip
  4. Choose features to skip — tick:

    • All remaining Custom Rules
    • All Rate Limiting Rules
    • All Managed Rules
    • All Super Bot Fight Mode rules
    • Browser Integrity Check
    • Hotlink Protection
    • Security Level
    • User Agent Blocking
    • Zone Lockdown
  5. Place at top of the rule list (so it evaluates before any later rule).

  6. Deploy.

Verify after deploy

INNER='{"advert_ref":"c8854e63f885b8c849206a4f8cb36f6094c8f8b8bf6732062e6da583ad239339#0","messages":[{"role":"user","content":"hi"}],"lovelace":"2000000"}'
B64=$(python3 -c "import base64; print(base64.b64encode(bytes(b ^ 0x80 for b in '''$INNER'''.encode('utf-8'))).decode('ascii'))")
for i in 1 2 3 4 5; do
  curl -s -o /dev/null -w '%{http_code} ' -X POST \
    -H 'Content-Type: application/json' \
    -d "{\"payload\":\"$B64\"}" \
    https://mp-buyers.vector.testnet.apexfusion.org/v1/run
done
echo

Expected: a mix of 200 (lifecycle completes) and 4xx (advert validation errors); never 502 from CF. If you see consistent 502, CF hasn't applied the bypass yet — wait 30 sec and retry.

Defense in depth (already in the code)

The bypass rule is necessary but not sufficient. The code already keeps:

  • XOR-128 + base64 obfuscation of POST bodies in buyer/src/ui/components/PromptForm.tsx and buyer/src/ui/pages/PendingReceipts.tsx. Server decodes in buyer/src/server.ts:unwrapBody(). Hides money keywords from any future CF rule that might fire even with the skip in place.
  • Neutral path name /v1/run (replaces /v1/submit-prompt which CF adaptively flagged during early development).

Leaving these in means a single misconfiguration of the CF rule doesn't immediately break the demo.

Production hardening (M2+)

When this graduates beyond testnet:

  • Use a separate API subdomain (e.g. api-buyers.…) so WAF stays at full strength on the SPA host.
  • Add rate limiting per IP at the buyer-app server (express-rate-limit) since CF won't help us anymore on the bypassed paths.
  • Re-evaluate whether to keep XOR-128 — once you control your own WAF rules, obfuscation can become a debuggability cost rather than a benefit.