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

91 lines
3.4 KiB
Markdown

# 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
```sh
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.