[{"data":1,"prerenderedAt":3025},["ShallowReactive",2],{"navigation_docs":3,"-quayel-gateway-plugins-auto-ssl":91,"-quayel-gateway-plugins-auto-ssl-surround":3020},[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":64,"body":93,"description":3013,"extension":3014,"links":3015,"meta":3016,"navigation":3017,"path":65,"seo":3018,"stem":66,"__hash__":3019},"docs\u002Fquayel-gateway\u002F02.plugins\u002F6.auto-ssl.md",{"type":94,"value":95,"toc":2974},"minimark",[96,101,105,110,121,126,196,200,255,259,270,274,277,331,337,346,350,566,570,704,708,711,715,718,722,725,728,734,738,748,798,802,805,809,816,916,920,988,992,1043,1046,1050,1053,1059,1062,1089,1096,1100,1103,1109,1115,1119,1122,1141,1144,1148,1151,1173,1177,1197,1201,1205,1353,1357,1479,1483,1669,1673,2774,2778,2782,2803,2807,2814,2828,2832,2835,2848,2852,2855,2869,2873,2934,2938,2970],[97,98,100],"h1",{"id":99},"auto-ssl-plugin","Auto-SSL Plugin",[102,103,104],"p",{},"On-demand TLS certificate provisioning using ACME (Let's Encrypt). Certificates are obtained automatically when the first HTTPS request arrives for a configured domain — no manual certificate management required.",[106,107,109],"h2",{"id":108},"how-it-works","How it works",[111,112,117],"pre",{"className":113,"code":115,"language":116},[114],"language-text","TLS ClientHello (SNI = \"example.com\")\n    │\n    ├── cert_map has real cert? → serve it (O(1))\n    │\n    ├── NOT in allowed_domains? → serve default self-signed cert\n    │\n    ├── state = Pending? → serve fallback (worker running)\n    │\n    ├── state = Failed AND elapsed \u003C 24h? → serve fallback (cooldown)\n    │\n    └── otherwise → dedup insert + spawn bg worker + serve fallback\n        │\n        └── Worker completes → cert loaded into memory → next handshake gets real cert ✅\n","text",[118,119,115],"code",{"__ignoreMap":120},"",[122,123,125],"h3",{"id":124},"three-triggers","Three triggers",[127,128,129,145],"table",{},[130,131,132],"thead",{},[133,134,135,139,142],"tr",{},[136,137,138],"th",{},"Trigger",[136,140,141],{},"When",[136,143,144],{},"Behavior",[146,147,148,163,179],"tbody",{},[133,149,150,157,160],{},[151,152,153],"td",{},[154,155,156],"strong",{},"TLS handshake",[151,158,159],{},"First request for a domain in config",[151,161,162],{},"Serve fallback cert immediately, spawn background ACME worker",[133,164,165,170,176],{},[151,166,167],{},[154,168,169],{},"Config change",[151,171,172,175],{},[118,173,174],{},"auto_ssl.domains"," list updated",[151,177,178],{},"Update allowed domains list only — no cert fetched",[133,180,181,186,189],{},[151,182,183],{},[154,184,185],{},"Daily",[151,187,188],{},"Every 24 hours",[151,190,191,192,195],{},"Renew certs expiring within ",[118,193,194],{},"renewal_days_before",", retry 1 failed cert",[122,197,199],{"id":198},"on-demand-flow","On-demand flow",[201,202,203,210,217,224,230,237,240,243,246,252],"ol",{},[204,205,206,207],"li",{},"A TLS ClientHello arrives with SNI = ",[118,208,209],{},"example.com",[204,211,212,213,216],{},"Gateway checks the in-memory ",[118,214,215],{},"cert_map"," — no cert found",[204,218,219,220,223],{},"Gateway checks ",[118,221,222],{},"allowed_domains"," — domain is in the config",[204,225,219,226,229],{},[118,227,228],{},"cert_state"," — no state yet (first request)",[204,231,232,233,236],{},"Gateway inserts domain into ",[118,234,235],{},"provisioning_set"," (dedup guard)",[204,238,239],{},"Gateway spawns a background ACME worker",[204,241,242],{},"Gateway returns a self-signed fallback cert — connection succeeds, traffic flows",[204,244,245],{},"Background worker obtains a real cert from Let's Encrypt",[204,247,248,249,251],{},"Cert is loaded into ",[118,250,215],{}," and persisted to disk",[204,253,254],{},"Next TLS handshake for this domain gets the real cert ✅",[122,256,258],{"id":257},"deduplication","Deduplication",[102,260,261,262,265,266,269],{},"A ",[118,263,264],{},"DashSet\u003CString>"," ensures only ",[154,267,268],{},"one ACME worker per domain"," runs at a time. If 100 requests arrive simultaneously for the same domain without a cert, only the first spawns a worker. The rest get the fallback cert.",[122,271,273],{"id":272},"failed-cert-handling","Failed cert handling",[102,275,276],{},"When cert provisioning fails:",[127,278,279,289],{},[130,280,281],{},[133,282,283,286],{},[136,284,285],{},"Field",[136,287,288],{},"Value",[146,290,291,301,309,320],{},[133,292,293,296],{},[151,294,295],{},"State",[151,297,298],{},[118,299,300],{},"Failed",[133,302,303,306],{},[151,304,305],{},"Error",[151,307,308],{},"Stored in memory",[133,310,311,314],{},[151,312,313],{},"Timestamp",[151,315,316,319],{},[118,317,318],{},"failed_at"," recorded",[133,321,322,325],{},[151,323,324],{},"Retry",[151,326,327,328],{},"Only when: new request arrives AND ",[118,329,330],{},"failed_at >= 24h",[102,332,333,336],{},[154,334,335],{},"No aggressive retries."," A failed cert stays failed until:",[338,339,340,343],"ul",{},[204,341,342],{},"A new TLS handshake arrives for that domain AND 24 hours have passed",[204,344,345],{},"The daily renewal cycle retries one failed cert per cycle",[106,347,349],{"id":348},"configuration","Configuration",[111,351,355],{"className":352,"code":353,"language":354,"meta":120,"style":120},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"plugins\": {\n    \"auto_ssl\": {\n      \"enabled\": true,\n      \"email\": \"admin@example.com\",\n      \"domains\": [\n        \"example.com\",\n        \"www.example.com\",\n        \"api.example.com\"\n      ],\n      \"cert_storage_path\": \"\u002Fvar\u002Flib\u002Fquayel\u002Fcerts\",\n      \"staging\": false,\n      \"renewal_days_before\": 30\n    }\n  }\n}\n","json",[118,356,357,366,385,401,418,442,457,469,481,492,498,519,534,548,554,560],{"__ignoreMap":120},[358,359,362],"span",{"class":360,"line":361},"line",1,[358,363,365],{"class":364},"sMK4o","{\n",[358,367,369,372,376,379,382],{"class":360,"line":368},2,[358,370,371],{"class":364},"  \"",[358,373,375],{"class":374},"spNyl","plugins",[358,377,378],{"class":364},"\"",[358,380,381],{"class":364},":",[358,383,384],{"class":364}," {\n",[358,386,388,391,395,397,399],{"class":360,"line":387},3,[358,389,390],{"class":364},"    \"",[358,392,394],{"class":393},"sBMFI","auto_ssl",[358,396,378],{"class":364},[358,398,381],{"class":364},[358,400,384],{"class":364},[358,402,404,407,411,413,415],{"class":360,"line":403},4,[358,405,406],{"class":364},"      \"",[358,408,410],{"class":409},"sbssI","enabled",[358,412,378],{"class":364},[358,414,381],{"class":364},[358,416,417],{"class":364}," true,\n",[358,419,421,423,426,428,430,433,437,439],{"class":360,"line":420},5,[358,422,406],{"class":364},[358,424,425],{"class":409},"email",[358,427,378],{"class":364},[358,429,381],{"class":364},[358,431,432],{"class":364}," \"",[358,434,436],{"class":435},"sfazB","admin@example.com",[358,438,378],{"class":364},[358,440,441],{"class":364},",\n",[358,443,445,447,450,452,454],{"class":360,"line":444},6,[358,446,406],{"class":364},[358,448,449],{"class":409},"domains",[358,451,378],{"class":364},[358,453,381],{"class":364},[358,455,456],{"class":364}," [\n",[358,458,460,463,465,467],{"class":360,"line":459},7,[358,461,462],{"class":364},"        \"",[358,464,209],{"class":435},[358,466,378],{"class":364},[358,468,441],{"class":364},[358,470,472,474,477,479],{"class":360,"line":471},8,[358,473,462],{"class":364},[358,475,476],{"class":435},"www.example.com",[358,478,378],{"class":364},[358,480,441],{"class":364},[358,482,484,486,489],{"class":360,"line":483},9,[358,485,462],{"class":364},[358,487,488],{"class":435},"api.example.com",[358,490,491],{"class":364},"\"\n",[358,493,495],{"class":360,"line":494},10,[358,496,497],{"class":364},"      ],\n",[358,499,501,503,506,508,510,512,515,517],{"class":360,"line":500},11,[358,502,406],{"class":364},[358,504,505],{"class":409},"cert_storage_path",[358,507,378],{"class":364},[358,509,381],{"class":364},[358,511,432],{"class":364},[358,513,514],{"class":435},"\u002Fvar\u002Flib\u002Fquayel\u002Fcerts",[358,516,378],{"class":364},[358,518,441],{"class":364},[358,520,522,524,527,529,531],{"class":360,"line":521},12,[358,523,406],{"class":364},[358,525,526],{"class":409},"staging",[358,528,378],{"class":364},[358,530,381],{"class":364},[358,532,533],{"class":364}," false,\n",[358,535,537,539,541,543,545],{"class":360,"line":536},13,[358,538,406],{"class":364},[358,540,194],{"class":409},[358,542,378],{"class":364},[358,544,381],{"class":364},[358,546,547],{"class":409}," 30\n",[358,549,551],{"class":360,"line":550},14,[358,552,553],{"class":364},"    }\n",[358,555,557],{"class":360,"line":556},15,[358,558,559],{"class":364},"  }\n",[358,561,563],{"class":360,"line":562},16,[358,564,565],{"class":364},"}\n",[106,567,569],{"id":568},"config-fields","Config fields",[127,571,572,590],{},[130,573,574],{},[133,575,576,578,581,584,587],{},[136,577,285],{},[136,579,580],{},"Type",[136,582,583],{},"Default",[136,585,586],{},"Required",[136,588,589],{},"Description",[146,591,592,612,629,649,667,685],{},[133,593,594,598,601,606,609],{},[151,595,596],{},[118,597,410],{},[151,599,600],{},"bool",[151,602,603],{},[118,604,605],{},"true",[151,607,608],{},"—",[151,610,611],{},"Enable\u002Fdisable Auto-SSL",[133,613,614,618,621,623,626],{},[151,615,616],{},[118,617,425],{},[151,619,620],{},"string",[151,622,608],{},[151,624,625],{},"✅",[151,627,628],{},"Email for Let's Encrypt account registration",[133,630,631,635,639,644,646],{},[151,632,633],{},[118,634,449],{},[151,636,620,637],{},[358,638],{},[151,640,641],{},[118,642,643],{},"[]",[151,645,625],{},[151,647,648],{},"Domain names to provision certificates for",[133,650,651,655,657,662,664],{},[151,652,653],{},[118,654,505],{},[151,656,620],{},[151,658,659],{},[118,660,661],{},"\"\u002Fvar\u002Flib\u002Fquayel\u002Fcerts\"",[151,663,608],{},[151,665,666],{},"Directory to store PEM files (write-through cache)",[133,668,669,673,675,680,682],{},[151,670,671],{},[118,672,526],{},[151,674,600],{},[151,676,677],{},[118,678,679],{},"false",[151,681,608],{},[151,683,684],{},"Use Let's Encrypt staging environment (for testing)",[133,686,687,691,694,699,701],{},[151,688,689],{},[118,690,194],{},[151,692,693],{},"int",[151,695,696],{},[118,697,698],{},"30",[151,700,608],{},[151,702,703],{},"Renew certificates this many days before expiry",[122,705,706],{"id":425},[118,707,425],{},[102,709,710],{},"Required. Used to register a Let's Encrypt account. Let's Encrypt sends expiry warnings to this address.",[122,712,713],{"id":449},[118,714,449],{},[102,716,717],{},"List of domain names the gateway should obtain certificates for. Certificates are only provisioned when the first TLS handshake arrives for a domain — adding a domain to this list does not immediately trigger cert fetching.",[122,719,720],{"id":505},[118,721,505],{},[102,723,724],{},"Directory where PEM files are stored as a write-through cache. On startup, cached certs are loaded from disk into memory. This is for restart recovery only — all runtime lookups use in-memory data structures.",[102,726,727],{},"Directory structure:",[111,729,732],{"className":730,"code":731,"language":116},[114],"\u002Fvar\u002Flib\u002Fquayel\u002Fcerts\u002F\n├── example.com\u002F\n│   ├── cert.pem\n│   └── key.pem\n├── www.example.com\u002F\n│   ├── cert.pem\n│   └── key.pem\n└── api.example.com\u002F\n    ├── cert.pem\n    └── key.pem\n",[118,733,731],{"__ignoreMap":120},[122,735,736],{"id":526},[118,737,526],{},[102,739,740,741,743,744,747],{},"When ",[118,742,605],{},", uses Let's Encrypt's staging environment (",[118,745,746],{},"acme-staging-v02.api.letsencrypt.org","). Staging certificates are not trusted by browsers but have no rate limits. Use this for testing.",[127,749,750,766],{},[130,751,752],{},[133,753,754,757,760,763],{},[136,755,756],{},"Environment",[136,758,759],{},"URL",[136,761,762],{},"Rate Limits",[136,764,765],{},"Trusted",[146,767,768,783],{},[133,769,770,773,778,781],{},[151,771,772],{},"Production",[151,774,775],{},[118,776,777],{},"acme-v02.api.letsencrypt.org",[151,779,780],{},"50 certs\u002Fdomain\u002Fweek",[151,782,625],{},[133,784,785,788,792,795],{},[151,786,787],{},"Staging",[151,789,790],{},[118,791,746],{},[151,793,794],{},"Unlimited",[151,796,797],{},"❌",[122,799,800],{"id":194},[118,801,194],{},[102,803,804],{},"The daily renewal check renews certificates that will expire within this many days. Default is 30 days (Let's Encrypt certs are valid for 90 days).",[106,806,808],{"id":807},"in-memory-architecture","In-memory architecture",[102,810,811,812,815],{},"All runtime state lives in ",[118,813,814],{},"Arc","-wrapped concurrent data structures. No disk I\u002FO on the hot path.",[127,817,818,833],{},[130,819,820],{},[133,821,822,825,827,830],{},[136,823,824],{},"Store",[136,826,580],{},[136,828,829],{},"Purpose",[136,831,832],{},"Lookup",[146,834,835,852,868,884,901],{},[133,836,837,841,846,849],{},[151,838,839],{},[118,840,215],{},[151,842,843],{},[118,844,845],{},"DashMap\u003CString, Arc\u003CCertifiedKey>>",[151,847,848],{},"Domain → TLS certificate",[151,850,851],{},"O(1)",[133,853,854,858,863,866],{},[151,855,856],{},[118,857,228],{},[151,859,860],{},[118,861,862],{},"DashMap\u003CString, CertState>",[151,864,865],{},"Domain → provisioning state",[151,867,851],{},[133,869,870,874,879,882],{},[151,871,872],{},[118,873,222],{},[151,875,876],{},[118,877,878],{},"RwLock\u003CHashSet\u003CString>>",[151,880,881],{},"Config-sourced whitelist",[151,883,851],{},[133,885,886,891,896,899],{},[151,887,888],{},[118,889,890],{},"challenge_tokens",[151,892,893],{},[118,894,895],{},"DashMap\u003CString, String>",[151,897,898],{},"HTTP-01 token → key-auth",[151,900,851],{},[133,902,903,907,911,914],{},[151,904,905],{},[118,906,235],{},[151,908,909],{},[118,910,264],{},[151,912,913],{},"Dedup guard (one worker\u002Fdomain)",[151,915,851],{},[122,917,919],{"id":918},"cert-states","Cert states",[127,921,922,934],{},[130,923,924],{},[133,925,926,928,931],{},[136,927,295],{},[136,929,930],{},"Meaning",[136,932,933],{},"Retry Behavior",[146,935,936,951,963,975],{},[133,937,938,943,949],{},[151,939,940],{},[118,941,942],{},"Valid",[151,944,945,946],{},"Cert loaded, has ",[118,947,948],{},"expires_at",[151,950,608],{},[133,952,953,958,961],{},[151,954,955],{},[118,956,957],{},"Pending",[151,959,960],{},"ACME worker in progress",[151,962,608],{},[133,964,965,969,972],{},[151,966,967],{},[118,968,300],{},[151,970,971],{},"Error + timestamp + attempt count",[151,973,974],{},"Next request after 24h, or daily cycle",[133,976,977,982,985],{},[151,978,979],{},[118,980,981],{},"Expired",[151,983,984],{},"Detected by daily check",[151,986,987],{},"Triggers re-provision",[106,989,991],{"id":990},"disk-io","Disk I\u002FO",[127,993,994,1006],{},[130,995,996],{},[133,997,998,1001,1003],{},[136,999,1000],{},"Operation",[136,1002,141],{},[136,1004,1005],{},"Pattern",[146,1007,1008,1026],{},[133,1009,1010,1015,1018],{},[151,1011,1012],{},[154,1013,1014],{},"Read",[151,1016,1017],{},"Startup only",[151,1019,1020,1021,1023,1024],{},"Load cached PEMs from ",[118,1022,505],{}," into ",[118,1025,215],{},[133,1027,1028,1033,1036],{},[151,1029,1030],{},[154,1031,1032],{},"Write",[151,1034,1035],{},"After cert obtained",[151,1037,1038,1039,1042],{},"Fire-and-forget ",[118,1040,1041],{},"tokio::spawn"," — save PEM to disk in background",[102,1044,1045],{},"Memory is the source of truth. Disk is a write-through cache for restart recovery.",[106,1047,1049],{"id":1048},"http-01-challenge","HTTP-01 challenge",[102,1051,1052],{},"The ACME HTTP-01 challenge works by serving a token at a well-known URL:",[111,1054,1057],{"className":1055,"code":1056,"language":116},[114],"http:\u002F\u002FDOMAIN\u002F.well-known\u002Facme-challenge\u002FTOKEN\n",[118,1058,1056],{"__ignoreMap":120},[102,1060,1061],{},"When Let's Encrypt verifies domain ownership:",[201,1063,1064,1069,1080,1083,1086],{},[204,1065,1066,1067],{},"The ACME client stores the token + key-authorization in ",[118,1068,890],{},[204,1070,1071,1072,1075,1076,1079],{},"The gateway's ",[118,1073,1074],{},"request_filter"," intercepts ",[118,1077,1078],{},"\u002F.well-known\u002Facme-challenge\u002F*"," requests",[204,1081,1082],{},"The gateway serves the key-authorization as plain text",[204,1084,1085],{},"Let's Encrypt verifies the token and issues the cert",[204,1087,1088],{},"The token is removed from memory",[1090,1091,1092,1095],"warning",{},[154,1093,1094],{},"Port 80 must be accessible"," from the internet for HTTP-01 challenges to work. If the gateway only listens on HTTPS, HTTP-01 challenges will fail.",[106,1097,1099],{"id":1098},"startup-behavior","Startup behavior",[102,1101,1102],{},"The gateway starts serving traffic immediately. HTTPS uses a self-signed fallback cert until real certs are provisioned.",[111,1104,1107],{"className":1105,"code":1106,"language":116},[114],"main.rs:\n  HTTP listener starts (:8080)     ← traffic flows immediately\n  HTTPS listener starts (:8443)    ← uses fallback cert\n  if auto_ssl.enabled:\n    load cached certs from disk\n    (no blocking — provisioning happens on first request)\n",[118,1108,1106],{"__ignoreMap":120},[102,1110,1111,1114],{},[154,1112,1113],{},"Non-blocking."," The gateway does not wait for certificates to be provisioned before starting. Traffic flows immediately with fallback certs.",[106,1116,1118],{"id":1117},"config-hot-reload","Config hot-reload",[102,1120,1121],{},"When the config file changes:",[201,1123,1124,1127,1130,1135],{},[204,1125,1126],{},"Config watcher detects SHA-256 hash change",[204,1128,1129],{},"New config is validated and compiled",[204,1131,1132,1134],{},[118,1133,174],{}," list is updated in memory",[204,1136,1137,1140],{},[154,1138,1139],{},"No certs are fetched"," — that happens on the next TLS handshake",[102,1142,1143],{},"Adding a new domain to the config only updates the allowed list. The cert is provisioned when the first request arrives for that domain.",[106,1145,1147],{"id":1146},"daily-renewal","Daily renewal",[102,1149,1150],{},"A background task runs every 24 hours:",[201,1152,1153,1158,1164,1167],{},[204,1154,1155,1156],{},"Check all domains in ",[118,1157,222],{},[204,1159,1160,1161,1163],{},"If a cert expires within ",[118,1162,194],{}," days → re-provision",[204,1165,1166],{},"If a cert is expired → re-provision",[204,1168,1169,1170,1172],{},"If a cert is failed AND ",[118,1171,330],{}," → retry (max 1 failed cert per cycle)",[106,1174,1176],{"id":1175},"prerequisites","Prerequisites",[201,1178,1179,1185,1191],{},[204,1180,1181,1184],{},[154,1182,1183],{},"Port 80"," must be accessible from the internet (for HTTP-01 challenges)",[204,1186,1187,1190],{},[154,1188,1189],{},"DNS"," must point the configured domains to the gateway's public IP",[204,1192,1193,1196],{},[154,1194,1195],{},"Let's Encrypt rate limits",": 50 certificates per registered domain per week (production)",[106,1198,1200],{"id":1199},"examples","Examples",[122,1202,1204],{"id":1203},"basic-setup","Basic setup",[111,1206,1208],{"className":352,"code":1207,"language":354,"meta":120,"style":120},"{\n  \"plugins\": {\n    \"auto_ssl\": {\n      \"enabled\": true,\n      \"email\": \"admin@example.com\",\n      \"domains\": [\"example.com\", \"www.example.com\"],\n      \"cert_storage_path\": \"\u002Fvar\u002Flib\u002Fquayel\u002Fcerts\",\n      \"staging\": false,\n      \"renewal_days_before\": 30\n    }\n  }\n}\n",[118,1209,1210,1214,1226,1238,1250,1268,1299,1317,1329,1341,1345,1349],{"__ignoreMap":120},[358,1211,1212],{"class":360,"line":361},[358,1213,365],{"class":364},[358,1215,1216,1218,1220,1222,1224],{"class":360,"line":368},[358,1217,371],{"class":364},[358,1219,375],{"class":374},[358,1221,378],{"class":364},[358,1223,381],{"class":364},[358,1225,384],{"class":364},[358,1227,1228,1230,1232,1234,1236],{"class":360,"line":387},[358,1229,390],{"class":364},[358,1231,394],{"class":393},[358,1233,378],{"class":364},[358,1235,381],{"class":364},[358,1237,384],{"class":364},[358,1239,1240,1242,1244,1246,1248],{"class":360,"line":403},[358,1241,406],{"class":364},[358,1243,410],{"class":409},[358,1245,378],{"class":364},[358,1247,381],{"class":364},[358,1249,417],{"class":364},[358,1251,1252,1254,1256,1258,1260,1262,1264,1266],{"class":360,"line":420},[358,1253,406],{"class":364},[358,1255,425],{"class":409},[358,1257,378],{"class":364},[358,1259,381],{"class":364},[358,1261,432],{"class":364},[358,1263,436],{"class":435},[358,1265,378],{"class":364},[358,1267,441],{"class":364},[358,1269,1270,1272,1274,1276,1278,1281,1283,1285,1287,1290,1292,1294,1296],{"class":360,"line":444},[358,1271,406],{"class":364},[358,1273,449],{"class":409},[358,1275,378],{"class":364},[358,1277,381],{"class":364},[358,1279,1280],{"class":364}," [",[358,1282,378],{"class":364},[358,1284,209],{"class":435},[358,1286,378],{"class":364},[358,1288,1289],{"class":364},",",[358,1291,432],{"class":364},[358,1293,476],{"class":435},[358,1295,378],{"class":364},[358,1297,1298],{"class":364},"],\n",[358,1300,1301,1303,1305,1307,1309,1311,1313,1315],{"class":360,"line":459},[358,1302,406],{"class":364},[358,1304,505],{"class":409},[358,1306,378],{"class":364},[358,1308,381],{"class":364},[358,1310,432],{"class":364},[358,1312,514],{"class":435},[358,1314,378],{"class":364},[358,1316,441],{"class":364},[358,1318,1319,1321,1323,1325,1327],{"class":360,"line":471},[358,1320,406],{"class":364},[358,1322,526],{"class":409},[358,1324,378],{"class":364},[358,1326,381],{"class":364},[358,1328,533],{"class":364},[358,1330,1331,1333,1335,1337,1339],{"class":360,"line":483},[358,1332,406],{"class":364},[358,1334,194],{"class":409},[358,1336,378],{"class":364},[358,1338,381],{"class":364},[358,1340,547],{"class":409},[358,1342,1343],{"class":360,"line":494},[358,1344,553],{"class":364},[358,1346,1347],{"class":360,"line":500},[358,1348,559],{"class":364},[358,1350,1351],{"class":360,"line":521},[358,1352,565],{"class":364},[122,1354,1356],{"id":1355},"testing-with-staging","Testing with staging",[111,1358,1360],{"className":352,"code":1359,"language":354,"meta":120,"style":120},"{\n  \"plugins\": {\n    \"auto_ssl\": {\n      \"enabled\": true,\n      \"email\": \"dev@example.com\",\n      \"domains\": [\"staging.example.com\"],\n      \"staging\": true,\n      \"renewal_days_before\": 7\n    }\n  }\n}\n",[118,1361,1362,1366,1378,1390,1402,1421,1442,1454,1467,1471,1475],{"__ignoreMap":120},[358,1363,1364],{"class":360,"line":361},[358,1365,365],{"class":364},[358,1367,1368,1370,1372,1374,1376],{"class":360,"line":368},[358,1369,371],{"class":364},[358,1371,375],{"class":374},[358,1373,378],{"class":364},[358,1375,381],{"class":364},[358,1377,384],{"class":364},[358,1379,1380,1382,1384,1386,1388],{"class":360,"line":387},[358,1381,390],{"class":364},[358,1383,394],{"class":393},[358,1385,378],{"class":364},[358,1387,381],{"class":364},[358,1389,384],{"class":364},[358,1391,1392,1394,1396,1398,1400],{"class":360,"line":403},[358,1393,406],{"class":364},[358,1395,410],{"class":409},[358,1397,378],{"class":364},[358,1399,381],{"class":364},[358,1401,417],{"class":364},[358,1403,1404,1406,1408,1410,1412,1414,1417,1419],{"class":360,"line":420},[358,1405,406],{"class":364},[358,1407,425],{"class":409},[358,1409,378],{"class":364},[358,1411,381],{"class":364},[358,1413,432],{"class":364},[358,1415,1416],{"class":435},"dev@example.com",[358,1418,378],{"class":364},[358,1420,441],{"class":364},[358,1422,1423,1425,1427,1429,1431,1433,1435,1438,1440],{"class":360,"line":444},[358,1424,406],{"class":364},[358,1426,449],{"class":409},[358,1428,378],{"class":364},[358,1430,381],{"class":364},[358,1432,1280],{"class":364},[358,1434,378],{"class":364},[358,1436,1437],{"class":435},"staging.example.com",[358,1439,378],{"class":364},[358,1441,1298],{"class":364},[358,1443,1444,1446,1448,1450,1452],{"class":360,"line":459},[358,1445,406],{"class":364},[358,1447,526],{"class":409},[358,1449,378],{"class":364},[358,1451,381],{"class":364},[358,1453,417],{"class":364},[358,1455,1456,1458,1460,1462,1464],{"class":360,"line":471},[358,1457,406],{"class":364},[358,1459,194],{"class":409},[358,1461,378],{"class":364},[358,1463,381],{"class":364},[358,1465,1466],{"class":409}," 7\n",[358,1468,1469],{"class":360,"line":483},[358,1470,553],{"class":364},[358,1472,1473],{"class":360,"line":494},[358,1474,559],{"class":364},[358,1476,1477],{"class":360,"line":500},[358,1478,565],{"class":364},[122,1480,1482],{"id":1481},"multiple-domains","Multiple domains",[111,1484,1486],{"className":352,"code":1485,"language":354,"meta":120,"style":120},"{\n  \"plugins\": {\n    \"auto_ssl\": {\n      \"enabled\": true,\n      \"email\": \"ops@example.com\",\n      \"domains\": [\n        \"example.com\",\n        \"www.example.com\",\n        \"api.example.com\",\n        \"admin.example.com\",\n        \"cdn.example.com\"\n      ],\n      \"cert_storage_path\": \"\u002Fvar\u002Flib\u002Fquayel\u002Fcerts\",\n      \"staging\": false,\n      \"renewal_days_before\": 30\n    }\n  }\n}\n",[118,1487,1488,1492,1504,1516,1528,1547,1559,1569,1579,1589,1600,1609,1613,1631,1643,1655,1659,1664],{"__ignoreMap":120},[358,1489,1490],{"class":360,"line":361},[358,1491,365],{"class":364},[358,1493,1494,1496,1498,1500,1502],{"class":360,"line":368},[358,1495,371],{"class":364},[358,1497,375],{"class":374},[358,1499,378],{"class":364},[358,1501,381],{"class":364},[358,1503,384],{"class":364},[358,1505,1506,1508,1510,1512,1514],{"class":360,"line":387},[358,1507,390],{"class":364},[358,1509,394],{"class":393},[358,1511,378],{"class":364},[358,1513,381],{"class":364},[358,1515,384],{"class":364},[358,1517,1518,1520,1522,1524,1526],{"class":360,"line":403},[358,1519,406],{"class":364},[358,1521,410],{"class":409},[358,1523,378],{"class":364},[358,1525,381],{"class":364},[358,1527,417],{"class":364},[358,1529,1530,1532,1534,1536,1538,1540,1543,1545],{"class":360,"line":420},[358,1531,406],{"class":364},[358,1533,425],{"class":409},[358,1535,378],{"class":364},[358,1537,381],{"class":364},[358,1539,432],{"class":364},[358,1541,1542],{"class":435},"ops@example.com",[358,1544,378],{"class":364},[358,1546,441],{"class":364},[358,1548,1549,1551,1553,1555,1557],{"class":360,"line":444},[358,1550,406],{"class":364},[358,1552,449],{"class":409},[358,1554,378],{"class":364},[358,1556,381],{"class":364},[358,1558,456],{"class":364},[358,1560,1561,1563,1565,1567],{"class":360,"line":459},[358,1562,462],{"class":364},[358,1564,209],{"class":435},[358,1566,378],{"class":364},[358,1568,441],{"class":364},[358,1570,1571,1573,1575,1577],{"class":360,"line":471},[358,1572,462],{"class":364},[358,1574,476],{"class":435},[358,1576,378],{"class":364},[358,1578,441],{"class":364},[358,1580,1581,1583,1585,1587],{"class":360,"line":483},[358,1582,462],{"class":364},[358,1584,488],{"class":435},[358,1586,378],{"class":364},[358,1588,441],{"class":364},[358,1590,1591,1593,1596,1598],{"class":360,"line":494},[358,1592,462],{"class":364},[358,1594,1595],{"class":435},"admin.example.com",[358,1597,378],{"class":364},[358,1599,441],{"class":364},[358,1601,1602,1604,1607],{"class":360,"line":500},[358,1603,462],{"class":364},[358,1605,1606],{"class":435},"cdn.example.com",[358,1608,491],{"class":364},[358,1610,1611],{"class":360,"line":521},[358,1612,497],{"class":364},[358,1614,1615,1617,1619,1621,1623,1625,1627,1629],{"class":360,"line":536},[358,1616,406],{"class":364},[358,1618,505],{"class":409},[358,1620,378],{"class":364},[358,1622,381],{"class":364},[358,1624,432],{"class":364},[358,1626,514],{"class":435},[358,1628,378],{"class":364},[358,1630,441],{"class":364},[358,1632,1633,1635,1637,1639,1641],{"class":360,"line":550},[358,1634,406],{"class":364},[358,1636,526],{"class":409},[358,1638,378],{"class":364},[358,1640,381],{"class":364},[358,1642,533],{"class":364},[358,1644,1645,1647,1649,1651,1653],{"class":360,"line":556},[358,1646,406],{"class":364},[358,1648,194],{"class":409},[358,1650,378],{"class":364},[358,1652,381],{"class":364},[358,1654,547],{"class":409},[358,1656,1657],{"class":360,"line":562},[358,1658,553],{"class":364},[358,1660,1662],{"class":360,"line":1661},17,[358,1663,559],{"class":364},[358,1665,1667],{"class":360,"line":1666},18,[358,1668,565],{"class":364},[122,1670,1672],{"id":1671},"full-gateway-config-with-auto-ssl","Full gateway config with Auto-SSL",[111,1674,1676],{"className":352,"code":1675,"language":354,"meta":120,"style":120},"{\n  \"gateway_id\": \"gw-prod-01\",\n  \"gateway_name\": \"production\",\n  \"server_name\": \"example.com\",\n  \"config_version\": \"2026-01-01-001\",\n  \"listen\": {\n    \"http\": \"0.0.0.0:80\",\n    \"https\": \"0.0.0.0:443\"\n  },\n  \"client_ip\": {\n    \"source\": \"header\",\n    \"header\": \"X-Forwarded-For\"\n  },\n  \"plugins\": {\n    \"ip_intel\": { \"enabled\": true },\n    \"auto_ssl\": {\n      \"enabled\": true,\n      \"email\": \"admin@example.com\",\n      \"domains\": [\"example.com\", \"www.example.com\"],\n      \"cert_storage_path\": \"\u002Fvar\u002Flib\u002Fquayel\u002Fcerts\",\n      \"staging\": false,\n      \"renewal_days_before\": 30\n    },\n    \"firewall\": {\n      \"enabled\": true,\n      \"rules\": [\n        {\n          \"id\": \"block-tor\",\n          \"name\": \"Block Tor Exit Nodes\",\n          \"enabled\": true,\n          \"conditions\": [\n            { \"left\": \"ip.is_tor\", \"operator\": \"equals\", \"value\": true }\n          ],\n          \"action\": { \"type\": \"block\", \"status\": 403 }\n        }\n      ]\n    },\n    \"load_balancer\": {\n      \"enabled\": true,\n      \"load_balancers\": [\n        {\n          \"id\": \"default\",\n          \"name\": \"Backend\",\n          \"enabled\": true,\n          \"conditions\": [],\n          \"algorithm\": { \"type\": \"round_robin\" },\n          \"targets\": [\n            {\n              \"id\": \"backend-1\",\n              \"host\": \"127.0.0.1\",\n              \"port\": 3000,\n              \"protocol\": \"http\",\n              \"weight\": 100,\n              \"enabled\": true\n            }\n          ],\n          \"host_header\": { \"mode\": \"visitor\" },\n          \"health_check\": { \"mode\": \"off\" }\n        }\n      ]\n    },\n    \"logging\": {\n      \"enabled\": true,\n      \"mode\": \"file\",\n      \"file\": {\n        \"path\": \"\u002Fvar\u002Flog\u002Fquayel\u002Faccess.jsonl\",\n        \"buffer_size\": 8192,\n        \"flush_interval_ms\": 1000\n      }\n    }\n  }\n}\n",[118,1677,1678,1682,1702,1722,1741,1761,1774,1794,1812,1817,1830,1850,1867,1871,1883,1911,1923,1935,1953,1982,2001,2014,2027,2033,2047,2060,2074,2080,2103,2124,2137,2151,2208,2214,2260,2266,2272,2277,2291,2304,2318,2323,2343,2363,2376,2390,2421,2435,2441,2462,2483,2500,2520,2537,2551,2557,2562,2594,2625,2630,2635,2640,2654,2667,2687,2700,2721,2738,2753,2759,2764,2769],{"__ignoreMap":120},[358,1679,1680],{"class":360,"line":361},[358,1681,365],{"class":364},[358,1683,1684,1686,1689,1691,1693,1695,1698,1700],{"class":360,"line":368},[358,1685,371],{"class":364},[358,1687,1688],{"class":374},"gateway_id",[358,1690,378],{"class":364},[358,1692,381],{"class":364},[358,1694,432],{"class":364},[358,1696,1697],{"class":435},"gw-prod-01",[358,1699,378],{"class":364},[358,1701,441],{"class":364},[358,1703,1704,1706,1709,1711,1713,1715,1718,1720],{"class":360,"line":387},[358,1705,371],{"class":364},[358,1707,1708],{"class":374},"gateway_name",[358,1710,378],{"class":364},[358,1712,381],{"class":364},[358,1714,432],{"class":364},[358,1716,1717],{"class":435},"production",[358,1719,378],{"class":364},[358,1721,441],{"class":364},[358,1723,1724,1726,1729,1731,1733,1735,1737,1739],{"class":360,"line":403},[358,1725,371],{"class":364},[358,1727,1728],{"class":374},"server_name",[358,1730,378],{"class":364},[358,1732,381],{"class":364},[358,1734,432],{"class":364},[358,1736,209],{"class":435},[358,1738,378],{"class":364},[358,1740,441],{"class":364},[358,1742,1743,1745,1748,1750,1752,1754,1757,1759],{"class":360,"line":420},[358,1744,371],{"class":364},[358,1746,1747],{"class":374},"config_version",[358,1749,378],{"class":364},[358,1751,381],{"class":364},[358,1753,432],{"class":364},[358,1755,1756],{"class":435},"2026-01-01-001",[358,1758,378],{"class":364},[358,1760,441],{"class":364},[358,1762,1763,1765,1768,1770,1772],{"class":360,"line":444},[358,1764,371],{"class":364},[358,1766,1767],{"class":374},"listen",[358,1769,378],{"class":364},[358,1771,381],{"class":364},[358,1773,384],{"class":364},[358,1775,1776,1778,1781,1783,1785,1787,1790,1792],{"class":360,"line":459},[358,1777,390],{"class":364},[358,1779,1780],{"class":393},"http",[358,1782,378],{"class":364},[358,1784,381],{"class":364},[358,1786,432],{"class":364},[358,1788,1789],{"class":435},"0.0.0.0:80",[358,1791,378],{"class":364},[358,1793,441],{"class":364},[358,1795,1796,1798,1801,1803,1805,1807,1810],{"class":360,"line":471},[358,1797,390],{"class":364},[358,1799,1800],{"class":393},"https",[358,1802,378],{"class":364},[358,1804,381],{"class":364},[358,1806,432],{"class":364},[358,1808,1809],{"class":435},"0.0.0.0:443",[358,1811,491],{"class":364},[358,1813,1814],{"class":360,"line":483},[358,1815,1816],{"class":364},"  },\n",[358,1818,1819,1821,1824,1826,1828],{"class":360,"line":494},[358,1820,371],{"class":364},[358,1822,1823],{"class":374},"client_ip",[358,1825,378],{"class":364},[358,1827,381],{"class":364},[358,1829,384],{"class":364},[358,1831,1832,1834,1837,1839,1841,1843,1846,1848],{"class":360,"line":500},[358,1833,390],{"class":364},[358,1835,1836],{"class":393},"source",[358,1838,378],{"class":364},[358,1840,381],{"class":364},[358,1842,432],{"class":364},[358,1844,1845],{"class":435},"header",[358,1847,378],{"class":364},[358,1849,441],{"class":364},[358,1851,1852,1854,1856,1858,1860,1862,1865],{"class":360,"line":521},[358,1853,390],{"class":364},[358,1855,1845],{"class":393},[358,1857,378],{"class":364},[358,1859,381],{"class":364},[358,1861,432],{"class":364},[358,1863,1864],{"class":435},"X-Forwarded-For",[358,1866,491],{"class":364},[358,1868,1869],{"class":360,"line":536},[358,1870,1816],{"class":364},[358,1872,1873,1875,1877,1879,1881],{"class":360,"line":550},[358,1874,371],{"class":364},[358,1876,375],{"class":374},[358,1878,378],{"class":364},[358,1880,381],{"class":364},[358,1882,384],{"class":364},[358,1884,1885,1887,1890,1892,1894,1897,1899,1901,1903,1905,1908],{"class":360,"line":556},[358,1886,390],{"class":364},[358,1888,1889],{"class":393},"ip_intel",[358,1891,378],{"class":364},[358,1893,381],{"class":364},[358,1895,1896],{"class":364}," {",[358,1898,432],{"class":364},[358,1900,410],{"class":409},[358,1902,378],{"class":364},[358,1904,381],{"class":364},[358,1906,1907],{"class":364}," true",[358,1909,1910],{"class":364}," },\n",[358,1912,1913,1915,1917,1919,1921],{"class":360,"line":562},[358,1914,390],{"class":364},[358,1916,394],{"class":393},[358,1918,378],{"class":364},[358,1920,381],{"class":364},[358,1922,384],{"class":364},[358,1924,1925,1927,1929,1931,1933],{"class":360,"line":1661},[358,1926,406],{"class":364},[358,1928,410],{"class":409},[358,1930,378],{"class":364},[358,1932,381],{"class":364},[358,1934,417],{"class":364},[358,1936,1937,1939,1941,1943,1945,1947,1949,1951],{"class":360,"line":1666},[358,1938,406],{"class":364},[358,1940,425],{"class":409},[358,1942,378],{"class":364},[358,1944,381],{"class":364},[358,1946,432],{"class":364},[358,1948,436],{"class":435},[358,1950,378],{"class":364},[358,1952,441],{"class":364},[358,1954,1956,1958,1960,1962,1964,1966,1968,1970,1972,1974,1976,1978,1980],{"class":360,"line":1955},19,[358,1957,406],{"class":364},[358,1959,449],{"class":409},[358,1961,378],{"class":364},[358,1963,381],{"class":364},[358,1965,1280],{"class":364},[358,1967,378],{"class":364},[358,1969,209],{"class":435},[358,1971,378],{"class":364},[358,1973,1289],{"class":364},[358,1975,432],{"class":364},[358,1977,476],{"class":435},[358,1979,378],{"class":364},[358,1981,1298],{"class":364},[358,1983,1985,1987,1989,1991,1993,1995,1997,1999],{"class":360,"line":1984},20,[358,1986,406],{"class":364},[358,1988,505],{"class":409},[358,1990,378],{"class":364},[358,1992,381],{"class":364},[358,1994,432],{"class":364},[358,1996,514],{"class":435},[358,1998,378],{"class":364},[358,2000,441],{"class":364},[358,2002,2004,2006,2008,2010,2012],{"class":360,"line":2003},21,[358,2005,406],{"class":364},[358,2007,526],{"class":409},[358,2009,378],{"class":364},[358,2011,381],{"class":364},[358,2013,533],{"class":364},[358,2015,2017,2019,2021,2023,2025],{"class":360,"line":2016},22,[358,2018,406],{"class":364},[358,2020,194],{"class":409},[358,2022,378],{"class":364},[358,2024,381],{"class":364},[358,2026,547],{"class":409},[358,2028,2030],{"class":360,"line":2029},23,[358,2031,2032],{"class":364},"    },\n",[358,2034,2036,2038,2041,2043,2045],{"class":360,"line":2035},24,[358,2037,390],{"class":364},[358,2039,2040],{"class":393},"firewall",[358,2042,378],{"class":364},[358,2044,381],{"class":364},[358,2046,384],{"class":364},[358,2048,2050,2052,2054,2056,2058],{"class":360,"line":2049},25,[358,2051,406],{"class":364},[358,2053,410],{"class":409},[358,2055,378],{"class":364},[358,2057,381],{"class":364},[358,2059,417],{"class":364},[358,2061,2063,2065,2068,2070,2072],{"class":360,"line":2062},26,[358,2064,406],{"class":364},[358,2066,2067],{"class":409},"rules",[358,2069,378],{"class":364},[358,2071,381],{"class":364},[358,2073,456],{"class":364},[358,2075,2077],{"class":360,"line":2076},27,[358,2078,2079],{"class":364},"        {\n",[358,2081,2083,2086,2090,2092,2094,2096,2099,2101],{"class":360,"line":2082},28,[358,2084,2085],{"class":364},"          \"",[358,2087,2089],{"class":2088},"swJcz","id",[358,2091,378],{"class":364},[358,2093,381],{"class":364},[358,2095,432],{"class":364},[358,2097,2098],{"class":435},"block-tor",[358,2100,378],{"class":364},[358,2102,441],{"class":364},[358,2104,2106,2108,2111,2113,2115,2117,2120,2122],{"class":360,"line":2105},29,[358,2107,2085],{"class":364},[358,2109,2110],{"class":2088},"name",[358,2112,378],{"class":364},[358,2114,381],{"class":364},[358,2116,432],{"class":364},[358,2118,2119],{"class":435},"Block Tor Exit Nodes",[358,2121,378],{"class":364},[358,2123,441],{"class":364},[358,2125,2127,2129,2131,2133,2135],{"class":360,"line":2126},30,[358,2128,2085],{"class":364},[358,2130,410],{"class":2088},[358,2132,378],{"class":364},[358,2134,381],{"class":364},[358,2136,417],{"class":364},[358,2138,2140,2142,2145,2147,2149],{"class":360,"line":2139},31,[358,2141,2085],{"class":364},[358,2143,2144],{"class":2088},"conditions",[358,2146,378],{"class":364},[358,2148,381],{"class":364},[358,2150,456],{"class":364},[358,2152,2154,2157,2159,2163,2165,2167,2169,2172,2174,2176,2178,2181,2183,2185,2187,2190,2192,2194,2196,2199,2201,2203,2205],{"class":360,"line":2153},32,[358,2155,2156],{"class":364},"            {",[358,2158,432],{"class":364},[358,2160,2162],{"class":2161},"su27w","left",[358,2164,378],{"class":364},[358,2166,381],{"class":364},[358,2168,432],{"class":364},[358,2170,2171],{"class":435},"ip.is_tor",[358,2173,378],{"class":364},[358,2175,1289],{"class":364},[358,2177,432],{"class":364},[358,2179,2180],{"class":2161},"operator",[358,2182,378],{"class":364},[358,2184,381],{"class":364},[358,2186,432],{"class":364},[358,2188,2189],{"class":435},"equals",[358,2191,378],{"class":364},[358,2193,1289],{"class":364},[358,2195,432],{"class":364},[358,2197,2198],{"class":2161},"value",[358,2200,378],{"class":364},[358,2202,381],{"class":364},[358,2204,1907],{"class":364},[358,2206,2207],{"class":364}," }\n",[358,2209,2211],{"class":360,"line":2210},33,[358,2212,2213],{"class":364},"          ],\n",[358,2215,2217,2219,2222,2224,2226,2228,2230,2233,2235,2237,2239,2242,2244,2246,2248,2251,2253,2255,2258],{"class":360,"line":2216},34,[358,2218,2085],{"class":364},[358,2220,2221],{"class":2088},"action",[358,2223,378],{"class":364},[358,2225,381],{"class":364},[358,2227,1896],{"class":364},[358,2229,432],{"class":364},[358,2231,2232],{"class":2161},"type",[358,2234,378],{"class":364},[358,2236,381],{"class":364},[358,2238,432],{"class":364},[358,2240,2241],{"class":435},"block",[358,2243,378],{"class":364},[358,2245,1289],{"class":364},[358,2247,432],{"class":364},[358,2249,2250],{"class":2161},"status",[358,2252,378],{"class":364},[358,2254,381],{"class":364},[358,2256,2257],{"class":409}," 403",[358,2259,2207],{"class":364},[358,2261,2263],{"class":360,"line":2262},35,[358,2264,2265],{"class":364},"        }\n",[358,2267,2269],{"class":360,"line":2268},36,[358,2270,2271],{"class":364},"      ]\n",[358,2273,2275],{"class":360,"line":2274},37,[358,2276,2032],{"class":364},[358,2278,2280,2282,2285,2287,2289],{"class":360,"line":2279},38,[358,2281,390],{"class":364},[358,2283,2284],{"class":393},"load_balancer",[358,2286,378],{"class":364},[358,2288,381],{"class":364},[358,2290,384],{"class":364},[358,2292,2294,2296,2298,2300,2302],{"class":360,"line":2293},39,[358,2295,406],{"class":364},[358,2297,410],{"class":409},[358,2299,378],{"class":364},[358,2301,381],{"class":364},[358,2303,417],{"class":364},[358,2305,2307,2309,2312,2314,2316],{"class":360,"line":2306},40,[358,2308,406],{"class":364},[358,2310,2311],{"class":409},"load_balancers",[358,2313,378],{"class":364},[358,2315,381],{"class":364},[358,2317,456],{"class":364},[358,2319,2321],{"class":360,"line":2320},41,[358,2322,2079],{"class":364},[358,2324,2326,2328,2330,2332,2334,2336,2339,2341],{"class":360,"line":2325},42,[358,2327,2085],{"class":364},[358,2329,2089],{"class":2088},[358,2331,378],{"class":364},[358,2333,381],{"class":364},[358,2335,432],{"class":364},[358,2337,2338],{"class":435},"default",[358,2340,378],{"class":364},[358,2342,441],{"class":364},[358,2344,2346,2348,2350,2352,2354,2356,2359,2361],{"class":360,"line":2345},43,[358,2347,2085],{"class":364},[358,2349,2110],{"class":2088},[358,2351,378],{"class":364},[358,2353,381],{"class":364},[358,2355,432],{"class":364},[358,2357,2358],{"class":435},"Backend",[358,2360,378],{"class":364},[358,2362,441],{"class":364},[358,2364,2366,2368,2370,2372,2374],{"class":360,"line":2365},44,[358,2367,2085],{"class":364},[358,2369,410],{"class":2088},[358,2371,378],{"class":364},[358,2373,381],{"class":364},[358,2375,417],{"class":364},[358,2377,2379,2381,2383,2385,2387],{"class":360,"line":2378},45,[358,2380,2085],{"class":364},[358,2382,2144],{"class":2088},[358,2384,378],{"class":364},[358,2386,381],{"class":364},[358,2388,2389],{"class":364}," [],\n",[358,2391,2393,2395,2398,2400,2402,2404,2406,2408,2410,2412,2414,2417,2419],{"class":360,"line":2392},46,[358,2394,2085],{"class":364},[358,2396,2397],{"class":2088},"algorithm",[358,2399,378],{"class":364},[358,2401,381],{"class":364},[358,2403,1896],{"class":364},[358,2405,432],{"class":364},[358,2407,2232],{"class":2161},[358,2409,378],{"class":364},[358,2411,381],{"class":364},[358,2413,432],{"class":364},[358,2415,2416],{"class":435},"round_robin",[358,2418,378],{"class":364},[358,2420,1910],{"class":364},[358,2422,2424,2426,2429,2431,2433],{"class":360,"line":2423},47,[358,2425,2085],{"class":364},[358,2427,2428],{"class":2088},"targets",[358,2430,378],{"class":364},[358,2432,381],{"class":364},[358,2434,456],{"class":364},[358,2436,2438],{"class":360,"line":2437},48,[358,2439,2440],{"class":364},"            {\n",[358,2442,2444,2447,2449,2451,2453,2455,2458,2460],{"class":360,"line":2443},49,[358,2445,2446],{"class":364},"              \"",[358,2448,2089],{"class":2161},[358,2450,378],{"class":364},[358,2452,381],{"class":364},[358,2454,432],{"class":364},[358,2456,2457],{"class":435},"backend-1",[358,2459,378],{"class":364},[358,2461,441],{"class":364},[358,2463,2465,2467,2470,2472,2474,2476,2479,2481],{"class":360,"line":2464},50,[358,2466,2446],{"class":364},[358,2468,2469],{"class":2161},"host",[358,2471,378],{"class":364},[358,2473,381],{"class":364},[358,2475,432],{"class":364},[358,2477,2478],{"class":435},"127.0.0.1",[358,2480,378],{"class":364},[358,2482,441],{"class":364},[358,2484,2486,2488,2491,2493,2495,2498],{"class":360,"line":2485},51,[358,2487,2446],{"class":364},[358,2489,2490],{"class":2161},"port",[358,2492,378],{"class":364},[358,2494,381],{"class":364},[358,2496,2497],{"class":409}," 3000",[358,2499,441],{"class":364},[358,2501,2503,2505,2508,2510,2512,2514,2516,2518],{"class":360,"line":2502},52,[358,2504,2446],{"class":364},[358,2506,2507],{"class":2161},"protocol",[358,2509,378],{"class":364},[358,2511,381],{"class":364},[358,2513,432],{"class":364},[358,2515,1780],{"class":435},[358,2517,378],{"class":364},[358,2519,441],{"class":364},[358,2521,2523,2525,2528,2530,2532,2535],{"class":360,"line":2522},53,[358,2524,2446],{"class":364},[358,2526,2527],{"class":2161},"weight",[358,2529,378],{"class":364},[358,2531,381],{"class":364},[358,2533,2534],{"class":409}," 100",[358,2536,441],{"class":364},[358,2538,2540,2542,2544,2546,2548],{"class":360,"line":2539},54,[358,2541,2446],{"class":364},[358,2543,410],{"class":2161},[358,2545,378],{"class":364},[358,2547,381],{"class":364},[358,2549,2550],{"class":364}," true\n",[358,2552,2554],{"class":360,"line":2553},55,[358,2555,2556],{"class":364},"            }\n",[358,2558,2560],{"class":360,"line":2559},56,[358,2561,2213],{"class":364},[358,2563,2565,2567,2570,2572,2574,2576,2578,2581,2583,2585,2587,2590,2592],{"class":360,"line":2564},57,[358,2566,2085],{"class":364},[358,2568,2569],{"class":2088},"host_header",[358,2571,378],{"class":364},[358,2573,381],{"class":364},[358,2575,1896],{"class":364},[358,2577,432],{"class":364},[358,2579,2580],{"class":2161},"mode",[358,2582,378],{"class":364},[358,2584,381],{"class":364},[358,2586,432],{"class":364},[358,2588,2589],{"class":435},"visitor",[358,2591,378],{"class":364},[358,2593,1910],{"class":364},[358,2595,2597,2599,2602,2604,2606,2608,2610,2612,2614,2616,2618,2621,2623],{"class":360,"line":2596},58,[358,2598,2085],{"class":364},[358,2600,2601],{"class":2088},"health_check",[358,2603,378],{"class":364},[358,2605,381],{"class":364},[358,2607,1896],{"class":364},[358,2609,432],{"class":364},[358,2611,2580],{"class":2161},[358,2613,378],{"class":364},[358,2615,381],{"class":364},[358,2617,432],{"class":364},[358,2619,2620],{"class":435},"off",[358,2622,378],{"class":364},[358,2624,2207],{"class":364},[358,2626,2628],{"class":360,"line":2627},59,[358,2629,2265],{"class":364},[358,2631,2633],{"class":360,"line":2632},60,[358,2634,2271],{"class":364},[358,2636,2638],{"class":360,"line":2637},61,[358,2639,2032],{"class":364},[358,2641,2643,2645,2648,2650,2652],{"class":360,"line":2642},62,[358,2644,390],{"class":364},[358,2646,2647],{"class":393},"logging",[358,2649,378],{"class":364},[358,2651,381],{"class":364},[358,2653,384],{"class":364},[358,2655,2657,2659,2661,2663,2665],{"class":360,"line":2656},63,[358,2658,406],{"class":364},[358,2660,410],{"class":409},[358,2662,378],{"class":364},[358,2664,381],{"class":364},[358,2666,417],{"class":364},[358,2668,2670,2672,2674,2676,2678,2680,2683,2685],{"class":360,"line":2669},64,[358,2671,406],{"class":364},[358,2673,2580],{"class":409},[358,2675,378],{"class":364},[358,2677,381],{"class":364},[358,2679,432],{"class":364},[358,2681,2682],{"class":435},"file",[358,2684,378],{"class":364},[358,2686,441],{"class":364},[358,2688,2690,2692,2694,2696,2698],{"class":360,"line":2689},65,[358,2691,406],{"class":364},[358,2693,2682],{"class":409},[358,2695,378],{"class":364},[358,2697,381],{"class":364},[358,2699,384],{"class":364},[358,2701,2703,2705,2708,2710,2712,2714,2717,2719],{"class":360,"line":2702},66,[358,2704,462],{"class":364},[358,2706,2707],{"class":2088},"path",[358,2709,378],{"class":364},[358,2711,381],{"class":364},[358,2713,432],{"class":364},[358,2715,2716],{"class":435},"\u002Fvar\u002Flog\u002Fquayel\u002Faccess.jsonl",[358,2718,378],{"class":364},[358,2720,441],{"class":364},[358,2722,2724,2726,2729,2731,2733,2736],{"class":360,"line":2723},67,[358,2725,462],{"class":364},[358,2727,2728],{"class":2088},"buffer_size",[358,2730,378],{"class":364},[358,2732,381],{"class":364},[358,2734,2735],{"class":409}," 8192",[358,2737,441],{"class":364},[358,2739,2741,2743,2746,2748,2750],{"class":360,"line":2740},68,[358,2742,462],{"class":364},[358,2744,2745],{"class":2088},"flush_interval_ms",[358,2747,378],{"class":364},[358,2749,381],{"class":364},[358,2751,2752],{"class":409}," 1000\n",[358,2754,2756],{"class":360,"line":2755},69,[358,2757,2758],{"class":364},"      }\n",[358,2760,2762],{"class":360,"line":2761},70,[358,2763,553],{"class":364},[358,2765,2767],{"class":360,"line":2766},71,[358,2768,559],{"class":364},[358,2770,2772],{"class":360,"line":2771},72,[358,2773,565],{"class":364},[106,2775,2777],{"id":2776},"troubleshooting","Troubleshooting",[122,2779,2781],{"id":2780},"cert-not-being-provisioned","Cert not being provisioned",[201,2783,2784,2787,2790,2796],{},[204,2785,2786],{},"Check that port 80 is accessible from the internet",[204,2788,2789],{},"Check that DNS points to the gateway's IP",[204,2791,2792,2793],{},"Check logs for ACME errors: ",[118,2794,2795],{},"RUST_LOG=debug",[204,2797,2798,2799,2802],{},"Try with ",[118,2800,2801],{},"\"staging\": true"," first to avoid rate limits",[122,2804,2806],{"id":2805},"challenge-verification-fails","Challenge verification fails",[102,2808,2809,2810,2813],{},"Let's Encrypt needs to reach ",[118,2811,2812],{},"http:\u002F\u002FDOMAIN\u002F.well-known\u002Facme-challenge\u002FTOKEN",". Common issues:",[338,2815,2816,2819,2822,2825],{},[204,2817,2818],{},"Port 80 is blocked by a firewall",[204,2820,2821],{},"DNS is not pointing to the gateway",[204,2823,2824],{},"Another service is listening on port 80",[204,2826,2827],{},"The domain is behind a CDN that doesn't proxy port 80",[122,2829,2831],{"id":2830},"cert-provisioning-takes-too-long","Cert provisioning takes too long",[102,2833,2834],{},"ACME provisioning typically takes 5-30 seconds. If it's taking longer:",[338,2836,2837,2842,2845],{},[204,2838,2839,2840],{},"Check network connectivity to ",[118,2841,777],{},[204,2843,2844],{},"Check logs for timeout errors",[204,2846,2847],{},"The challenge poll timeout is 60 seconds (30 polls × 2s interval)",[122,2849,2851],{"id":2850},"fallback-cert-shown-in-browser","Fallback cert shown in browser",[102,2853,2854],{},"If browsers show a certificate warning:",[338,2856,2857,2860,2863],{},[204,2858,2859],{},"The real cert hasn't been provisioned yet (first request)",[204,2861,2862],{},"The cert provisioning failed (check logs)",[204,2864,2865,2866,2868],{},"The domain is not in the ",[118,2867,174],{}," list",[106,2870,2872],{"id":2871},"performance","Performance",[127,2874,2875,2884],{},[130,2876,2877],{},[133,2878,2879,2882],{},[136,2880,2881],{},"Metric",[136,2883,288],{},[146,2885,2886,2894,2902,2910,2918,2926],{},[133,2887,2888,2891],{},[151,2889,2890],{},"Cert lookup",[151,2892,2893],{},"O(1) — DashMap",[133,2895,2896,2899],{},[151,2897,2898],{},"SNI resolution",[151,2900,2901],{},"O(1) — in-memory",[133,2903,2904,2907],{},[151,2905,2906],{},"Disk I\u002FO on hot path",[151,2908,2909],{},"None — memory is source of truth",[133,2911,2912,2915],{},[151,2913,2914],{},"Startup delay",[151,2916,2917],{},"None — non-blocking",[133,2919,2920,2923],{},[151,2921,2922],{},"Memory per cert",[151,2924,2925],{},"~4 KB (CertifiedKey + metadata)",[133,2927,2928,2931],{},[151,2929,2930],{},"ACME provisioning time",[151,2932,2933],{},"5-30 seconds (first request only)",[106,2935,2937],{"id":2936},"limitations","Limitations",[338,2939,2940,2946,2952,2958,2964],{},[204,2941,2942,2945],{},[154,2943,2944],{},"HTTP-01 only",": Only HTTP-01 challenges are supported (not DNS-01 or TLS-ALPN-01)",[204,2947,2948,2951],{},[154,2949,2950],{},"Single domain per cert",": Each certificate covers one domain (no multi-SAN)",[204,2953,2954,2957],{},[154,2955,2956],{},"No wildcard certs",": Wildcard certificates require DNS-01 challenges",[204,2959,2960,2963],{},[154,2961,2962],{},"Port 80 required",": HTTP-01 challenges need port 80 accessible from the internet",[204,2965,2966,2969],{},[154,2967,2968],{},"Let's Encrypt only",": Currently hardcoded to Let's Encrypt (not other ACME CAs)",[2971,2972,2973],"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 .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 .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 .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}",{"title":120,"searchDepth":368,"depth":368,"links":2975},[2976,2982,2983,2990,2993,2994,2995,2996,2997,2998,2999,3005,3011,3012],{"id":108,"depth":368,"text":109,"children":2977},[2978,2979,2980,2981],{"id":124,"depth":387,"text":125},{"id":198,"depth":387,"text":199},{"id":257,"depth":387,"text":258},{"id":272,"depth":387,"text":273},{"id":348,"depth":368,"text":349},{"id":568,"depth":368,"text":569,"children":2984},[2985,2986,2987,2988,2989],{"id":425,"depth":387,"text":425},{"id":449,"depth":387,"text":449},{"id":505,"depth":387,"text":505},{"id":526,"depth":387,"text":526},{"id":194,"depth":387,"text":194},{"id":807,"depth":368,"text":808,"children":2991},[2992],{"id":918,"depth":387,"text":919},{"id":990,"depth":368,"text":991},{"id":1048,"depth":368,"text":1049},{"id":1098,"depth":368,"text":1099},{"id":1117,"depth":368,"text":1118},{"id":1146,"depth":368,"text":1147},{"id":1175,"depth":368,"text":1176},{"id":1199,"depth":368,"text":1200,"children":3000},[3001,3002,3003,3004],{"id":1203,"depth":387,"text":1204},{"id":1355,"depth":387,"text":1356},{"id":1481,"depth":387,"text":1482},{"id":1671,"depth":387,"text":1672},{"id":2776,"depth":368,"text":2777,"children":3006},[3007,3008,3009,3010],{"id":2780,"depth":387,"text":2781},{"id":2805,"depth":387,"text":2806},{"id":2830,"depth":387,"text":2831},{"id":2850,"depth":387,"text":2851},{"id":2871,"depth":368,"text":2872},{"id":2936,"depth":368,"text":2937},"On-demand TLS certificate provisioning via Let's Encrypt ACME (HTTP-01) with in-memory cert state.","md",null,{},{"icon":67},{"title":64,"description":3013},"iTp-155Qx3DFNZZFo_EdtcL3lrjQSYPSDd11EaxXITw",[3021,3023],{"title":59,"path":60,"stem":61,"description":3022,"icon":62,"children":-1},"Upstream routing with round-robin, weighted, least-connections, fast-response, and sticky session algorithms.",{"title":36,"path":71,"stem":72,"description":3024,"icon":70,"children":-1},"AI tooling for Quayel Gateway: a portable skill package, an install guide, and a machine-oriented agent contract.",1791649767383]