[{"data":1,"prerenderedAt":5087},["ShallowReactive",2],{"navigation_docs":3,"-quayel-gateway-ai-agent-guide":91,"-quayel-gateway-ai-agent-guide-surround":5084},[4],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":90},"Quayel Gateway","i-lucide-network","\u002Fquayel-gateway","quayel-gateway",[10,29,68],{"title":11,"icon":12,"path":13,"stem":14,"children":15},"Getting Started","i-lucide-rocket","\u002Fquayel-gateway\u002Fgetting-started","quayel-gateway\u002F01.getting-started\u002Findex",[16,19,24],{"title":17,"path":13,"stem":14,"icon":18},"Introduction","i-lucide-book-open",{"title":20,"path":21,"stem":22,"icon":23},"Installation","\u002Fquayel-gateway\u002Fgetting-started\u002Finstallation","quayel-gateway\u002F01.getting-started\u002F1.installation","i-lucide-container",{"title":25,"path":26,"stem":27,"icon":28},"Project Structure","\u002Fquayel-gateway\u002Fgetting-started\u002Fproject-structure","quayel-gateway\u002F01.getting-started\u002F2.project-structure","i-lucide-folder-tree",{"title":30,"icon":31,"path":32,"stem":33,"children":34},"Plugins","i-lucide-puzzle","\u002Fquayel-gateway\u002Fplugins","quayel-gateway\u002F02.plugins\u002Findex",[35,38,43,48,53,58,63],{"title":36,"path":32,"stem":33,"icon":37},"Overview","i-lucide-workflow",{"title":39,"path":40,"stem":41,"icon":42},"IP Intelligence","\u002Fquayel-gateway\u002Fplugins\u002Fip-intel","quayel-gateway\u002F02.plugins\u002F1.ip-intel","i-lucide-radar",{"title":44,"path":45,"stem":46,"icon":47},"Firewall","\u002Fquayel-gateway\u002Fplugins\u002Ffirewall","quayel-gateway\u002F02.plugins\u002F2.firewall","i-lucide-shield",{"title":49,"path":50,"stem":51,"icon":52},"Rate Limiting","\u002Fquayel-gateway\u002Fplugins\u002Frate-limiting","quayel-gateway\u002F02.plugins\u002F3.rate-limiting","i-lucide-gauge",{"title":54,"path":55,"stem":56,"icon":57},"Cache","\u002Fquayel-gateway\u002Fplugins\u002Fcache","quayel-gateway\u002F02.plugins\u002F4.cache","i-lucide-database",{"title":59,"path":60,"stem":61,"icon":62},"Load Balancer","\u002Fquayel-gateway\u002Fplugins\u002Fload-balancer","quayel-gateway\u002F02.plugins\u002F5.load-balancer","i-lucide-scale",{"title":64,"path":65,"stem":66,"icon":67},"Auto-SSL","\u002Fquayel-gateway\u002Fplugins\u002Fauto-ssl","quayel-gateway\u002F02.plugins\u002F6.auto-ssl","i-lucide-lock",{"title":69,"icon":70,"path":71,"stem":72,"children":73},"AI","i-lucide-bot","\u002Fquayel-gateway\u002Fai","quayel-gateway\u002F03.ai\u002Findex",[74,75,80,85],{"title":36,"path":71,"stem":72,"icon":70},{"title":76,"path":77,"stem":78,"icon":79},"The Quayel Gateway Skill","\u002Fquayel-gateway\u002Fai\u002Fskill","quayel-gateway\u002F03.ai\u002F1.skill","i-lucide-wand-sparkles",{"title":81,"path":82,"stem":83,"icon":84},"Installing the Skill","\u002Fquayel-gateway\u002Fai\u002Finstallation","quayel-gateway\u002F03.ai\u002F2.installation","i-lucide-download",{"title":86,"path":87,"stem":88,"icon":89},"AI Agent Guide","\u002Fquayel-gateway\u002Fai\u002Fagent-guide","quayel-gateway\u002F03.ai\u002F3.agent-guide","i-lucide-braces",false,{"id":92,"title":86,"body":93,"description":5077,"extension":5078,"links":5079,"meta":5080,"navigation":5081,"path":87,"seo":5082,"stem":88,"__hash__":5083},"docs\u002Fquayel-gateway\u002F03.ai\u002F3.agent-guide.md",{"type":94,"value":95,"toc":5043},"minimark",[96,101,122,127,147,257,261,271,421,426,908,911,917,925,929,946,950,955,1018,1022,1049,1097,1101,1133,1196,1200,1232,1236,1241,1255,1259,1285,1289,1323,1358,1362,1391,1395,1400,1404,1407,1668,1672,2003,2014,2368,2371,2410,2414,2425,2466,2482,2504,2510,2550,2562,2565,2664,2670,2758,2774,2778,2782,3346,3350,3769,3773,4107,4111,4527,4531,4680,4690,4694,4711,4714,4846,4850,4957,4961,5039],[97,98,100],"h1",{"id":99},"ai-agent-guide-quayel-gateway","AI Agent Guide — Quayel Gateway",[102,103,104,108,109,112,113,117,118,121],"note",{},[105,106,107],"strong",{},"Audience:"," LLM agents and automation. This page is the machine-oriented contract for generating, validating, and operating Quayel Gateway configs. Human prose lives in the other pages; ",[105,110,111],{},"when they disagree with this page, this page wins"," (it is verified against ",[114,115,116],"code",{},"src\u002Fconfig\u002Fschema.rs",", ",[114,119,120],{},"src\u002Fconfig\u002Floader.rs"," and the release binary).",[123,124,126],"h2",{"id":125},"_1-what-this-program-is","1. What this program is",[128,129,130,131,134,135,138,139,142,143,146],"p",{},"Quayel Gateway is a single-binary reverse proxy \u002F API gateway (Rust, Pingora core).\nRun: ",[114,132,133],{},"quayel-gateway [OPTIONS] [CONFIG_PATH]",". One JSON file describes everything: listeners, client-IP resolution, and a plugin pipeline (",[114,136,137],{},"ip_intel → firewall → rate_limiting → load_balancer → cache → logging",", plus ",[114,140,141],{},"auto_ssl"," for TLS). All IP-intelligence data is embedded in the binary. There are ",[105,144,145],{},"no"," external services to install (no Redis\u002Fetcd\u002FDB).",[148,149,150,163],"table",{},[151,152,153],"thead",{},[154,155,156,160],"tr",{},[157,158,159],"th",{},"Fact",[157,161,162],{},"Value",[164,165,166,178,201,222,237,245],"tbody",{},[154,167,168,172],{},[169,170,171],"td",{},"Binary",[169,173,174,177],{},[114,175,176],{},"target\u002Frelease\u002Fquayel-gateway"," (~153 MB, data embedded)",[154,179,180,183],{},[169,181,182],{},"CLI",[169,184,185,188,189,192,193,196,197,200],{},[114,186,187],{},"--workers \u003CN>"," (default: all CPU cores), ",[114,190,191],{},"--help","; positional ",[114,194,195],{},"CONFIG_PATH"," (default ",[114,198,199],{},"config.json",")",[154,202,203,206],{},[169,204,205],{},"Env",[169,207,208,211,212,215,216,196,219,200],{},[114,209,210],{},"RUST_LOG"," (",[114,213,214],{},"info"," default), ",[114,217,218],{},"QUAYEL_IP_INTEL_CACHE_SIZE",[114,220,221],{},"65536",[154,223,224,227],{},[169,225,226],{},"Build",[169,228,229,232,233,236],{},[114,230,231],{},".\u002Fsetup.sh"," then ",[114,234,235],{},"cargo build --release"," (Linux x86_64)",[154,238,239,242],{},[169,240,241],{},"Config reload",[169,243,244],{},"Automatic — the file is watched; a valid edit hot-reloads (no restart). Invalid edits are rejected and the old config keeps serving.",[154,246,247,250],{},[169,248,249],{},"Logs",[169,251,252,253,256],{},"stderr = JSON tracing; access log = JSONL file (if ",[114,254,255],{},"plugins.logging"," configured)",[123,258,260],{"id":259},"_2-config-generation-contract","2. Config generation contract",[128,262,263,264,266,267,270],{},"When you generate a ",[114,265,199],{},", follow this checklist ",[105,268,269],{},"every time",":",[272,273,274,310,328,348,357,369,374,386,396,402],"ol",{},[275,276,277,280,281,117,284,117,287,117,290,293,294,297,298,301,302,305,306,309],"li",{},[105,278,279],{},"Required top-level fields:"," ",[114,282,283],{},"gateway_id",[114,285,286],{},"gateway_name",[114,288,289],{},"server_name",[114,291,292],{},"config_version"," (all strings), ",[114,295,296],{},"listen"," (object), ",[114,299,300],{},"plugins"," (object). ",[114,303,304],{},"client_ip"," is optional (defaults to ",[114,307,308],{},"peer_ip",").",[275,311,312,315,316,319,320,323,324,327],{},[105,313,314],{},"At least one"," of ",[114,317,318],{},"listen.http"," \u002F ",[114,321,322],{},"listen.https"," must be set (",[114,325,326],{},"\"host:port\""," strings).",[275,329,330,333,334,117,337,117,340,343,344,347],{},[105,331,332],{},"Enums are snake_case strings"," — see §4. A single wrong enum value makes the whole config fail to load (serde error at startup). Examples of wrong values seen in the wild: ",[114,335,336],{},"\"round-robin\"",[114,338,339],{},"\"HTTP\"",[114,341,342],{},"\"mode\": \"http\""," (health check), ",[114,345,346],{},"\"Header_Index\"",".",[275,349,350,211,353,356],{},[105,351,352],{},"Firewall rules must have ≥ 1 condition",[114,354,355],{},"conditions: []"," is rejected).",[275,358,359,280,362,365,366,347],{},[105,360,361],{},"Rate limit rules:",[114,363,364],{},"limit > 0"," and ",[114,367,368],{},"window_seconds > 0",[275,370,371],{},[105,372,373],{},"Each load balancer needs ≥ 1 target.",[275,375,376,378,379,382,383,347],{},[105,377,64],{},", if enabled, requires ",[114,380,381],{},"email"," (string) and non-empty ",[114,384,385],{},"domains",[275,387,388,391,392,395],{},[105,389,390],{},"Rule\u002Fordering semantics:"," rules inside a plugin are evaluated ",[105,393,394],{},"in array order, first match wins",". Put the most specific rules first.",[275,397,398,401],{},[105,399,400],{},"Rule ids"," must be unique strings (they appear in logs, block responses, and rate-limit counters).",[275,403,404,405,408,409,416,417,420],{},"Unknown JSON fields are ",[105,406,407],{},"ignored"," by the gateway, but the provided JSON Schema (",[410,411,413],"a",{"href":412},"\u002Fconfig.schema.json",[114,414,415],{},"config.schema.json",") is strict (",[114,418,419],{},"additionalProperties: false",") so typos get caught. Validate before deploying (§7).",[422,423,425],"h3",{"id":424},"minimal-valid-config-proxy-to-one-backend","Minimal valid config (proxy to one backend)",[427,428,433],"pre",{"className":429,"code":430,"language":431,"meta":432,"style":432},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"gateway_id\": \"gw-01\",\n  \"gateway_name\": \"default\",\n  \"server_name\": \"api.example.com\",\n  \"config_version\": \"1\",\n  \"listen\": { \"http\": \"0.0.0.0:8080\" },\n  \"plugins\": {\n    \"ip_intel\": { \"enabled\": true },\n    \"load_balancer\": {\n      \"enabled\": true,\n      \"load_balancers\": [{\n        \"id\": \"default\",\n        \"name\": \"Backend\",\n        \"conditions\": [],\n        \"algorithm\": { \"type\": \"round_robin\" },\n        \"targets\": [\n          { \"id\": \"t1\", \"host\": \"127.0.0.1\", \"port\": 3000 }\n        ],\n        \"host_header\": { \"mode\": \"visitor\" },\n        \"health_check\": { \"mode\": \"off\" }\n      }]\n    }\n  }\n}\n","json","",[114,434,435,444,470,490,510,530,564,578,608,622,637,652,674,695,710,743,758,815,821,853,884,890,896,902],{"__ignoreMap":432},[436,437,440],"span",{"class":438,"line":439},"line",1,[436,441,443],{"class":442},"sMK4o","{\n",[436,445,447,450,453,456,458,461,465,467],{"class":438,"line":446},2,[436,448,449],{"class":442},"  \"",[436,451,283],{"class":452},"spNyl",[436,454,455],{"class":442},"\"",[436,457,270],{"class":442},[436,459,460],{"class":442}," \"",[436,462,464],{"class":463},"sfazB","gw-01",[436,466,455],{"class":442},[436,468,469],{"class":442},",\n",[436,471,473,475,477,479,481,483,486,488],{"class":438,"line":472},3,[436,474,449],{"class":442},[436,476,286],{"class":452},[436,478,455],{"class":442},[436,480,270],{"class":442},[436,482,460],{"class":442},[436,484,485],{"class":463},"default",[436,487,455],{"class":442},[436,489,469],{"class":442},[436,491,493,495,497,499,501,503,506,508],{"class":438,"line":492},4,[436,494,449],{"class":442},[436,496,289],{"class":452},[436,498,455],{"class":442},[436,500,270],{"class":442},[436,502,460],{"class":442},[436,504,505],{"class":463},"api.example.com",[436,507,455],{"class":442},[436,509,469],{"class":442},[436,511,513,515,517,519,521,523,526,528],{"class":438,"line":512},5,[436,514,449],{"class":442},[436,516,292],{"class":452},[436,518,455],{"class":442},[436,520,270],{"class":442},[436,522,460],{"class":442},[436,524,525],{"class":463},"1",[436,527,455],{"class":442},[436,529,469],{"class":442},[436,531,533,535,537,539,541,544,546,550,552,554,556,559,561],{"class":438,"line":532},6,[436,534,449],{"class":442},[436,536,296],{"class":452},[436,538,455],{"class":442},[436,540,270],{"class":442},[436,542,543],{"class":442}," {",[436,545,460],{"class":442},[436,547,549],{"class":548},"sBMFI","http",[436,551,455],{"class":442},[436,553,270],{"class":442},[436,555,460],{"class":442},[436,557,558],{"class":463},"0.0.0.0:8080",[436,560,455],{"class":442},[436,562,563],{"class":442}," },\n",[436,565,567,569,571,573,575],{"class":438,"line":566},7,[436,568,449],{"class":442},[436,570,300],{"class":452},[436,572,455],{"class":442},[436,574,270],{"class":442},[436,576,577],{"class":442}," {\n",[436,579,581,584,587,589,591,593,595,599,601,603,606],{"class":438,"line":580},8,[436,582,583],{"class":442},"    \"",[436,585,586],{"class":548},"ip_intel",[436,588,455],{"class":442},[436,590,270],{"class":442},[436,592,543],{"class":442},[436,594,460],{"class":442},[436,596,598],{"class":597},"sbssI","enabled",[436,600,455],{"class":442},[436,602,270],{"class":442},[436,604,605],{"class":442}," true",[436,607,563],{"class":442},[436,609,611,613,616,618,620],{"class":438,"line":610},9,[436,612,583],{"class":442},[436,614,615],{"class":548},"load_balancer",[436,617,455],{"class":442},[436,619,270],{"class":442},[436,621,577],{"class":442},[436,623,625,628,630,632,634],{"class":438,"line":624},10,[436,626,627],{"class":442},"      \"",[436,629,598],{"class":597},[436,631,455],{"class":442},[436,633,270],{"class":442},[436,635,636],{"class":442}," true,\n",[436,638,640,642,645,647,649],{"class":438,"line":639},11,[436,641,627],{"class":442},[436,643,644],{"class":597},"load_balancers",[436,646,455],{"class":442},[436,648,270],{"class":442},[436,650,651],{"class":442}," [{\n",[436,653,655,658,662,664,666,668,670,672],{"class":438,"line":654},12,[436,656,657],{"class":442},"        \"",[436,659,661],{"class":660},"swJcz","id",[436,663,455],{"class":442},[436,665,270],{"class":442},[436,667,460],{"class":442},[436,669,485],{"class":463},[436,671,455],{"class":442},[436,673,469],{"class":442},[436,675,677,679,682,684,686,688,691,693],{"class":438,"line":676},13,[436,678,657],{"class":442},[436,680,681],{"class":660},"name",[436,683,455],{"class":442},[436,685,270],{"class":442},[436,687,460],{"class":442},[436,689,690],{"class":463},"Backend",[436,692,455],{"class":442},[436,694,469],{"class":442},[436,696,698,700,703,705,707],{"class":438,"line":697},14,[436,699,657],{"class":442},[436,701,702],{"class":660},"conditions",[436,704,455],{"class":442},[436,706,270],{"class":442},[436,708,709],{"class":442}," [],\n",[436,711,713,715,718,720,722,724,726,730,732,734,736,739,741],{"class":438,"line":712},15,[436,714,657],{"class":442},[436,716,717],{"class":660},"algorithm",[436,719,455],{"class":442},[436,721,270],{"class":442},[436,723,543],{"class":442},[436,725,460],{"class":442},[436,727,729],{"class":728},"su27w","type",[436,731,455],{"class":442},[436,733,270],{"class":442},[436,735,460],{"class":442},[436,737,738],{"class":463},"round_robin",[436,740,455],{"class":442},[436,742,563],{"class":442},[436,744,746,748,751,753,755],{"class":438,"line":745},16,[436,747,657],{"class":442},[436,749,750],{"class":660},"targets",[436,752,455],{"class":442},[436,754,270],{"class":442},[436,756,757],{"class":442}," [\n",[436,759,761,764,766,768,770,772,774,777,779,782,784,787,789,791,793,796,798,800,802,805,807,809,812],{"class":438,"line":760},17,[436,762,763],{"class":442},"          {",[436,765,460],{"class":442},[436,767,661],{"class":728},[436,769,455],{"class":442},[436,771,270],{"class":442},[436,773,460],{"class":442},[436,775,776],{"class":463},"t1",[436,778,455],{"class":442},[436,780,781],{"class":442},",",[436,783,460],{"class":442},[436,785,786],{"class":728},"host",[436,788,455],{"class":442},[436,790,270],{"class":442},[436,792,460],{"class":442},[436,794,795],{"class":463},"127.0.0.1",[436,797,455],{"class":442},[436,799,781],{"class":442},[436,801,460],{"class":442},[436,803,804],{"class":728},"port",[436,806,455],{"class":442},[436,808,270],{"class":442},[436,810,811],{"class":597}," 3000",[436,813,814],{"class":442}," }\n",[436,816,818],{"class":438,"line":817},18,[436,819,820],{"class":442},"        ],\n",[436,822,824,826,829,831,833,835,837,840,842,844,846,849,851],{"class":438,"line":823},19,[436,825,657],{"class":442},[436,827,828],{"class":660},"host_header",[436,830,455],{"class":442},[436,832,270],{"class":442},[436,834,543],{"class":442},[436,836,460],{"class":442},[436,838,839],{"class":728},"mode",[436,841,455],{"class":442},[436,843,270],{"class":442},[436,845,460],{"class":442},[436,847,848],{"class":463},"visitor",[436,850,455],{"class":442},[436,852,563],{"class":442},[436,854,856,858,861,863,865,867,869,871,873,875,877,880,882],{"class":438,"line":855},20,[436,857,657],{"class":442},[436,859,860],{"class":660},"health_check",[436,862,455],{"class":442},[436,864,270],{"class":442},[436,866,543],{"class":442},[436,868,460],{"class":442},[436,870,839],{"class":728},[436,872,455],{"class":442},[436,874,270],{"class":442},[436,876,460],{"class":442},[436,878,879],{"class":463},"off",[436,881,455],{"class":442},[436,883,814],{"class":442},[436,885,887],{"class":438,"line":886},21,[436,888,889],{"class":442},"      }]\n",[436,891,893],{"class":438,"line":892},22,[436,894,895],{"class":442},"    }\n",[436,897,899],{"class":438,"line":898},23,[436,900,901],{"class":442},"  }\n",[436,903,905],{"class":438,"line":904},24,[436,906,907],{"class":442},"}\n",[128,909,910],{},"Everything else has working defaults (see §5).",[123,912,914,915,200],{"id":913},"_3-full-config-tree-ground-truth-srcconfigschemars","3. Full config tree (ground truth: ",[114,916,116],{},[427,918,923],{"className":919,"code":921,"language":922},[920],"language-text","GatewayConfig\n├── gateway_id: string                       (required)\n├── gateway_name: string                     (required)\n├── server_name: string                      (required)\n├── config_version: string                   (required; bump it on every change)\n├── listen\n│   ├── http:  \"host:port\" | absent\n│   └── https: \"host:port\" | absent          (≥1 of the two required)\n├── client_ip                                (optional)\n│   ├── source: peer_ip | header | header_index        (default peer_ip)\n│   ├── header: string                       (required when source is header|header_index)\n│   ├── index: integer ≥ 0                   (required when source is header_index)\n│   └── fallback: peer_ip                    (only valid value)\n└── plugins                                  (required object; every sub-plugin optional)\n    ├── ip_intel       { enabled: bool = true }\n    ├── firewall       { enabled = true, rules: FirewallRule[] = [] }\n    ├── rate_limiting  { enabled = true, rules: RateLimitRule[] = [] }\n    ├── cache          { enabled = true, max_object_size_bytes = 10485760, rules: CacheRule[] = [] }\n    ├── load_balancer  { enabled = true, load_balancers: LoadBalancer[] = [] }\n    ├── logging        { enabled = true, mode = \"file\", file?: { path, buffer_size = 8192, flush_interval_ms = 1000 } }\n    └── auto_ssl       { enabled = true, email*, domains*, cert_storage_path = \"\u002Fvar\u002Flib\u002Fquayel\u002Fcerts\", staging = false, renewal_days_before = 30 }\n","text",[114,924,921],{"__ignoreMap":432},[422,926,928],{"id":927},"firewallrule","FirewallRule",[128,930,931,117,934,117,937,117,940,117,943],{},[114,932,933],{},"id*",[114,935,936],{},"name*",[114,938,939],{},"enabled = true",[114,941,942],{},"conditions*: Condition[] (≥1)",[114,944,945],{},"action*: FirewallAction",[422,947,949],{"id":948},"firewallaction","FirewallAction",[128,951,952],{},[114,953,954],{},"type*: allow | block | redirect | set_request_header | set_response_header",[956,957,958,974,1006],"ul",{},[275,959,960,963,964,196,967,970,971],{},[114,961,962],{},"block"," → ",[114,965,966],{},"status",[114,968,969],{},"403","), JSON body ",[114,972,973],{},"{\"error\":\"blocked\",\"status\":403,\"rule_id\":\"\u003Cid>\"}",[275,975,976,963,979,196,981,984,985,988,989,117,992,280,995,998,999,1001,1002,1005],{},[114,977,978],{},"redirect",[114,980,966],{},[114,982,983],{},"302","), ",[114,986,987],{},"location*"," (URL). ⚠️ The field is ",[114,990,991],{},"location",[105,993,994],{},"not",[114,996,997],{},"url"," — ",[114,1000,997],{}," is silently ignored and the gateway redirects to ",[114,1003,1004],{},"\u002F"," instead.",[275,1007,1008,319,1011,963,1014,1017],{},[114,1009,1010],{},"set_request_header",[114,1012,1013],{},"set_response_header",[114,1015,1016],{},"headers*"," (map of string→string)",[422,1019,1021],{"id":1020},"ratelimitrule","RateLimitRule",[128,1023,1024,117,1026,117,1028,117,1030,1033,1034,117,1037,117,1040,117,1043,196,1046,200],{},[114,1025,933],{},[114,1027,936],{},[114,1029,939],{},[114,1031,1032],{},"conditions*: Condition[]"," (empty = every request), ",[114,1035,1036],{},"key*: string | string[]",[114,1038,1039],{},"limit*: integer > 0",[114,1041,1042],{},"window_seconds*: integer > 0",[114,1044,1045],{},"action?: { type: \"block\", status? }",[114,1047,1048],{},"{type:\"block\", status:429}",[956,1050,1051,1060,1078,1084],{},[275,1052,1053,1056,1057,347],{},[114,1054,1055],{},"key"," is any condition path (§6). Array form = composite key, parts joined with ",[114,1058,1059],{},"\":\"",[275,1061,1062,1063,1066,1067,1070,1071,1074,1075,309],{},"JWT keys: ",[114,1064,1065],{},"\"authorization.jwt.user_id\""," → decode Bearer token from the ",[114,1068,1069],{},"authorization"," header, extract claim ",[114,1072,1073],{},"user_id"," (nested claims: ",[114,1076,1077],{},"\"authorization.jwt.data.role\"",[275,1079,1080,1081,347],{},"Legacy template form also accepted: ",[114,1082,1083],{},"\"{req.client_ip}+{http.header.x-api-key}\"",[275,1085,1086,1087,1090,1091,1090,1094,347],{},"Blocked response: ",[114,1088,1089],{},"429"," + ",[114,1092,1093],{},"Retry-After: \u003Cwindow_seconds>",[114,1095,1096],{},"{\"error\":\"rate_limited\",\"status\":429,\"rule_id\":\"...\",\"limit\":N,\"window_seconds\":N}",[422,1098,1100],{"id":1099},"cacherule","CacheRule",[128,1102,1103,117,1105,117,1107,117,1109,1111,1112,117,1115,117,1118,117,1121,117,1124,117,1127,117,1130],{},[114,1104,933],{},[114,1106,936],{},[114,1108,939],{},[114,1110,1032],{}," (empty = always), ",[114,1113,1114],{},"cache: \"eligible\" | \"bypass\" = \"eligible\"",[114,1116,1117],{},"edge_ttl*: { mode: \"origin\" | \"custom\", seconds?: int }",[114,1119,1120],{},"browser_ttl?: { mode: \"bypass\" | \"origin\" | \"custom\", seconds?: int }",[114,1122,1123],{},"cache_key?: { mode: \"default\" | \"custom\", parts?: string[] }",[114,1125,1126],{},"statuses?: int[] = [200, 301, 404]",[114,1128,1129],{},"stale_while_revalidate?: int",[114,1131,1132],{},"stale_if_error?: int",[956,1134,1135,1145,1173,1190],{},[275,1136,1137,1140,1141,1144],{},[114,1138,1139],{},"edge_ttl.mode = \"custom\""," uses ",[114,1142,1143],{},"seconds"," (falls back to origin CDN headers if absent).",[275,1146,1147,1150,1151,1154,1155,1158,1159,1162,1163,319,1166,319,1169,1172],{},[114,1148,1149],{},"edge_ttl.mode = \"origin\""," reads ",[114,1152,1153],{},"s-maxage",", then ",[114,1156,1157],{},"max-age"," from upstream ",[114,1160,1161],{},"Cache-Control","; ",[114,1164,1165],{},"no-store",[114,1167,1168],{},"no-cache",[114,1170,1171],{},"private"," always skip the cache.",[275,1174,1175,1178,1179,1182,1183,1186,1187,347],{},[114,1176,1177],{},"cache_key.mode = \"custom\""," adds ",[114,1180,1181],{},"parts"," (condition paths, e.g. ",[114,1184,1185],{},"[\"ip.country\"]",") to the default key ",[114,1188,1189],{},"scheme|host|path",[275,1191,1192,1193,347],{},"Response header ",[114,1194,1195],{},"Quayel-Cache-Status: NONE|HIT|MISS|BYPASS|STALE|REVALIDATING",[422,1197,1199],{"id":1198},"loadbalancer","LoadBalancer",[128,1201,1202,117,1204,117,1206,117,1208,1210,1211,117,1214,117,1217,196,1220,1162,1222,1225,1226,984,1229],{},[114,1203,933],{},[114,1205,936],{},[114,1207,939],{},[114,1209,1032],{}," (empty = matches all; first LB in the array whose conditions match handles the request), ",[114,1212,1213],{},"algorithm*: { type*: AlgorithmType, key?: string }",[114,1215,1216],{},"targets*: Target[] (≥1)",[114,1218,1219],{},"host_header?: { mode: \"visitor\" | \"custom\" | \"target\", value?: string }",[114,1221,848],{},[114,1223,1224],{},"custom"," requires ",[114,1227,1228],{},"value",[114,1230,1231],{},"health_check?: HealthCheck",[422,1233,1235],{"id":1234},"algorithmtype","AlgorithmType",[128,1237,1238],{},[114,1239,1240],{},"round_robin | weighted_round_robin | least_connections | fast_response | sticky_session",[956,1242,1243],{},[275,1244,1245,1140,1248,1251,1252,309],{},[114,1246,1247],{},"sticky_session",[114,1249,1250],{},"algorithm.key"," (a condition path, e.g. ",[114,1253,1254],{},"\"req.header.X-Session-Id\"",[422,1256,1258],{"id":1257},"target","Target",[128,1260,1261,117,1263,117,1266,1269,1270,1273,1274,984,1277,117,1280,117,1282],{},[114,1262,933],{},[114,1264,1265],{},"host*",[114,1267,1268],{},"port*"," (1–65535), ",[114,1271,1272],{},"protocol: \"http\" | \"https\" = \"http\""," (⚠️ ",[105,1275,1276],{},"lowercase",[114,1278,1279],{},"weight: int = 100",[114,1281,939],{},[114,1283,1284],{},"tls?: { verify: bool = false, sni?: string }",[422,1286,1288],{"id":1287},"healthcheck","HealthCheck",[128,1290,1291,1294,1295,280,1297,1300,1301,1304,1305,1300,1308,117,1311,117,1314,117,1317,117,1320],{},[114,1292,1293],{},"mode*: \"https\" | \"accepts_connections\" | \"off\""," (⚠️ there is ",[105,1296,145],{},[114,1298,1299],{},"\"http\""," mode), ",[114,1302,1303],{},"path: string = \"\u002F\""," (used by ",[114,1306,1307],{},"https",[114,1309,1310],{},"interval_seconds = 5",[114,1312,1313],{},"timeout_ms = 1000",[114,1315,1316],{},"expected_statuses?: int[]",[114,1318,1319],{},"healthy_threshold = 2",[114,1321,1322],{},"unhealthy_threshold = 3",[956,1324,1325,1333,1339],{},[275,1326,1327,1329,1330,347],{},[114,1328,1307],{}," → HTTP(S) GET to the target's protocol\u002Fhost\u002Fport at ",[114,1331,1332],{},"path",[275,1334,1335,1338],{},[114,1336,1337],{},"accepts_connections"," → TCP connect check only.",[275,1340,1341,1342,1345,1346,1349,1350,1353,1354,1357],{},"No healthy target → ",[114,1343,1344],{},"503"," with ",[114,1347,1348],{},"{\"error\":\"no_healthy_upstream\"}",". (With ",[114,1351,1352],{},"health_check: {mode:\"off\"}"," all targets are assumed healthy; a dead backend then yields Pingora's plain ",[114,1355,1356],{},"502",".)",[422,1359,1361],{"id":1360},"condition","Condition",[128,1363,1364,117,1367,117,1370,1373,1374,1004,1377,984,1380,1383,1384,1387,1388,200],{},[114,1365,1366],{},"left*: string (path, §6)",[114,1368,1369],{},"operator*: Operator",[114,1371,1372],{},"value: any"," (omit for ",[114,1375,1376],{},"exists",[114,1378,1379],{},"not_exists",[114,1381,1382],{},"next: \"and\" | \"or\""," (joins to the ",[105,1385,1386],{},"next"," condition; default ",[114,1389,1390],{},"and",[422,1392,1394],{"id":1393},"operator","Operator",[128,1396,1397],{},[114,1398,1399],{},"equals, not_equals, contains, not_contains, starts_with, ends_with, exists, not_exists, in, not_in, greater_than, greater_than_or_equal, less_than, less_than_or_equal, regex",[123,1401,1403],{"id":1402},"_4-enum-cheat-sheet","4. Enum cheat-sheet",[128,1405,1406],{},"Copy exactly — these strings are case- and spelling-sensitive.",[148,1408,1409,1419],{},[151,1410,1411],{},[154,1412,1413,1416],{},[157,1414,1415],{},"Enum",[157,1417,1418],{},"Allowed values",[164,1420,1421,1438,1449,1470,1481,1532,1545,1561,1575,1590,1603,1625,1638,1653],{},[154,1422,1423,1428],{},[169,1424,1425],{},[114,1426,1427],{},"client_ip.source",[169,1429,1430,280,1432,280,1435],{},[114,1431,308],{},[114,1433,1434],{},"header",[114,1436,1437],{},"header_index",[154,1439,1440,1445],{},[169,1441,1442],{},[114,1443,1444],{},"client_ip.fallback",[169,1446,1447],{},[114,1448,308],{},[154,1450,1451,1457],{},[169,1452,1453,1456],{},[114,1454,1455],{},"action.type"," (firewall)",[169,1458,1459,280,1462,280,1464,280,1466,280,1468],{},[114,1460,1461],{},"allow",[114,1463,962],{},[114,1465,978],{},[114,1467,1010],{},[114,1469,1013],{},[154,1471,1472,1477],{},[169,1473,1474,1476],{},[114,1475,1455],{}," (rate limit)",[169,1478,1479],{},[114,1480,962],{},[154,1482,1483,1487],{},[169,1484,1485],{},[114,1486,1393],{},[169,1488,1489,280,1492,280,1495,280,1498,280,1501,280,1504,280,1507,280,1509,280,1511,280,1514,280,1517,280,1520,280,1523,280,1526,280,1529],{},[114,1490,1491],{},"equals",[114,1493,1494],{},"not_equals",[114,1496,1497],{},"contains",[114,1499,1500],{},"not_contains",[114,1502,1503],{},"starts_with",[114,1505,1506],{},"ends_with",[114,1508,1376],{},[114,1510,1379],{},[114,1512,1513],{},"in",[114,1515,1516],{},"not_in",[114,1518,1519],{},"greater_than",[114,1521,1522],{},"greater_than_or_equal",[114,1524,1525],{},"less_than",[114,1527,1528],{},"less_than_or_equal",[114,1530,1531],{},"regex",[154,1533,1534,1538],{},[169,1535,1536],{},[114,1537,1386],{},[169,1539,1540,280,1542],{},[114,1541,1390],{},[114,1543,1544],{},"or",[154,1546,1547,1553],{},[169,1548,1549,1552],{},[114,1550,1551],{},"cache"," (rule)",[169,1554,1555,280,1558],{},[114,1556,1557],{},"eligible",[114,1559,1560],{},"bypass",[154,1562,1563,1568],{},[169,1564,1565],{},[114,1566,1567],{},"edge_ttl.mode",[169,1569,1570,280,1573],{},[114,1571,1572],{},"origin",[114,1574,1224],{},[154,1576,1577,1582],{},[169,1578,1579],{},[114,1580,1581],{},"browser_ttl.mode",[169,1583,1584,280,1586,280,1588],{},[114,1585,1560],{},[114,1587,1572],{},[114,1589,1224],{},[154,1591,1592,1597],{},[169,1593,1594],{},[114,1595,1596],{},"cache_key.mode",[169,1598,1599,280,1601],{},[114,1600,485],{},[114,1602,1224],{},[154,1604,1605,1610],{},[169,1606,1607],{},[114,1608,1609],{},"algorithm.type",[169,1611,1612,280,1614,280,1617,280,1620,280,1623],{},[114,1613,738],{},[114,1615,1616],{},"weighted_round_robin",[114,1618,1619],{},"least_connections",[114,1621,1622],{},"fast_response",[114,1624,1247],{},[154,1626,1627,1632],{},[169,1628,1629],{},[114,1630,1631],{},"target.protocol",[169,1633,1634,280,1636],{},[114,1635,549],{},[114,1637,1307],{},[154,1639,1640,1645],{},[169,1641,1642],{},[114,1643,1644],{},"host_header.mode",[169,1646,1647,280,1649,280,1651],{},[114,1648,848],{},[114,1650,1224],{},[114,1652,1257],{},[154,1654,1655,1660],{},[169,1656,1657],{},[114,1658,1659],{},"health_check.mode",[169,1661,1662,280,1664,280,1666],{},[114,1663,1307],{},[114,1665,1337],{},[114,1667,879],{},[123,1669,1671],{"id":1670},"_5-defaults-reference-fields-you-can-omit","5. Defaults reference (fields you can omit)",[148,1673,1674,1687],{},[151,1675,1676],{},[154,1677,1678,1681,1684],{},[157,1679,1680],{},"Location",[157,1682,1683],{},"Field",[157,1685,1686],{},"Default",[164,1688,1689,1703,1718,1744,1761,1775,1789,1803,1818,1841,1855,1869,1890,1910,1923,1948,1974,1989],{},[154,1690,1691,1695,1698],{},[169,1692,1693],{},[114,1694,304],{},[169,1696,1697],{},"whole object",[169,1699,1700],{},[114,1701,1702],{},"{ \"source\": \"peer_ip\" }",[154,1704,1705,1708,1712],{},[169,1706,1707],{},"every plugin",[169,1709,1710],{},[114,1711,598],{},[169,1713,1714,1717],{},[114,1715,1716],{},"true"," (a plugin object that is present runs)",[154,1719,1720,1732,1739],{},[169,1721,1722,319,1725,319,1728,319,1730],{},[114,1723,1724],{},"firewall",[114,1726,1727],{},"rate_limiting",[114,1729,1551],{},[114,1731,615],{},[169,1733,1734,319,1737],{},[114,1735,1736],{},"rules",[114,1738,644],{},[169,1740,1741],{},[114,1742,1743],{},"[]",[154,1745,1746,1750,1755],{},[169,1747,1748],{},[114,1749,1551],{},[169,1751,1752],{},[114,1753,1754],{},"max_object_size_bytes",[169,1756,1757,1760],{},[114,1758,1759],{},"10485760"," (10 MB)",[154,1762,1763,1766,1770],{},[169,1764,1765],{},"cache rule",[169,1767,1768],{},[114,1769,1551],{},[169,1771,1772],{},[114,1773,1774],{},"\"eligible\"",[154,1776,1777,1779,1784],{},[169,1778,1765],{},[169,1780,1781],{},[114,1782,1783],{},"statuses",[169,1785,1786],{},[114,1787,1788],{},"[200, 301, 404]",[154,1790,1791,1793,1798],{},[169,1792,1765],{},[169,1794,1795],{},[114,1796,1797],{},"cache_key",[169,1799,1800],{},[114,1801,1802],{},"{ \"mode\": \"default\" }",[154,1804,1805,1808,1813],{},[169,1806,1807],{},"rate limit rule",[169,1809,1810],{},[114,1811,1812],{},"action",[169,1814,1815],{},[114,1816,1817],{},"{ \"type\": \"block\", \"status\": 429 }",[154,1819,1820,1822,1832],{},[169,1821,1257],{},[169,1823,1824,319,1827,319,1830],{},[114,1825,1826],{},"protocol",[114,1828,1829],{},"weight",[114,1831,598],{},[169,1833,1834,319,1836,319,1839],{},[114,1835,549],{},[114,1837,1838],{},"100",[114,1840,1716],{},[154,1842,1843,1845,1850],{},[169,1844,1257],{},[169,1846,1847],{},[114,1848,1849],{},"tls",[169,1851,1852],{},[114,1853,1854],{},"{ \"verify\": false, \"sni\": null }",[154,1856,1857,1860,1864],{},[169,1858,1859],{},"load balancer",[169,1861,1862],{},[114,1863,828],{},[169,1865,1866],{},[114,1867,1868],{},"{ \"mode\": \"visitor\" }",[154,1870,1871,1874,1882],{},[169,1872,1873],{},"health check",[169,1875,1876,319,1879],{},[114,1877,1878],{},"interval_seconds",[114,1880,1881],{},"timeout_ms",[169,1883,1884,319,1887],{},[114,1885,1886],{},"5",[114,1888,1889],{},"1000",[154,1891,1892,1894,1902],{},[169,1893,1873],{},[169,1895,1896,319,1899],{},[114,1897,1898],{},"healthy_threshold",[114,1900,1901],{},"unhealthy_threshold",[169,1903,1904,319,1907],{},[114,1905,1906],{},"2",[114,1908,1909],{},"3",[154,1911,1912,1914,1918],{},[169,1913,1873],{},[169,1915,1916],{},[114,1917,1332],{},[169,1919,1920],{},[114,1921,1922],{},"\"\u002F\"",[154,1924,1925,1928,1938],{},[169,1926,1927],{},"logging",[169,1929,1930,319,1932,319,1935],{},[114,1931,839],{},[114,1933,1934],{},"buffer_size",[114,1936,1937],{},"flush_interval_ms",[169,1939,1940,319,1943,319,1946],{},[114,1941,1942],{},"file",[114,1944,1945],{},"8192",[114,1947,1889],{},[154,1949,1950,1952,1963],{},[169,1951,141],{},[169,1953,1954,319,1957,319,1960],{},[114,1955,1956],{},"cert_storage_path",[114,1958,1959],{},"staging",[114,1961,1962],{},"renewal_days_before",[169,1964,1965,319,1968,319,1971],{},[114,1966,1967],{},"\u002Fvar\u002Flib\u002Fquayel\u002Fcerts",[114,1969,1970],{},"false",[114,1972,1973],{},"30",[154,1975,1976,1981,1985],{},[169,1977,1978,1979],{},"firewall action ",[114,1980,962],{},[169,1982,1983],{},[114,1984,966],{},[169,1986,1987],{},[114,1988,969],{},[154,1990,1991,1995,1999],{},[169,1992,1978,1993],{},[114,1994,978],{},[169,1996,1997],{},[114,1998,966],{},[169,2000,2001],{},[114,2002,983],{},[123,2004,2006,2007,2010,2011,2013],{"id":2005},"_6-condition-paths-the-left-key-cache_key-parts-vocabulary","6. Condition paths (the ",[114,2008,2009],{},"left"," \u002F key \u002F cache_key ",[114,2012,1181],{}," vocabulary)",[148,2015,2016,2029],{},[151,2017,2018],{},[154,2019,2020,2023,2026],{},[157,2021,2022],{},"Path",[157,2024,2025],{},"Type",[157,2027,2028],{},"Notes",[164,2030,2031,2048,2067,2085,2103,2121,2136,2150,2167,2182,2196,2208,2222,2237,2279,2293,2305,2324,2340,2354],{},[154,2032,2033,2042,2045],{},[169,2034,2035,2038,2039,200],{},[114,2036,2037],{},"req.host"," (alias ",[114,2040,2041],{},"http.host",[169,2043,2044],{},"string",[169,2046,2047],{},"Host header",[154,2049,2050,2058,2060],{},[169,2051,2052,211,2055,200],{},[114,2053,2054],{},"req.path",[114,2056,2057],{},"http.path",[169,2059,2044],{},[169,2061,2062,2063,2066],{},"Path ",[105,2064,2065],{},"including"," query string",[154,2068,2069,2077,2079],{},[169,2070,2071,211,2074,200],{},[114,2072,2073],{},"req.method",[114,2075,2076],{},"http.method",[169,2078,2044],{},[169,2080,2081,2082],{},"e.g. ",[114,2083,2084],{},"\"POST\"",[154,2086,2087,2095,2097],{},[169,2088,2089,211,2092,200],{},[114,2090,2091],{},"req.scheme",[114,2093,2094],{},"http.scheme",[169,2096,2044],{},[169,2098,2099,319,2101],{},[114,2100,549],{},[114,2102,1307],{},[154,2104,2105,2113,2115],{},[169,2106,2107,211,2110,200],{},[114,2108,2109],{},"req.ip",[114,2111,2112],{},"http.ip",[169,2114,2044],{},[169,2116,2117,2118,2120],{},"Resolved client IP (per ",[114,2119,304],{}," config)",[154,2122,2123,2131,2133],{},[169,2124,2125,211,2128,200],{},[114,2126,2127],{},"req.peer_ip",[114,2129,2130],{},"http.peer_ip",[169,2132,2044],{},[169,2134,2135],{},"Direct TCP peer",[154,2137,2138,2146,2148],{},[169,2139,2140,211,2143,200],{},[114,2141,2142],{},"req.user_agent",[114,2144,2145],{},"http.user_agent",[169,2147,2044],{},[169,2149],{},[154,2151,2152,2157,2159],{},[169,2153,2154],{},[114,2155,2156],{},"req.client_ip",[169,2158,2044],{},[169,2160,2161,2164,2165],{},[105,2162,2163],{},"Rate-limit keys only"," — alias of ",[114,2166,2109],{},[154,2168,2169,2174,2176],{},[169,2170,2171],{},[114,2172,2173],{},"ip.country",[169,2175,2044],{},[169,2177,2178,2179],{},"ISO 3166-1 alpha-2, e.g. ",[114,2180,2181],{},"\"US\"",[154,2183,2184,2192,2194],{},[169,2185,2186,319,2189],{},[114,2187,2188],{},"ip.region",[114,2190,2191],{},"ip.city",[169,2193,2044],{},[169,2195],{},[154,2197,2198,2203,2206],{},[169,2199,2200],{},[114,2201,2202],{},"ip.asn",[169,2204,2205],{},"number",[169,2207],{},[154,2209,2210,2215,2217],{},[169,2211,2212],{},[114,2213,2214],{},"ip.as_org",[169,2216,2044],{},[169,2218,2081,2219],{},[114,2220,2221],{},"\"AMAZON-02\"",[154,2223,2224,2229,2231],{},[169,2225,2226],{},[114,2227,2228],{},"ip.provider",[169,2230,2044],{},[169,2232,2233,2234],{},"Hosting label, e.g. ",[114,2235,2236],{},"\"Hostinger\"",[154,2238,2239,2262,2265],{},[169,2240,2241,280,2244,280,2247,280,2250,280,2253,280,2256,280,2259],{},[114,2242,2243],{},"ip.is_vpn",[114,2245,2246],{},"ip.is_tor",[114,2248,2249],{},"ip.is_datacenter",[114,2251,2252],{},"ip.is_proxy",[114,2254,2255],{},"ip.is_mobile",[114,2257,2258],{},"ip.is_residential",[114,2260,2261],{},"ip.is_residential_proxy",[169,2263,2264],{},"bool",[169,2266,2267,2268,2271,2272,2275,2276],{},"may be ",[114,2269,2270],{},"null"," → use ",[114,2273,2274],{},"equals true",", not ",[114,2277,2278],{},"not_equals false",[154,2280,2281,2286,2288],{},[169,2282,2283],{},[114,2284,2285],{},"ip.network_type",[169,2287,2044],{},[169,2289,2081,2290],{},[114,2291,2292],{},"\"datacenter\"",[154,2294,2295,2300,2302],{},[169,2296,2297],{},[114,2298,2299],{},"ip.risk_score",[169,2301,2205],{},[169,2303,2304],{},"0–100",[154,2306,2307,2319,2321],{},[169,2308,2309,211,2312,2315,2316,200],{},[114,2310,2311],{},"req.header.\u003Cname>",[114,2313,2314],{},"http.header.\u003Cname>",", or bare ",[114,2317,2318],{},"header.\u003Cname>",[169,2320,2044],{},[169,2322,2323],{},"case-insensitive header name",[154,2325,2326,2331,2334],{},[169,2327,2328],{},[114,2329,2330],{},"req.header.\u003Cname>.jwt.\u003Cclaim_path>",[169,2332,2333],{},"any",[169,2335,2336,2337],{},"decode JWT payload (Bearer prefix stripped), navigate dotted claim path, e.g. ",[114,2338,2339],{},"req.header.authorization.jwt.data.role",[154,2341,2342,2350,2352],{},[169,2343,2344,211,2347,200],{},[114,2345,2346],{},"req.query.\u003Cname>",[114,2348,2349],{},"http.query.\u003Cname>",[169,2351,2044],{},[169,2353],{},[154,2355,2356,2364,2366],{},[169,2357,2358,211,2361,200],{},[114,2359,2360],{},"req.cookie.\u003Cname>",[114,2362,2363],{},"http.cookie.\u003Cname>",[169,2365,2044],{},[169,2367],{},[128,2369,2370],{},"Notes:",[956,2372,2373,2383,2399,2402],{},[275,2374,2375,2378,2379,2382],{},[114,2376,2377],{},"ip.*"," paths need ",[114,2380,2381],{},"plugins.ip_intel.enabled: true","; otherwise they resolve to nothing.",[275,2384,2385,2386,2388,2389,319,2391,2393,2394,1004,2396,2398],{},"Unresolvable paths are ",[114,2387,2270],{},": ",[114,2390,1376],{},[114,2392,1379],{}," see this; ",[114,2395,1491],{},[114,2397,1513],{}," won't match.",[275,2400,2401],{},"Coercion: string↔number compares numerically (string parsed as float); no bool coercion.",[275,2403,2404,2406,2407,2409],{},[114,2405,1531],{}," uses Rust ",[114,2408,1531],{}," syntax; an invalid pattern is logged and never matches.",[123,2411,2413],{"id":2412},"_7-validate-before-you-deploy","7. Validate before you deploy",[128,2415,2416,2419,2420,2424],{},[105,2417,2418],{},"Step 1 — syntax + schema"," (JSON Schema at ",[410,2421,2422],{"href":412},[114,2423,415],{},"):",[427,2426,2430],{"className":2427,"code":2428,"language":2429,"meta":432,"style":432},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","python3 - \u003C\u003C'EOF'\nimport json\ncfg = json.load(open(\"config.json\"))            # step 1: syntax\nprint(\"JSON OK\")\nEOF\n","bash",[114,2431,2432,2446,2451,2456,2461],{"__ignoreMap":432},[436,2433,2434,2437,2440,2443],{"class":438,"line":439},[436,2435,2436],{"class":548},"python3",[436,2438,2439],{"class":463}," -",[436,2441,2442],{"class":442}," \u003C\u003C",[436,2444,2445],{"class":442},"'EOF'\n",[436,2447,2448],{"class":438,"line":446},[436,2449,2450],{"class":463},"import json\n",[436,2452,2453],{"class":438,"line":472},[436,2454,2455],{"class":463},"cfg = json.load(open(\"config.json\"))            # step 1: syntax\n",[436,2457,2458],{"class":438,"line":492},[436,2459,2460],{"class":463},"print(\"JSON OK\")\n",[436,2462,2463],{"class":438,"line":512},[436,2464,2465],{"class":442},"EOF\n",[128,2467,2468,2469,2472,2473,2476,2477,2481],{},"If the environment has ",[114,2470,2471],{},"jsonschema"," (or ",[114,2474,2475],{},"check-jsonschema"," \u002F VS Code), validate against ",[410,2478,2479],{"href":412},[114,2480,415],{}," for full enum\u002Frequired checking:",[427,2483,2485],{"className":2427,"code":2484,"language":2429,"meta":432,"style":432},"check-jsonschema --schemafile config.schema.json config.json   # if installed\n",[114,2486,2487],{"__ignoreMap":432},[436,2488,2489,2491,2494,2497,2500],{"class":438,"line":439},[436,2490,2475],{"class":548},[436,2492,2493],{"class":463}," --schemafile",[436,2495,2496],{"class":463}," config.schema.json",[436,2498,2499],{"class":463}," config.json",[436,2501,2503],{"class":2502},"sHwdD","   # if installed\n",[128,2505,2506,2509],{},[105,2507,2508],{},"Step 2 — the gateway's own parser is the final judge."," It validates at startup and exits with a precise error (this is the most reliable check):",[427,2511,2513],{"className":2427,"code":2512,"language":2429,"meta":432,"style":432},"timeout -k 2 3 .\u002Ftarget\u002Frelease\u002Fquayel-gateway config.json\n# exit 0 \u002F listening logs  → config is valid\n# \"Failed to load config: Failed to parse config: unknown variant `X`, expected one of ...\" → fix the enum\n# \"Failed to load config: \u003Crule\u002Ffield> ...\" → fix the semantic error\n",[114,2514,2515,2535,2540,2545],{"__ignoreMap":432},[436,2516,2517,2520,2523,2526,2529,2532],{"class":438,"line":439},[436,2518,2519],{"class":548},"timeout",[436,2521,2522],{"class":463}," -k",[436,2524,2525],{"class":597}," 2",[436,2527,2528],{"class":597}," 3",[436,2530,2531],{"class":463}," .\u002Ftarget\u002Frelease\u002Fquayel-gateway",[436,2533,2534],{"class":463}," config.json\n",[436,2536,2537],{"class":438,"line":446},[436,2538,2539],{"class":2502},"# exit 0 \u002F listening logs  → config is valid\n",[436,2541,2542],{"class":438,"line":472},[436,2543,2544],{"class":2502},"# \"Failed to load config: Failed to parse config: unknown variant `X`, expected one of ...\" → fix the enum\n",[436,2546,2547],{"class":438,"line":492},[436,2548,2549],{"class":2502},"# \"Failed to load config: \u003Crule\u002Ffield> ...\" → fix the semantic error\n",[2551,2552,2553,2554,2557,2558,2561],"warning",{},"Use ",[114,2555,2556],{},"timeout -k"," — a valid config ",[105,2559,2560],{},"starts a server"," and will not exit on its own.",[128,2563,2564],{},"Common startup errors and their meaning:",[148,2566,2567,2577],{},[151,2568,2569],{},[154,2570,2571,2574],{},[157,2572,2573],{},"Message contains",[157,2575,2576],{},"Fix",[164,2578,2579,2589,2599,2612,2624,2634,2644,2654],{},[154,2580,2581,2586],{},[169,2582,2583],{},[114,2584,2585],{},"unknown variant `x`, expected one of `...` ",[169,2587,2588],{},"Wrong enum value; copy from §4",[154,2590,2591,2596],{},[169,2592,2593],{},[114,2594,2595],{},"missing field `y` ",[169,2597,2598],{},"Required field absent; see §3",[154,2600,2601,2606],{},[169,2602,2603],{},[114,2604,2605],{},"client_ip.source is header... but no header specified",[169,2607,2608,2609],{},"Add ",[114,2610,2611],{},"client_ip.header",[154,2613,2614,2619],{},[169,2615,2616],{},[114,2617,2618],{},"client_ip.source is header_index but no index specified",[169,2620,2608,2621],{},[114,2622,2623],{},"client_ip.index",[154,2625,2626,2631],{},[169,2627,2628],{},[114,2629,2630],{},"Firewall rule '\u003Cid>' has no conditions",[169,2632,2633],{},"Give the rule ≥1 condition",[154,2635,2636,2641],{},[169,2637,2638],{},[114,2639,2640],{},"Rate limit rule '\u003Cid>' has zero limit \u002F zero window",[169,2642,2643],{},"Use positive integers",[154,2645,2646,2651],{},[169,2647,2648],{},[114,2649,2650],{},"Load balancer '\u003Cid>' has no targets",[169,2652,2653],{},"Add ≥1 target",[154,2655,2656,2661],{},[169,2657,2658],{},[114,2659,2660],{},"At least one of listen.http or listen.https must be specified",[169,2662,2663],{},"Set a listener",[128,2665,2666,2669],{},[105,2667,2668],{},"Step 3 — smoke test"," (with the gateway running and a backend up):",[427,2671,2673],{"className":2427,"code":2672,"language":2429,"meta":432,"style":432},"curl -s -o \u002Fdev\u002Fnull -w '%{http_code}\\n' http:\u002F\u002Flocalhost:8080\u002Fhealth     # 200 from backend\ncurl -s -i -H 'X-Forwarded-For: 1.2.3.4' http:\u002F\u002Flocalhost:8080\u002Fapi\u002Fx     # check firewall\u002Frate-limit behavior\ntail -1 \u002Fvar\u002Flog\u002Fquayel\u002Faccess.jsonl | python3 -m json.tool               # inspect the decision\n",[114,2674,2675,2707,2732],{"__ignoreMap":432},[436,2676,2677,2680,2683,2686,2689,2692,2695,2698,2701,2704],{"class":438,"line":439},[436,2678,2679],{"class":548},"curl",[436,2681,2682],{"class":463}," -s",[436,2684,2685],{"class":463}," -o",[436,2687,2688],{"class":463}," \u002Fdev\u002Fnull",[436,2690,2691],{"class":463}," -w",[436,2693,2694],{"class":442}," '",[436,2696,2697],{"class":463},"%{http_code}\\n",[436,2699,2700],{"class":442},"'",[436,2702,2703],{"class":463}," http:\u002F\u002Flocalhost:8080\u002Fhealth",[436,2705,2706],{"class":2502},"     # 200 from backend\n",[436,2708,2709,2711,2713,2716,2719,2721,2724,2726,2729],{"class":438,"line":446},[436,2710,2679],{"class":548},[436,2712,2682],{"class":463},[436,2714,2715],{"class":463}," -i",[436,2717,2718],{"class":463}," -H",[436,2720,2694],{"class":442},[436,2722,2723],{"class":463},"X-Forwarded-For: 1.2.3.4",[436,2725,2700],{"class":442},[436,2727,2728],{"class":463}," http:\u002F\u002Flocalhost:8080\u002Fapi\u002Fx",[436,2730,2731],{"class":2502},"     # check firewall\u002Frate-limit behavior\n",[436,2733,2734,2737,2740,2743,2746,2749,2752,2755],{"class":438,"line":472},[436,2735,2736],{"class":548},"tail",[436,2738,2739],{"class":463}," -1",[436,2741,2742],{"class":463}," \u002Fvar\u002Flog\u002Fquayel\u002Faccess.jsonl",[436,2744,2745],{"class":442}," |",[436,2747,2748],{"class":548}," python3",[436,2750,2751],{"class":463}," -m",[436,2753,2754],{"class":463}," json.tool",[436,2756,2757],{"class":2502},"               # inspect the decision\n",[128,2759,2760,2763,2764,2766,2767,2472,2770,2773],{},[105,2761,2762],{},"Step 4 — hot reload check:"," edit ",[114,2765,292],{}," and save the file. Watch stderr for ",[114,2768,2769],{},"\"Config hot-reloaded successfully\"",[114,2771,2772],{},"\"Config file changed, validating...\""," followed by an error, which means the old config is still live).",[123,2775,2777],{"id":2776},"_8-recipes-safe-to-copy","8. Recipes (safe to copy)",[422,2779,2781],{"id":2780},"block-torvpnhigh-risk-geo-restrict-the-api","Block Tor\u002FVPN\u002Fhigh-risk, geo-restrict the API",[427,2783,2785],{"className":429,"code":2784,"language":431,"meta":432,"style":432},"\"firewall\": {\n  \"enabled\": true,\n  \"rules\": [\n    {\n      \"id\": \"block-anon\",\n      \"name\": \"Block anonymizers\",\n      \"conditions\": [\n        { \"left\": \"ip.is_tor\", \"operator\": \"equals\", \"value\": true, \"next\": \"or\" },\n        { \"left\": \"ip.is_vpn\", \"operator\": \"equals\", \"value\": true, \"next\": \"or\" },\n        { \"left\": \"ip.risk_score\", \"operator\": \"greater_than\", \"value\": 60 }\n      ],\n      \"action\": { \"type\": \"block\", \"status\": 403 }\n    },\n    {\n      \"id\": \"geo-api\",\n      \"name\": \"API only from US\u002FCA\u002FGB\",\n      \"conditions\": [\n        { \"left\": \"req.path\", \"operator\": \"starts_with\", \"value\": \"\u002Fapi\u002F\" },\n        { \"left\": \"ip.country\", \"operator\": \"not_in\", \"value\": [\"US\", \"CA\", \"GB\"] }\n      ],\n      \"action\": { \"type\": \"block\", \"status\": 403 }\n    }\n  ]\n}\n",[114,2786,2787,2800,2812,2824,2829,2848,2867,2879,2943,3005,3054,3059,3100,3105,3109,3128,3147,3159,3212,3289,3293,3333,3337,3342],{"__ignoreMap":432},[436,2788,2789,2791,2793,2795,2798],{"class":438,"line":439},[436,2790,455],{"class":442},[436,2792,1724],{"class":463},[436,2794,455],{"class":442},[436,2796,2388],{"class":2797},"sTEyZ",[436,2799,443],{"class":442},[436,2801,2802,2804,2806,2808,2810],{"class":438,"line":446},[436,2803,449],{"class":442},[436,2805,598],{"class":452},[436,2807,455],{"class":442},[436,2809,270],{"class":442},[436,2811,636],{"class":442},[436,2813,2814,2816,2818,2820,2822],{"class":438,"line":472},[436,2815,449],{"class":442},[436,2817,1736],{"class":452},[436,2819,455],{"class":442},[436,2821,270],{"class":442},[436,2823,757],{"class":442},[436,2825,2826],{"class":438,"line":492},[436,2827,2828],{"class":442},"    {\n",[436,2830,2831,2833,2835,2837,2839,2841,2844,2846],{"class":438,"line":512},[436,2832,627],{"class":442},[436,2834,661],{"class":548},[436,2836,455],{"class":442},[436,2838,270],{"class":442},[436,2840,460],{"class":442},[436,2842,2843],{"class":463},"block-anon",[436,2845,455],{"class":442},[436,2847,469],{"class":442},[436,2849,2850,2852,2854,2856,2858,2860,2863,2865],{"class":438,"line":532},[436,2851,627],{"class":442},[436,2853,681],{"class":548},[436,2855,455],{"class":442},[436,2857,270],{"class":442},[436,2859,460],{"class":442},[436,2861,2862],{"class":463},"Block anonymizers",[436,2864,455],{"class":442},[436,2866,469],{"class":442},[436,2868,2869,2871,2873,2875,2877],{"class":438,"line":566},[436,2870,627],{"class":442},[436,2872,702],{"class":548},[436,2874,455],{"class":442},[436,2876,270],{"class":442},[436,2878,757],{"class":442},[436,2880,2881,2884,2886,2888,2890,2892,2894,2896,2898,2900,2902,2904,2906,2908,2910,2912,2914,2916,2918,2920,2922,2924,2927,2929,2931,2933,2935,2937,2939,2941],{"class":438,"line":580},[436,2882,2883],{"class":442},"        {",[436,2885,460],{"class":442},[436,2887,2009],{"class":597},[436,2889,455],{"class":442},[436,2891,270],{"class":442},[436,2893,460],{"class":442},[436,2895,2246],{"class":463},[436,2897,455],{"class":442},[436,2899,781],{"class":442},[436,2901,460],{"class":442},[436,2903,1393],{"class":597},[436,2905,455],{"class":442},[436,2907,270],{"class":442},[436,2909,460],{"class":442},[436,2911,1491],{"class":463},[436,2913,455],{"class":442},[436,2915,781],{"class":442},[436,2917,460],{"class":442},[436,2919,1228],{"class":597},[436,2921,455],{"class":442},[436,2923,270],{"class":442},[436,2925,2926],{"class":442}," true,",[436,2928,460],{"class":442},[436,2930,1386],{"class":597},[436,2932,455],{"class":442},[436,2934,270],{"class":442},[436,2936,460],{"class":442},[436,2938,1544],{"class":463},[436,2940,455],{"class":442},[436,2942,563],{"class":442},[436,2944,2945,2947,2949,2951,2953,2955,2957,2959,2961,2963,2965,2967,2969,2971,2973,2975,2977,2979,2981,2983,2985,2987,2989,2991,2993,2995,2997,2999,3001,3003],{"class":438,"line":610},[436,2946,2883],{"class":442},[436,2948,460],{"class":442},[436,2950,2009],{"class":597},[436,2952,455],{"class":442},[436,2954,270],{"class":442},[436,2956,460],{"class":442},[436,2958,2243],{"class":463},[436,2960,455],{"class":442},[436,2962,781],{"class":442},[436,2964,460],{"class":442},[436,2966,1393],{"class":597},[436,2968,455],{"class":442},[436,2970,270],{"class":442},[436,2972,460],{"class":442},[436,2974,1491],{"class":463},[436,2976,455],{"class":442},[436,2978,781],{"class":442},[436,2980,460],{"class":442},[436,2982,1228],{"class":597},[436,2984,455],{"class":442},[436,2986,270],{"class":442},[436,2988,2926],{"class":442},[436,2990,460],{"class":442},[436,2992,1386],{"class":597},[436,2994,455],{"class":442},[436,2996,270],{"class":442},[436,2998,460],{"class":442},[436,3000,1544],{"class":463},[436,3002,455],{"class":442},[436,3004,563],{"class":442},[436,3006,3007,3009,3011,3013,3015,3017,3019,3021,3023,3025,3027,3029,3031,3033,3035,3037,3039,3041,3043,3045,3047,3049,3052],{"class":438,"line":624},[436,3008,2883],{"class":442},[436,3010,460],{"class":442},[436,3012,2009],{"class":597},[436,3014,455],{"class":442},[436,3016,270],{"class":442},[436,3018,460],{"class":442},[436,3020,2299],{"class":463},[436,3022,455],{"class":442},[436,3024,781],{"class":442},[436,3026,460],{"class":442},[436,3028,1393],{"class":597},[436,3030,455],{"class":442},[436,3032,270],{"class":442},[436,3034,460],{"class":442},[436,3036,1519],{"class":463},[436,3038,455],{"class":442},[436,3040,781],{"class":442},[436,3042,460],{"class":442},[436,3044,1228],{"class":597},[436,3046,455],{"class":442},[436,3048,270],{"class":442},[436,3050,3051],{"class":597}," 60",[436,3053,814],{"class":442},[436,3055,3056],{"class":438,"line":639},[436,3057,3058],{"class":442},"      ],\n",[436,3060,3061,3063,3065,3067,3069,3071,3073,3075,3077,3079,3081,3083,3085,3087,3089,3091,3093,3095,3098],{"class":438,"line":654},[436,3062,627],{"class":442},[436,3064,1812],{"class":548},[436,3066,455],{"class":442},[436,3068,270],{"class":442},[436,3070,543],{"class":442},[436,3072,460],{"class":442},[436,3074,729],{"class":597},[436,3076,455],{"class":442},[436,3078,270],{"class":442},[436,3080,460],{"class":442},[436,3082,962],{"class":463},[436,3084,455],{"class":442},[436,3086,781],{"class":442},[436,3088,460],{"class":442},[436,3090,966],{"class":597},[436,3092,455],{"class":442},[436,3094,270],{"class":442},[436,3096,3097],{"class":597}," 403",[436,3099,814],{"class":442},[436,3101,3102],{"class":438,"line":676},[436,3103,3104],{"class":442},"    },\n",[436,3106,3107],{"class":438,"line":697},[436,3108,2828],{"class":442},[436,3110,3111,3113,3115,3117,3119,3121,3124,3126],{"class":438,"line":712},[436,3112,627],{"class":442},[436,3114,661],{"class":548},[436,3116,455],{"class":442},[436,3118,270],{"class":442},[436,3120,460],{"class":442},[436,3122,3123],{"class":463},"geo-api",[436,3125,455],{"class":442},[436,3127,469],{"class":442},[436,3129,3130,3132,3134,3136,3138,3140,3143,3145],{"class":438,"line":745},[436,3131,627],{"class":442},[436,3133,681],{"class":548},[436,3135,455],{"class":442},[436,3137,270],{"class":442},[436,3139,460],{"class":442},[436,3141,3142],{"class":463},"API only from US\u002FCA\u002FGB",[436,3144,455],{"class":442},[436,3146,469],{"class":442},[436,3148,3149,3151,3153,3155,3157],{"class":438,"line":760},[436,3150,627],{"class":442},[436,3152,702],{"class":548},[436,3154,455],{"class":442},[436,3156,270],{"class":442},[436,3158,757],{"class":442},[436,3160,3161,3163,3165,3167,3169,3171,3173,3175,3177,3179,3181,3183,3185,3187,3189,3191,3193,3195,3197,3199,3201,3203,3205,3208,3210],{"class":438,"line":817},[436,3162,2883],{"class":442},[436,3164,460],{"class":442},[436,3166,2009],{"class":597},[436,3168,455],{"class":442},[436,3170,270],{"class":442},[436,3172,460],{"class":442},[436,3174,2054],{"class":463},[436,3176,455],{"class":442},[436,3178,781],{"class":442},[436,3180,460],{"class":442},[436,3182,1393],{"class":597},[436,3184,455],{"class":442},[436,3186,270],{"class":442},[436,3188,460],{"class":442},[436,3190,1503],{"class":463},[436,3192,455],{"class":442},[436,3194,781],{"class":442},[436,3196,460],{"class":442},[436,3198,1228],{"class":597},[436,3200,455],{"class":442},[436,3202,270],{"class":442},[436,3204,460],{"class":442},[436,3206,3207],{"class":463},"\u002Fapi\u002F",[436,3209,455],{"class":442},[436,3211,563],{"class":442},[436,3213,3214,3216,3218,3220,3222,3224,3226,3228,3230,3232,3234,3236,3238,3240,3242,3244,3246,3248,3250,3252,3254,3256,3259,3261,3264,3266,3268,3270,3273,3275,3277,3279,3282,3284,3287],{"class":438,"line":823},[436,3215,2883],{"class":442},[436,3217,460],{"class":442},[436,3219,2009],{"class":597},[436,3221,455],{"class":442},[436,3223,270],{"class":442},[436,3225,460],{"class":442},[436,3227,2173],{"class":463},[436,3229,455],{"class":442},[436,3231,781],{"class":442},[436,3233,460],{"class":442},[436,3235,1393],{"class":597},[436,3237,455],{"class":442},[436,3239,270],{"class":442},[436,3241,460],{"class":442},[436,3243,1516],{"class":463},[436,3245,455],{"class":442},[436,3247,781],{"class":442},[436,3249,460],{"class":442},[436,3251,1228],{"class":597},[436,3253,455],{"class":442},[436,3255,270],{"class":442},[436,3257,3258],{"class":442}," [",[436,3260,455],{"class":442},[436,3262,3263],{"class":463},"US",[436,3265,455],{"class":442},[436,3267,781],{"class":442},[436,3269,460],{"class":442},[436,3271,3272],{"class":463},"CA",[436,3274,455],{"class":442},[436,3276,781],{"class":442},[436,3278,460],{"class":442},[436,3280,3281],{"class":463},"GB",[436,3283,455],{"class":442},[436,3285,3286],{"class":442},"]",[436,3288,814],{"class":442},[436,3290,3291],{"class":438,"line":855},[436,3292,3058],{"class":442},[436,3294,3295,3297,3299,3301,3303,3305,3307,3309,3311,3313,3315,3317,3319,3321,3323,3325,3327,3329,3331],{"class":438,"line":886},[436,3296,627],{"class":442},[436,3298,1812],{"class":548},[436,3300,455],{"class":442},[436,3302,270],{"class":442},[436,3304,543],{"class":442},[436,3306,460],{"class":442},[436,3308,729],{"class":597},[436,3310,455],{"class":442},[436,3312,270],{"class":442},[436,3314,460],{"class":442},[436,3316,962],{"class":463},[436,3318,455],{"class":442},[436,3320,781],{"class":442},[436,3322,460],{"class":442},[436,3324,966],{"class":597},[436,3326,455],{"class":442},[436,3328,270],{"class":442},[436,3330,3097],{"class":597},[436,3332,814],{"class":442},[436,3334,3335],{"class":438,"line":892},[436,3336,895],{"class":442},[436,3338,3339],{"class":438,"line":898},[436,3340,3341],{"class":442},"  ]\n",[436,3343,3344],{"class":438,"line":904},[436,3345,907],{"class":442},[422,3347,3349],{"id":3348},"rate-limit-per-ip-for-the-api-per-api-key-stricter","Rate limit: per-IP for the API, per-API-key stricter",[427,3351,3353],{"className":429,"code":3352,"language":431,"meta":432,"style":432},"\"rate_limiting\": {\n  \"enabled\": true,\n  \"rules\": [\n    {\n      \"id\": \"api-per-ip\",\n      \"name\": \"API per IP\",\n      \"conditions\": [ { \"left\": \"req.path\", \"operator\": \"starts_with\", \"value\": \"\u002Fapi\u002F\" } ],\n      \"key\": \"req.client_ip\",\n      \"limit\": 100,\n      \"window_seconds\": 60\n    },\n    {\n      \"id\": \"api-per-key\",\n      \"name\": \"API per key\",\n      \"conditions\": [ { \"left\": \"req.path\", \"operator\": \"starts_with\", \"value\": \"\u002Fapi\u002F\" } ],\n      \"key\": [\"req.client_ip\", \"req.header.X-API-Key\"],\n      \"limit\": 1000,\n      \"window_seconds\": 60,\n      \"action\": { \"type\": \"block\", \"status\": 429 }\n    }\n  ]\n}\n",[114,3354,3355,3367,3379,3391,3395,3414,3433,3499,3517,3533,3547,3551,3555,3574,3593,3657,3687,3702,3716,3757,3761,3765],{"__ignoreMap":432},[436,3356,3357,3359,3361,3363,3365],{"class":438,"line":439},[436,3358,455],{"class":442},[436,3360,1727],{"class":463},[436,3362,455],{"class":442},[436,3364,2388],{"class":2797},[436,3366,443],{"class":442},[436,3368,3369,3371,3373,3375,3377],{"class":438,"line":446},[436,3370,449],{"class":442},[436,3372,598],{"class":452},[436,3374,455],{"class":442},[436,3376,270],{"class":442},[436,3378,636],{"class":442},[436,3380,3381,3383,3385,3387,3389],{"class":438,"line":472},[436,3382,449],{"class":442},[436,3384,1736],{"class":452},[436,3386,455],{"class":442},[436,3388,270],{"class":442},[436,3390,757],{"class":442},[436,3392,3393],{"class":438,"line":492},[436,3394,2828],{"class":442},[436,3396,3397,3399,3401,3403,3405,3407,3410,3412],{"class":438,"line":512},[436,3398,627],{"class":442},[436,3400,661],{"class":548},[436,3402,455],{"class":442},[436,3404,270],{"class":442},[436,3406,460],{"class":442},[436,3408,3409],{"class":463},"api-per-ip",[436,3411,455],{"class":442},[436,3413,469],{"class":442},[436,3415,3416,3418,3420,3422,3424,3426,3429,3431],{"class":438,"line":532},[436,3417,627],{"class":442},[436,3419,681],{"class":548},[436,3421,455],{"class":442},[436,3423,270],{"class":442},[436,3425,460],{"class":442},[436,3427,3428],{"class":463},"API per IP",[436,3430,455],{"class":442},[436,3432,469],{"class":442},[436,3434,3435,3437,3439,3441,3443,3445,3447,3449,3451,3453,3455,3457,3459,3461,3463,3465,3467,3469,3471,3473,3475,3477,3479,3481,3483,3485,3487,3489,3491,3493,3496],{"class":438,"line":566},[436,3436,627],{"class":442},[436,3438,702],{"class":548},[436,3440,455],{"class":442},[436,3442,270],{"class":442},[436,3444,3258],{"class":442},[436,3446,543],{"class":442},[436,3448,460],{"class":442},[436,3450,2009],{"class":597},[436,3452,455],{"class":442},[436,3454,270],{"class":442},[436,3456,460],{"class":442},[436,3458,2054],{"class":463},[436,3460,455],{"class":442},[436,3462,781],{"class":442},[436,3464,460],{"class":442},[436,3466,1393],{"class":597},[436,3468,455],{"class":442},[436,3470,270],{"class":442},[436,3472,460],{"class":442},[436,3474,1503],{"class":463},[436,3476,455],{"class":442},[436,3478,781],{"class":442},[436,3480,460],{"class":442},[436,3482,1228],{"class":597},[436,3484,455],{"class":442},[436,3486,270],{"class":442},[436,3488,460],{"class":442},[436,3490,3207],{"class":463},[436,3492,455],{"class":442},[436,3494,3495],{"class":442}," }",[436,3497,3498],{"class":442}," ],\n",[436,3500,3501,3503,3505,3507,3509,3511,3513,3515],{"class":438,"line":580},[436,3502,627],{"class":442},[436,3504,1055],{"class":548},[436,3506,455],{"class":442},[436,3508,270],{"class":442},[436,3510,460],{"class":442},[436,3512,2156],{"class":463},[436,3514,455],{"class":442},[436,3516,469],{"class":442},[436,3518,3519,3521,3524,3526,3528,3531],{"class":438,"line":610},[436,3520,627],{"class":442},[436,3522,3523],{"class":548},"limit",[436,3525,455],{"class":442},[436,3527,270],{"class":442},[436,3529,3530],{"class":597}," 100",[436,3532,469],{"class":442},[436,3534,3535,3537,3540,3542,3544],{"class":438,"line":624},[436,3536,627],{"class":442},[436,3538,3539],{"class":548},"window_seconds",[436,3541,455],{"class":442},[436,3543,270],{"class":442},[436,3545,3546],{"class":597}," 60\n",[436,3548,3549],{"class":438,"line":639},[436,3550,3104],{"class":442},[436,3552,3553],{"class":438,"line":654},[436,3554,2828],{"class":442},[436,3556,3557,3559,3561,3563,3565,3567,3570,3572],{"class":438,"line":676},[436,3558,627],{"class":442},[436,3560,661],{"class":548},[436,3562,455],{"class":442},[436,3564,270],{"class":442},[436,3566,460],{"class":442},[436,3568,3569],{"class":463},"api-per-key",[436,3571,455],{"class":442},[436,3573,469],{"class":442},[436,3575,3576,3578,3580,3582,3584,3586,3589,3591],{"class":438,"line":697},[436,3577,627],{"class":442},[436,3579,681],{"class":548},[436,3581,455],{"class":442},[436,3583,270],{"class":442},[436,3585,460],{"class":442},[436,3587,3588],{"class":463},"API per key",[436,3590,455],{"class":442},[436,3592,469],{"class":442},[436,3594,3595,3597,3599,3601,3603,3605,3607,3609,3611,3613,3615,3617,3619,3621,3623,3625,3627,3629,3631,3633,3635,3637,3639,3641,3643,3645,3647,3649,3651,3653,3655],{"class":438,"line":712},[436,3596,627],{"class":442},[436,3598,702],{"class":548},[436,3600,455],{"class":442},[436,3602,270],{"class":442},[436,3604,3258],{"class":442},[436,3606,543],{"class":442},[436,3608,460],{"class":442},[436,3610,2009],{"class":597},[436,3612,455],{"class":442},[436,3614,270],{"class":442},[436,3616,460],{"class":442},[436,3618,2054],{"class":463},[436,3620,455],{"class":442},[436,3622,781],{"class":442},[436,3624,460],{"class":442},[436,3626,1393],{"class":597},[436,3628,455],{"class":442},[436,3630,270],{"class":442},[436,3632,460],{"class":442},[436,3634,1503],{"class":463},[436,3636,455],{"class":442},[436,3638,781],{"class":442},[436,3640,460],{"class":442},[436,3642,1228],{"class":597},[436,3644,455],{"class":442},[436,3646,270],{"class":442},[436,3648,460],{"class":442},[436,3650,3207],{"class":463},[436,3652,455],{"class":442},[436,3654,3495],{"class":442},[436,3656,3498],{"class":442},[436,3658,3659,3661,3663,3665,3667,3669,3671,3673,3675,3677,3679,3682,3684],{"class":438,"line":745},[436,3660,627],{"class":442},[436,3662,1055],{"class":548},[436,3664,455],{"class":442},[436,3666,270],{"class":442},[436,3668,3258],{"class":442},[436,3670,455],{"class":442},[436,3672,2156],{"class":463},[436,3674,455],{"class":442},[436,3676,781],{"class":442},[436,3678,460],{"class":442},[436,3680,3681],{"class":463},"req.header.X-API-Key",[436,3683,455],{"class":442},[436,3685,3686],{"class":442},"],\n",[436,3688,3689,3691,3693,3695,3697,3700],{"class":438,"line":760},[436,3690,627],{"class":442},[436,3692,3523],{"class":548},[436,3694,455],{"class":442},[436,3696,270],{"class":442},[436,3698,3699],{"class":597}," 1000",[436,3701,469],{"class":442},[436,3703,3704,3706,3708,3710,3712,3714],{"class":438,"line":817},[436,3705,627],{"class":442},[436,3707,3539],{"class":548},[436,3709,455],{"class":442},[436,3711,270],{"class":442},[436,3713,3051],{"class":597},[436,3715,469],{"class":442},[436,3717,3718,3720,3722,3724,3726,3728,3730,3732,3734,3736,3738,3740,3742,3744,3746,3748,3750,3752,3755],{"class":438,"line":823},[436,3719,627],{"class":442},[436,3721,1812],{"class":548},[436,3723,455],{"class":442},[436,3725,270],{"class":442},[436,3727,543],{"class":442},[436,3729,460],{"class":442},[436,3731,729],{"class":597},[436,3733,455],{"class":442},[436,3735,270],{"class":442},[436,3737,460],{"class":442},[436,3739,962],{"class":463},[436,3741,455],{"class":442},[436,3743,781],{"class":442},[436,3745,460],{"class":442},[436,3747,966],{"class":597},[436,3749,455],{"class":442},[436,3751,270],{"class":442},[436,3753,3754],{"class":597}," 429",[436,3756,814],{"class":442},[436,3758,3759],{"class":438,"line":855},[436,3760,895],{"class":442},[436,3762,3763],{"class":438,"line":886},[436,3764,3341],{"class":442},[436,3766,3767],{"class":438,"line":892},[436,3768,907],{"class":442},[422,3770,3772],{"id":3771},"cache-static-assets-1h-at-edge-5-min-in-browser","Cache static assets 1h at edge, 5 min in browser",[427,3774,3776],{"className":429,"code":3775,"language":431,"meta":432,"style":432},"\"cache\": {\n  \"enabled\": true,\n  \"max_object_size_bytes\": 10485760,\n  \"rules\": [{\n    \"id\": \"static\",\n    \"name\": \"Static assets\",\n    \"conditions\": [\n      { \"left\": \"req.path\", \"operator\": \"regex\", \"value\": \"\\\\.(css|js|png|jpg|svg|woff2)$\" }\n    ],\n    \"cache\": \"eligible\",\n    \"edge_ttl\": { \"mode\": \"custom\", \"seconds\": 3600 },\n    \"browser_ttl\": { \"mode\": \"custom\", \"seconds\": 300 },\n    \"stale_while_revalidate\": 86400,\n    \"stale_if_error\": 604800,\n    \"statuses\": [200, 301]\n  }]\n}\n",[114,3777,3778,3790,3802,3817,3829,3848,3867,3879,3936,3941,3959,4001,4043,4059,4075,4098,4103],{"__ignoreMap":432},[436,3779,3780,3782,3784,3786,3788],{"class":438,"line":439},[436,3781,455],{"class":442},[436,3783,1551],{"class":463},[436,3785,455],{"class":442},[436,3787,2388],{"class":2797},[436,3789,443],{"class":442},[436,3791,3792,3794,3796,3798,3800],{"class":438,"line":446},[436,3793,449],{"class":442},[436,3795,598],{"class":452},[436,3797,455],{"class":442},[436,3799,270],{"class":442},[436,3801,636],{"class":442},[436,3803,3804,3806,3808,3810,3812,3815],{"class":438,"line":472},[436,3805,449],{"class":442},[436,3807,1754],{"class":452},[436,3809,455],{"class":442},[436,3811,270],{"class":442},[436,3813,3814],{"class":597}," 10485760",[436,3816,469],{"class":442},[436,3818,3819,3821,3823,3825,3827],{"class":438,"line":492},[436,3820,449],{"class":442},[436,3822,1736],{"class":452},[436,3824,455],{"class":442},[436,3826,270],{"class":442},[436,3828,651],{"class":442},[436,3830,3831,3833,3835,3837,3839,3841,3844,3846],{"class":438,"line":512},[436,3832,583],{"class":442},[436,3834,661],{"class":548},[436,3836,455],{"class":442},[436,3838,270],{"class":442},[436,3840,460],{"class":442},[436,3842,3843],{"class":463},"static",[436,3845,455],{"class":442},[436,3847,469],{"class":442},[436,3849,3850,3852,3854,3856,3858,3860,3863,3865],{"class":438,"line":532},[436,3851,583],{"class":442},[436,3853,681],{"class":548},[436,3855,455],{"class":442},[436,3857,270],{"class":442},[436,3859,460],{"class":442},[436,3861,3862],{"class":463},"Static assets",[436,3864,455],{"class":442},[436,3866,469],{"class":442},[436,3868,3869,3871,3873,3875,3877],{"class":438,"line":566},[436,3870,583],{"class":442},[436,3872,702],{"class":548},[436,3874,455],{"class":442},[436,3876,270],{"class":442},[436,3878,757],{"class":442},[436,3880,3881,3884,3886,3888,3890,3892,3894,3896,3898,3900,3902,3904,3906,3908,3910,3912,3914,3916,3918,3920,3922,3924,3926,3929,3932,3934],{"class":438,"line":580},[436,3882,3883],{"class":442},"      {",[436,3885,460],{"class":442},[436,3887,2009],{"class":597},[436,3889,455],{"class":442},[436,3891,270],{"class":442},[436,3893,460],{"class":442},[436,3895,2054],{"class":463},[436,3897,455],{"class":442},[436,3899,781],{"class":442},[436,3901,460],{"class":442},[436,3903,1393],{"class":597},[436,3905,455],{"class":442},[436,3907,270],{"class":442},[436,3909,460],{"class":442},[436,3911,1531],{"class":463},[436,3913,455],{"class":442},[436,3915,781],{"class":442},[436,3917,460],{"class":442},[436,3919,1228],{"class":597},[436,3921,455],{"class":442},[436,3923,270],{"class":442},[436,3925,460],{"class":442},[436,3927,3928],{"class":2797},"\\\\",[436,3930,3931],{"class":463},".(css|js|png|jpg|svg|woff2)$",[436,3933,455],{"class":442},[436,3935,814],{"class":442},[436,3937,3938],{"class":438,"line":610},[436,3939,3940],{"class":442},"    ],\n",[436,3942,3943,3945,3947,3949,3951,3953,3955,3957],{"class":438,"line":624},[436,3944,583],{"class":442},[436,3946,1551],{"class":548},[436,3948,455],{"class":442},[436,3950,270],{"class":442},[436,3952,460],{"class":442},[436,3954,1557],{"class":463},[436,3956,455],{"class":442},[436,3958,469],{"class":442},[436,3960,3961,3963,3966,3968,3970,3972,3974,3976,3978,3980,3982,3984,3986,3988,3990,3992,3994,3996,3999],{"class":438,"line":639},[436,3962,583],{"class":442},[436,3964,3965],{"class":548},"edge_ttl",[436,3967,455],{"class":442},[436,3969,270],{"class":442},[436,3971,543],{"class":442},[436,3973,460],{"class":442},[436,3975,839],{"class":597},[436,3977,455],{"class":442},[436,3979,270],{"class":442},[436,3981,460],{"class":442},[436,3983,1224],{"class":463},[436,3985,455],{"class":442},[436,3987,781],{"class":442},[436,3989,460],{"class":442},[436,3991,1143],{"class":597},[436,3993,455],{"class":442},[436,3995,270],{"class":442},[436,3997,3998],{"class":597}," 3600",[436,4000,563],{"class":442},[436,4002,4003,4005,4008,4010,4012,4014,4016,4018,4020,4022,4024,4026,4028,4030,4032,4034,4036,4038,4041],{"class":438,"line":654},[436,4004,583],{"class":442},[436,4006,4007],{"class":548},"browser_ttl",[436,4009,455],{"class":442},[436,4011,270],{"class":442},[436,4013,543],{"class":442},[436,4015,460],{"class":442},[436,4017,839],{"class":597},[436,4019,455],{"class":442},[436,4021,270],{"class":442},[436,4023,460],{"class":442},[436,4025,1224],{"class":463},[436,4027,455],{"class":442},[436,4029,781],{"class":442},[436,4031,460],{"class":442},[436,4033,1143],{"class":597},[436,4035,455],{"class":442},[436,4037,270],{"class":442},[436,4039,4040],{"class":597}," 300",[436,4042,563],{"class":442},[436,4044,4045,4047,4050,4052,4054,4057],{"class":438,"line":676},[436,4046,583],{"class":442},[436,4048,4049],{"class":548},"stale_while_revalidate",[436,4051,455],{"class":442},[436,4053,270],{"class":442},[436,4055,4056],{"class":597}," 86400",[436,4058,469],{"class":442},[436,4060,4061,4063,4066,4068,4070,4073],{"class":438,"line":697},[436,4062,583],{"class":442},[436,4064,4065],{"class":548},"stale_if_error",[436,4067,455],{"class":442},[436,4069,270],{"class":442},[436,4071,4072],{"class":597}," 604800",[436,4074,469],{"class":442},[436,4076,4077,4079,4081,4083,4085,4087,4090,4092,4095],{"class":438,"line":712},[436,4078,583],{"class":442},[436,4080,1783],{"class":548},[436,4082,455],{"class":442},[436,4084,270],{"class":442},[436,4086,3258],{"class":442},[436,4088,4089],{"class":597},"200",[436,4091,781],{"class":442},[436,4093,4094],{"class":597}," 301",[436,4096,4097],{"class":442},"]\n",[436,4099,4100],{"class":438,"line":745},[436,4101,4102],{"class":442},"  }]\n",[436,4104,4105],{"class":438,"line":760},[436,4106,907],{"class":442},[422,4108,4110],{"id":4109},"two-backends-least-connections-tcp-health-checks-real-host-header","Two backends, least-connections, TCP health checks, real Host header",[427,4112,4114],{"className":429,"code":4113,"language":431,"meta":432,"style":432},"\"load_balancer\": {\n  \"enabled\": true,\n  \"load_balancers\": [{\n    \"id\": \"api-pool\",\n    \"name\": \"API pool\",\n    \"conditions\": [],\n    \"algorithm\": { \"type\": \"least_connections\" },\n    \"targets\": [\n      { \"id\": \"a\", \"host\": \"10.0.0.1\", \"port\": 3000, \"protocol\": \"http\", \"weight\": 100 },\n      { \"id\": \"b\", \"host\": \"10.0.0.2\", \"port\": 3000, \"protocol\": \"http\", \"weight\": 100 }\n    ],\n    \"host_header\": { \"mode\": \"visitor\" },\n    \"health_check\": {\n      \"mode\": \"accepts_connections\",\n      \"interval_seconds\": 5,\n      \"timeout_ms\": 1000,\n      \"healthy_threshold\": 2,\n      \"unhealthy_threshold\": 3\n    }\n  }]\n}\n",[114,4115,4116,4128,4140,4152,4171,4190,4202,4230,4242,4319,4397,4401,4429,4441,4459,4474,4488,4502,4515,4519,4523],{"__ignoreMap":432},[436,4117,4118,4120,4122,4124,4126],{"class":438,"line":439},[436,4119,455],{"class":442},[436,4121,615],{"class":463},[436,4123,455],{"class":442},[436,4125,2388],{"class":2797},[436,4127,443],{"class":442},[436,4129,4130,4132,4134,4136,4138],{"class":438,"line":446},[436,4131,449],{"class":442},[436,4133,598],{"class":452},[436,4135,455],{"class":442},[436,4137,270],{"class":442},[436,4139,636],{"class":442},[436,4141,4142,4144,4146,4148,4150],{"class":438,"line":472},[436,4143,449],{"class":442},[436,4145,644],{"class":452},[436,4147,455],{"class":442},[436,4149,270],{"class":442},[436,4151,651],{"class":442},[436,4153,4154,4156,4158,4160,4162,4164,4167,4169],{"class":438,"line":492},[436,4155,583],{"class":442},[436,4157,661],{"class":548},[436,4159,455],{"class":442},[436,4161,270],{"class":442},[436,4163,460],{"class":442},[436,4165,4166],{"class":463},"api-pool",[436,4168,455],{"class":442},[436,4170,469],{"class":442},[436,4172,4173,4175,4177,4179,4181,4183,4186,4188],{"class":438,"line":512},[436,4174,583],{"class":442},[436,4176,681],{"class":548},[436,4178,455],{"class":442},[436,4180,270],{"class":442},[436,4182,460],{"class":442},[436,4184,4185],{"class":463},"API pool",[436,4187,455],{"class":442},[436,4189,469],{"class":442},[436,4191,4192,4194,4196,4198,4200],{"class":438,"line":532},[436,4193,583],{"class":442},[436,4195,702],{"class":548},[436,4197,455],{"class":442},[436,4199,270],{"class":442},[436,4201,709],{"class":442},[436,4203,4204,4206,4208,4210,4212,4214,4216,4218,4220,4222,4224,4226,4228],{"class":438,"line":566},[436,4205,583],{"class":442},[436,4207,717],{"class":548},[436,4209,455],{"class":442},[436,4211,270],{"class":442},[436,4213,543],{"class":442},[436,4215,460],{"class":442},[436,4217,729],{"class":597},[436,4219,455],{"class":442},[436,4221,270],{"class":442},[436,4223,460],{"class":442},[436,4225,1619],{"class":463},[436,4227,455],{"class":442},[436,4229,563],{"class":442},[436,4231,4232,4234,4236,4238,4240],{"class":438,"line":580},[436,4233,583],{"class":442},[436,4235,750],{"class":548},[436,4237,455],{"class":442},[436,4239,270],{"class":442},[436,4241,757],{"class":442},[436,4243,4244,4246,4248,4250,4252,4254,4256,4258,4260,4262,4264,4266,4268,4270,4272,4275,4277,4279,4281,4283,4285,4287,4289,4291,4293,4295,4297,4299,4301,4303,4305,4307,4309,4311,4313,4315,4317],{"class":438,"line":610},[436,4245,3883],{"class":442},[436,4247,460],{"class":442},[436,4249,661],{"class":597},[436,4251,455],{"class":442},[436,4253,270],{"class":442},[436,4255,460],{"class":442},[436,4257,410],{"class":463},[436,4259,455],{"class":442},[436,4261,781],{"class":442},[436,4263,460],{"class":442},[436,4265,786],{"class":597},[436,4267,455],{"class":442},[436,4269,270],{"class":442},[436,4271,460],{"class":442},[436,4273,4274],{"class":463},"10.0.0.1",[436,4276,455],{"class":442},[436,4278,781],{"class":442},[436,4280,460],{"class":442},[436,4282,804],{"class":597},[436,4284,455],{"class":442},[436,4286,270],{"class":442},[436,4288,811],{"class":597},[436,4290,781],{"class":442},[436,4292,460],{"class":442},[436,4294,1826],{"class":597},[436,4296,455],{"class":442},[436,4298,270],{"class":442},[436,4300,460],{"class":442},[436,4302,549],{"class":463},[436,4304,455],{"class":442},[436,4306,781],{"class":442},[436,4308,460],{"class":442},[436,4310,1829],{"class":597},[436,4312,455],{"class":442},[436,4314,270],{"class":442},[436,4316,3530],{"class":597},[436,4318,563],{"class":442},[436,4320,4321,4323,4325,4327,4329,4331,4333,4336,4338,4340,4342,4344,4346,4348,4350,4353,4355,4357,4359,4361,4363,4365,4367,4369,4371,4373,4375,4377,4379,4381,4383,4385,4387,4389,4391,4393,4395],{"class":438,"line":624},[436,4322,3883],{"class":442},[436,4324,460],{"class":442},[436,4326,661],{"class":597},[436,4328,455],{"class":442},[436,4330,270],{"class":442},[436,4332,460],{"class":442},[436,4334,4335],{"class":463},"b",[436,4337,455],{"class":442},[436,4339,781],{"class":442},[436,4341,460],{"class":442},[436,4343,786],{"class":597},[436,4345,455],{"class":442},[436,4347,270],{"class":442},[436,4349,460],{"class":442},[436,4351,4352],{"class":463},"10.0.0.2",[436,4354,455],{"class":442},[436,4356,781],{"class":442},[436,4358,460],{"class":442},[436,4360,804],{"class":597},[436,4362,455],{"class":442},[436,4364,270],{"class":442},[436,4366,811],{"class":597},[436,4368,781],{"class":442},[436,4370,460],{"class":442},[436,4372,1826],{"class":597},[436,4374,455],{"class":442},[436,4376,270],{"class":442},[436,4378,460],{"class":442},[436,4380,549],{"class":463},[436,4382,455],{"class":442},[436,4384,781],{"class":442},[436,4386,460],{"class":442},[436,4388,1829],{"class":597},[436,4390,455],{"class":442},[436,4392,270],{"class":442},[436,4394,3530],{"class":597},[436,4396,814],{"class":442},[436,4398,4399],{"class":438,"line":639},[436,4400,3940],{"class":442},[436,4402,4403,4405,4407,4409,4411,4413,4415,4417,4419,4421,4423,4425,4427],{"class":438,"line":654},[436,4404,583],{"class":442},[436,4406,828],{"class":548},[436,4408,455],{"class":442},[436,4410,270],{"class":442},[436,4412,543],{"class":442},[436,4414,460],{"class":442},[436,4416,839],{"class":597},[436,4418,455],{"class":442},[436,4420,270],{"class":442},[436,4422,460],{"class":442},[436,4424,848],{"class":463},[436,4426,455],{"class":442},[436,4428,563],{"class":442},[436,4430,4431,4433,4435,4437,4439],{"class":438,"line":676},[436,4432,583],{"class":442},[436,4434,860],{"class":548},[436,4436,455],{"class":442},[436,4438,270],{"class":442},[436,4440,577],{"class":442},[436,4442,4443,4445,4447,4449,4451,4453,4455,4457],{"class":438,"line":697},[436,4444,627],{"class":442},[436,4446,839],{"class":597},[436,4448,455],{"class":442},[436,4450,270],{"class":442},[436,4452,460],{"class":442},[436,4454,1337],{"class":463},[436,4456,455],{"class":442},[436,4458,469],{"class":442},[436,4460,4461,4463,4465,4467,4469,4472],{"class":438,"line":712},[436,4462,627],{"class":442},[436,4464,1878],{"class":597},[436,4466,455],{"class":442},[436,4468,270],{"class":442},[436,4470,4471],{"class":597}," 5",[436,4473,469],{"class":442},[436,4475,4476,4478,4480,4482,4484,4486],{"class":438,"line":745},[436,4477,627],{"class":442},[436,4479,1881],{"class":597},[436,4481,455],{"class":442},[436,4483,270],{"class":442},[436,4485,3699],{"class":597},[436,4487,469],{"class":442},[436,4489,4490,4492,4494,4496,4498,4500],{"class":438,"line":760},[436,4491,627],{"class":442},[436,4493,1898],{"class":597},[436,4495,455],{"class":442},[436,4497,270],{"class":442},[436,4499,2525],{"class":597},[436,4501,469],{"class":442},[436,4503,4504,4506,4508,4510,4512],{"class":438,"line":817},[436,4505,627],{"class":442},[436,4507,1901],{"class":597},[436,4509,455],{"class":442},[436,4511,270],{"class":442},[436,4513,4514],{"class":597}," 3\n",[436,4516,4517],{"class":438,"line":823},[436,4518,895],{"class":442},[436,4520,4521],{"class":438,"line":855},[436,4522,4102],{"class":442},[436,4524,4525],{"class":438,"line":886},[436,4526,907],{"class":442},[422,4528,4530],{"id":4529},"https-with-on-demand-lets-encrypt","HTTPS with on-demand Let's Encrypt",[427,4532,4534],{"className":429,"code":4533,"language":431,"meta":432,"style":432},"\"listen\": { \"http\": \"0.0.0.0:8080\", \"https\": \"0.0.0.0:8443\" },\n\"plugins\": {\n  \"auto_ssl\": {\n    \"enabled\": true,\n    \"email\": \"admin@example.com\",\n    \"domains\": [\"api.example.com\"],\n    \"staging\": true\n  }\n}\n",[114,4535,4536,4584,4596,4608,4620,4639,4659,4672,4676],{"__ignoreMap":432},[436,4537,4538,4540,4542,4544,4546,4549,4551,4553,4555,4557,4559,4561,4563,4565,4567,4569,4571,4573,4575,4578,4580,4582],{"class":438,"line":439},[436,4539,455],{"class":442},[436,4541,296],{"class":463},[436,4543,455],{"class":442},[436,4545,2388],{"class":2797},[436,4547,4548],{"class":442},"{",[436,4550,460],{"class":442},[436,4552,549],{"class":452},[436,4554,455],{"class":442},[436,4556,270],{"class":442},[436,4558,460],{"class":442},[436,4560,558],{"class":463},[436,4562,455],{"class":442},[436,4564,781],{"class":442},[436,4566,460],{"class":442},[436,4568,1307],{"class":452},[436,4570,455],{"class":442},[436,4572,270],{"class":442},[436,4574,460],{"class":442},[436,4576,4577],{"class":463},"0.0.0.0:8443",[436,4579,455],{"class":442},[436,4581,3495],{"class":442},[436,4583,469],{"class":2797},[436,4585,4586,4588,4590,4592,4594],{"class":438,"line":446},[436,4587,455],{"class":442},[436,4589,300],{"class":463},[436,4591,455],{"class":442},[436,4593,2388],{"class":2797},[436,4595,443],{"class":442},[436,4597,4598,4600,4602,4604,4606],{"class":438,"line":472},[436,4599,449],{"class":442},[436,4601,141],{"class":452},[436,4603,455],{"class":442},[436,4605,270],{"class":442},[436,4607,577],{"class":442},[436,4609,4610,4612,4614,4616,4618],{"class":438,"line":492},[436,4611,583],{"class":442},[436,4613,598],{"class":548},[436,4615,455],{"class":442},[436,4617,270],{"class":442},[436,4619,636],{"class":442},[436,4621,4622,4624,4626,4628,4630,4632,4635,4637],{"class":438,"line":512},[436,4623,583],{"class":442},[436,4625,381],{"class":548},[436,4627,455],{"class":442},[436,4629,270],{"class":442},[436,4631,460],{"class":442},[436,4633,4634],{"class":463},"admin@example.com",[436,4636,455],{"class":442},[436,4638,469],{"class":442},[436,4640,4641,4643,4645,4647,4649,4651,4653,4655,4657],{"class":438,"line":532},[436,4642,583],{"class":442},[436,4644,385],{"class":548},[436,4646,455],{"class":442},[436,4648,270],{"class":442},[436,4650,3258],{"class":442},[436,4652,455],{"class":442},[436,4654,505],{"class":463},[436,4656,455],{"class":442},[436,4658,3686],{"class":442},[436,4660,4661,4663,4665,4667,4669],{"class":438,"line":566},[436,4662,583],{"class":442},[436,4664,1959],{"class":548},[436,4666,455],{"class":442},[436,4668,270],{"class":442},[436,4670,4671],{"class":442}," true\n",[436,4673,4674],{"class":438,"line":580},[436,4675,901],{"class":442},[436,4677,4678],{"class":438,"line":610},[436,4679,907],{"class":442},[128,4681,4682,4683,4686,4687,4689],{},"Set ",[114,4684,4685],{},"staging: true"," first, verify issuance works, then flip to ",[114,4688,1970],{},". Port 80 must be reachable for the HTTP-01 challenge. First handshake serves a self-signed fallback cert until ACME finishes in the background.",[123,4691,4693],{"id":4692},"_9-what-backends-receive-what-logs-contain","9. What backends receive \u002F what logs contain",[128,4695,4696,4697,4700,4701,117,4704,4707,4708,4710],{},"Upstream requests get ",[114,4698,4699],{},"X-Forwarded-For: \u003Cclient_ip>"," (appended to the chain), ",[114,4702,4703],{},"X-Forwarded-Proto",[114,4705,4706],{},"X-Forwarded-Host",". Extra headers can be injected with a firewall ",[114,4709,1010],{}," rule.",[128,4712,4713],{},"Access log = one JSON object per line. Useful JSON paths for automation:",[148,4715,4716,4725],{},[151,4717,4718],{},[154,4719,4720,4723],{},[157,4721,4722],{},"Question",[157,4724,2022],{},[164,4726,4727,4746,4762,4775,4803,4819,4832],{},[154,4728,4729,4732],{},[169,4730,4731],{},"Who was it?",[169,4733,4734,117,4737,117,4740,117,4743],{},[114,4735,4736],{},".client_ip",[114,4738,4739],{},".ip_intel.country",[114,4741,4742],{},".ip_intel.asn",[114,4744,4745],{},".ip_intel.provider",[154,4747,4748,4751],{},[169,4749,4750],{},"Blocked by firewall?",[169,4752,4753,117,4756,117,4759],{},[114,4754,4755],{},".firewall.matched",[114,4757,4758],{},".firewall.rule_id",[114,4760,4761],{},".firewall.action",[154,4763,4764,4767],{},[169,4765,4766],{},"Rate limited?",[169,4768,4769,117,4772],{},[114,4770,4771],{},".rate_limit.blocked",[114,4773,4774],{},".rate_limit.remaining",[154,4776,4777,4780],{},[169,4778,4779],{},"Cache outcome",[169,4781,4782,211,4785,1004,4788,1004,4791,1004,4794,1004,4797,1004,4800,200],{},[114,4783,4784],{},".cache.status",[114,4786,4787],{},"HIT",[114,4789,4790],{},"MISS",[114,4792,4793],{},"BYPASS",[114,4795,4796],{},"STALE",[114,4798,4799],{},"REVALIDATING",[114,4801,4802],{},"NONE",[154,4804,4805,4808],{},[169,4806,4807],{},"Which backend?",[169,4809,4810,117,4813,117,4816],{},[114,4811,4812],{},".load_balancer.lb_id",[114,4814,4815],{},".load_balancer.target_id",[114,4817,4818],{},".load_balancer.target",[154,4820,4821,4824],{},[169,4822,4823],{},"Where was it terminated?",[169,4825,4826,211,4829,4831],{},[114,4827,4828],{},".termination_phase",[114,4830,2270],{}," = proxied through)",[154,4833,4834,4837],{},[169,4835,4836],{},"Latency",[169,4838,4839,117,4842,4845],{},[114,4840,4841],{},".timings_us.total_us",[114,4843,4844],{},".timings_us.upstream_ttfb_us",", …",[123,4847,4849],{"id":4848},"_10-operational-gotchas","10. Operational gotchas",[956,4851,4852,4865,4871,4884,4896,4906,4912,4921,4927,4947],{},[275,4853,4854,280,4857,4859,4860,4864],{},[105,4855,4856],{},"Pipeline order matters:",[114,4858,137],{},". Load balancer selection runs ",[4861,4862,4863],"em",{},"before"," cache so cache revalidation knows the upstream. A firewall block never touches cache or backends.",[275,4866,4867,4870],{},[105,4868,4869],{},"Rule order matters"," within each plugin: first match wins.",[275,4872,4873,4880,4881,347],{},[105,4874,4875,4877,4878],{},[114,4876,2377],{}," booleans can be ",[114,4879,2270],{}," (unknown). Prefer ",[114,4882,4883],{},"{\"operator\":\"equals\",\"value\":true}",[275,4885,4886,4889,4890,4892,4893,347],{},[105,4887,4888],{},"Firewall rules cannot be catch-all by design"," — empty ",[114,4891,702],{}," is a config error. To match everything use e.g. ",[114,4894,4895],{},"{\"left\":\"req.path\",\"operator\":\"starts_with\",\"value\":\"\u002F\"}",[275,4897,4898,4901,4902,4905],{},[105,4899,4900],{},"Rate-limit counters are in-memory"," and per-process; ",[114,4903,4904],{},"--workers N"," does not change that (shared within the process). A restart clears them.",[275,4907,4908,4911],{},[105,4909,4910],{},"Cache is in-memory"," and per-process too; no persistence across restarts.",[275,4913,4914,4917,4918,4920],{},[105,4915,4916],{},"Auto-SSL state"," is in memory + ",[114,4919,1956],{}," PEM files (survives restarts).",[275,4922,4923,4926],{},[105,4924,4925],{},"Config hot-reload"," replaces the whole config; make edits atomic (write temp file + rename) so the watcher never reads a half-written file.",[275,4928,4929,280,4932,4935,4936,4938,4939,4942,4943,4946],{},[105,4930,4931],{},"TLS targets:",[114,4933,4934],{},"tls.verify"," defaults to ",[114,4937,1970],{}," (self-signed upstreams work out of the box). Set ",[114,4940,4941],{},"verify: true"," for production upstreams. SNI rules: ",[114,4944,4945],{},"tls.sni"," if set, else the visitor Host for IP targets, else the target hostname.",[275,4948,4949,4950,4952,4953,4956],{},"The gateway returns ",[114,4951,1356],{}," (empty body) when a supposedly-healthy upstream refuses the connection, and ",[114,4954,4955],{},"503 {\"error\":\"no_healthy_upstream\"}"," only when health checks have marked every target down.",[123,4958,4960],{"id":4959},"_11-where-to-read-more","11. Where to read more",[148,4962,4963,4973],{},[151,4964,4965],{},[154,4966,4967,4970],{},[157,4968,4969],{},"Need",[157,4971,4972],{},"Page",[164,4974,4975,4986,4997,5008,5019,5030],{},[154,4976,4977,4980],{},[169,4978,4979],{},"Machine schema for validation",[169,4981,4982],{},[410,4983,4984],{"href":412},[114,4985,415],{},[154,4987,4988,4991],{},[169,4989,4990],{},"Full field-by-field prose reference",[169,4992,4993],{},[410,4994,4996],{"href":4995},"\u002Fquayel-gateway\u002Fconfiguration","Configuration",[154,4998,4999,5002],{},[169,5000,5001],{},"Condition semantics \u002F coercion",[169,5003,5004],{},[410,5005,5007],{"href":5006},"\u002Fquayel-gateway\u002Fconditions","Conditions",[154,5009,5010,5013],{},[169,5011,5012],{},"Log format with full example",[169,5014,5015],{},[410,5016,5018],{"href":5017},"\u002Fquayel-gateway\u002Flogging","Logging",[154,5020,5021,5024],{},[169,5022,5023],{},"Headers, variables, response bodies",[169,5025,5026],{},[410,5027,5029],{"href":5028},"\u002Fquayel-gateway\u002Fapi-reference","API Reference",[154,5031,5032,5035],{},[169,5033,5034],{},"Plugin internals (cache singleflight, ACME flow, …)",[169,5036,5037],{},[410,5038,30],{"href":32},[5040,5041,5042],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .su27w, html code.shiki .su27w{--shiki-light:#916B53;--shiki-default:#916B53;--shiki-dark:#916B53}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}",{"title":432,"searchDepth":446,"depth":446,"links":5044},[5045,5046,5049,5062,5063,5064,5066,5067,5074,5075,5076],{"id":125,"depth":446,"text":126},{"id":259,"depth":446,"text":260,"children":5047},[5048],{"id":424,"depth":472,"text":425},{"id":913,"depth":446,"text":5050,"children":5051},"3. Full config tree (ground truth: src\u002Fconfig\u002Fschema.rs)",[5052,5053,5054,5055,5056,5057,5058,5059,5060,5061],{"id":927,"depth":472,"text":928},{"id":948,"depth":472,"text":949},{"id":1020,"depth":472,"text":1021},{"id":1099,"depth":472,"text":1100},{"id":1198,"depth":472,"text":1199},{"id":1234,"depth":472,"text":1235},{"id":1257,"depth":472,"text":1258},{"id":1287,"depth":472,"text":1288},{"id":1360,"depth":472,"text":1361},{"id":1393,"depth":472,"text":1394},{"id":1402,"depth":446,"text":1403},{"id":1670,"depth":446,"text":1671},{"id":2005,"depth":446,"text":5065},"6. Condition paths (the left \u002F key \u002F cache_key parts vocabulary)",{"id":2412,"depth":446,"text":2413},{"id":2776,"depth":446,"text":2777,"children":5068},[5069,5070,5071,5072,5073],{"id":2780,"depth":472,"text":2781},{"id":3348,"depth":472,"text":3349},{"id":3771,"depth":472,"text":3772},{"id":4109,"depth":472,"text":4110},{"id":4529,"depth":472,"text":4530},{"id":4692,"depth":446,"text":4693},{"id":4848,"depth":446,"text":4849},{"id":4959,"depth":446,"text":4960},"Machine-oriented contract for generating, validating, and operating Quayel Gateway configs.","md",null,{},{"icon":89},{"title":86,"description":5077},"g16b3aOIJkd4CQqJbi3szOwVxtocbZ59c2VhxpnY7rI",[5085,5079],{"title":81,"path":82,"stem":83,"description":5086,"icon":84,"children":-1},"Add the Quayel Gateway skill to Claude Code or any Agent Skills compatible tool — project-scoped or user-wide.",1791649767383]