[{"data":1,"prerenderedAt":780},["ShallowReactive",2],{"blog-llm-endpoint-abuse-denial-of-wallet-hu":3},{"slug":4,"published":5,"minutes":6,"category":7,"tags":8,"keywords":14,"about":27,"sources":35,"cover":69,"og":70,"expertise":71,"locales":72,"lang":75,"title":76,"description":77,"coverAlt":78,"metaTitle":79,"takeaways":80,"faq":86,"toc":102,"blocks":138,"others":593},"llm-endpoint-abuse-denial-of-wallet","2026-10-16",14,"web",[9,10,11,12,13],"Nuxt","LLM security","Rate limiting","OWASP","Cost control",[15,16,17,18,19,20,21,22,23,24,25,26],"denial of wallet llm","owasp llm10 unbounded consumption","rate limit llm api nuxt","nuxt security rate limiter","protect openai api key frontend","llm token budget per user","limit llm cost per user","nitro api rate limiting","cloudflare turnstile nuxt","anthropic spend limit","openai hard spend limit","llm prompt size limit",[28,30,33],{"name":11,"url":29},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRate_limiting",{"name":31,"url":32},"Denial-of-service attack","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FDenial-of-service_attack",{"name":9,"url":34},"https:\u002F\u002Fnuxt.com\u002F",[36,39,42,45,48,51,54,57,60,63,66],{"title":37,"url":38},"OWASP GenAI Security Project: LLM10:2025 Unbounded Consumption","https:\u002F\u002Fgenai.owasp.org\u002Fllmrisk\u002Fllm102025-unbounded-consumption\u002F",{"title":40,"url":41},"Nuxt Security: Rate Limiter middleware","https:\u002F\u002Fnuxt-security.vercel.app\u002Fmiddleware\u002Frate-limiter",{"title":43,"url":44},"Nuxt Security: Request Size Limiter middleware","https:\u002F\u002Fnuxt-security.vercel.app\u002Fmiddleware\u002Frequest-size-limiter",{"title":46,"url":47},"nuxt-auth-utils: README (GitHub)","https:\u002F\u002Fgithub.com\u002Fatinux\u002Fnuxt-auth-utils",{"title":49,"url":50},"h3: request utilities (getRequestIP, readValidatedBody)","https:\u002F\u002Fh3.dev\u002Futils\u002Frequest",{"title":52,"url":53},"Upstash: Ratelimiting algorithms","https:\u002F\u002Fupstash.com\u002Fdocs\u002Fredis\u002Fsdks\u002Fratelimit-ts\u002Falgorithms",{"title":55,"url":56},"Cloudflare Turnstile: Validate the token","https:\u002F\u002Fdevelopers.cloudflare.com\u002Fturnstile\u002Fget-started\u002Fserver-side-validation\u002F",{"title":58,"url":59},"Claude Platform Docs: Rate limits and spend limits","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fapi\u002Frate-limits",{"title":61,"url":62},"Claude Platform Docs: Token counting","https:\u002F\u002Fplatform.claude.com\u002Fdocs\u002Fen\u002Fbuild-with-claude\u002Ftoken-counting",{"title":64,"url":65},"OpenAI API Docs: Spend limits","https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Fguides\u002Fspend-limits",{"title":67,"url":68},"OpenAI API Docs: Rate limits","https:\u002F\u002Fplatform.openai.com\u002Fdocs\u002Fguides\u002Frate-limits","\u002Fimages\u002Fblog\u002Fllm-endpoint-abuse-denial-of-wallet\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fllm-endpoint-abuse-denial-of-wallet\u002Fog.jpg","vue-nuxt-developer",[73,74,75],"en","de","hu","Denial of wallet: nyilvános LLM-végpontok védelme egy Nuxt-alkalmazásban","Visszaélés és elszabaduló költség megállítása egy nyilvános Nuxt LLM-útvonalon: auth, rate limit, promptkorlát, tokenkeret, Turnstile és költéskorlát.","Egy Nuxt chat-végpontra érkező kérések botszűrésen, sessionön, rate limiten, promptkorláton és tokenkereten mennek át, mielőtt elérik a modell API-t.","Denial of wallet: Nuxt LLM-végpontok védelme · Balázs Csorba",[81,82,83,84,85],"Egy nyilvános LLM-útvonal használat alapján fizetős API, mögötte a te bankkártyáddal; az OWASP ezt az LLM10:2025 Unbounded Consumption kockázatként tartja nyilván.","A modellhívás elé session kell, és soha ne legyen általános proxy: a szerver választja a modellt, a system promptot és a max_tokens értéket.","A kéréseket felhasználónként és IP-nként közös tárban korlátozd, a tokeneket pedig külön, mert egy kérés ezerszer annyiba kerülhet, mint egy másik.","A hívás előtt foglalj tokenkeretet, utána pedig számold el a tényleges felhasználással, atomi számlálóval.","A szolgáltatói költéskorlát az utolsó biztosíték, nem a vezérlés: egyszerre minden felhasználónak kikapcsolja a funkciót, és nem azonnal lép érvénybe.",[87,90,93,96,99],{"q":88,"a":89},"Mi az a denial of wallet?","A denial of wallet olyan támadás egy használat alapján fizetős szolgáltatás ellen, amelynek célja nem a leállás, hanem a költség. Az OWASP az LLM10:2025 Unbounded Consumption példájaként említi: a támadó annyi műveletet generál, hogy a szolgáltató számlája fenntarthatatlanná válik.",{"q":91,"a":92},"Elég a nuxt-security rate limitere egy LLM-végponthoz?","Jó első réteg, de a saját dokumentációja szerint a beépített rate limiter nem alkalmas összetett éles alkalmazásokhoz. Kéréseket számol, nem tokeneket, és az alapértelmezett LRU-cache szerverpéldányonként külön él. Egészítsd ki edge-oldali korláttal és felhasználónkénti tokenkerettel egy közös tárban, például Redisben.",{"q":94,"a":95},"IP vagy felhasználó szerint korlátozzak?","Mindkettő szerint. Az IP-korlát a bejelentkezés előtt is elérhető útvonalakat védi, és tompítja az egyszerű elárasztást. A költség szempontjából a felhasználónkénti korlát számít, mert sok jóhiszemű felhasználó osztozhat egy IP-n, egy támadó pedig sok IP-t váltogathat.",{"q":97,"a":98},"Hogyan korlátozzam a promptok hosszát?","Korlátozd a request body méretét, ellenőrizd az üzenet hosszát sémával, a beszélgetés előzményét a szerveren tárold ahelyett, hogy a klienstől fogadnád el, és a modellhívás előtt ellenőrizd a tokenszámot. A kimenetet a szerveren beállított max_tokens korlátozza.",{"q":100,"a":101},"Megvédenek a szolgáltatói költéskorlátok a visszaéléstől?","Csak végső esetben. Az OpenAI hard limitje és az Anthropic spend limitje elérésekor leállítja a szervezet, projekt vagy workspace teljes forgalmát, így a támadó mindenkinek megbénítja a funkciót. Az OpenAI azt is jelzi, hogy a betartatás nem azonnali, és a költés kissé túllépheti a korlátot.",[103,105,108,111,114,117,120,123,126,129,132,135],{"id":104,"title":88},"what-is-denial-of-wallet",{"id":106,"title":107},"abuse-patterns","Milyen visszaélési minták érik a nyilvános LLM-útvonalakat?",{"id":109,"title":110},"layered-defence","Hogyan néz ki a többrétegű védelem?",{"id":112,"title":113},"authentication-first","Miért a hitelesítés az első?",{"id":115,"title":116},"rate-limits","Hogyan korlátozz felhasználónként és IP-nként?",{"id":118,"title":119},"input-limits","Hogyan korlátozd a prompt méretét és a kimenetet?",{"id":121,"title":122},"token-budgets","Hogyan érvényesíts felhasználónkénti tokenkeretet?",{"id":124,"title":125},"bot-protection","Hová illik a CAPTCHA és a Turnstile?",{"id":127,"title":128},"provider-spend-limits","Mit fognak meg a szolgáltatói költéskorlátok?",{"id":130,"title":131},"monitoring","Mit figyelj és mire riassz?",{"id":133,"title":134},"checklist","Ellenőrzőlista egy nyilvános LLM-útvonalhoz",{"id":136,"title":137},"sources","Források",[139,143,146,149,157,160,161,164,209,212,213,216,225,226,238,251,252,268,287,289,300,330,333,334,345,352,361,363,364,370,376,391,393,409,410,421,423,430,431,434,487,490,491,494,518,525,526,550,557,558],{"type":140,"content":141},"paragraph",[142],"Egy nyilvános weboldal LLM-funkciója használat alapján fizetős API, mögötte a te fizetési adataiddal. Egy kapcsolatfelvételi asszisztens, egy termékkereső vagy egy „foglald össze az oldalt” gomb mind egy szerveroldali útvonalban végződik, amely szöveget továbbít egy modellnek, és tokenenként fizet. Ha ez az útvonal bárkinek, bármennyiszer, bármilyen bemenetre válaszol, nem az a kérdés, hogy valaki ingyenes modell-API-ként használja-e, hanem csak az, hogy mikor.",{"type":140,"content":144},[145],"Ez a cikk az LLM-funkciók Nuxtban való szállításának költség- és visszaélési oldala. Az OWASP LLM10:2025 Unbounded Consumption kockázatát egy Nuxt- és Nitro-alkalmazás konkrét rétegeire fordítja le: session, kérés- és tokenkorlát, promptméret-korlát, felhasználónkénti keret, Cloudflare Turnstile, valamint az OpenAI és az Anthropic költéskorlátai, átírható kóddal.",{"type":147,"level":148,"id":104,"text":88},"heading",2,{"type":140,"content":150},[151,152,156],"Az OWASP az ",{"tag":153,"href":38,"children":154},"a",[155],"Unbounded Consumption"," kockázatot úgy írja le, hogy egy LLM-alkalmazás „túlzott és ellenőrizetlen inferenciát” enged a felhasználóknak, ami szolgáltatásmegtagadáshoz, anyagi veszteséghez, modell-lopáshoz és romló szolgáltatáshoz vezet. A denial of wallet ennek költségközpontú változata: a támadó túlzott mennyiségű műveletet generál, hogy kihasználja a felhőalapú AI-szolgáltatások használat alapú díjazását, és fenntarthatatlan költséget okoz. Ehhez semminek sem kell leállnia; maga a számla a kár.",{"type":140,"content":158},[159],"Az LLM-útvonalak két okból sebezhetőbbek a hagyományos API-útvonalaknál. A kérésenkénti költség változó és nagy: egy hosszú beszélgetést küldő és hosszú választ kérő kérés nagyságrendekkel többe kerülhet, mint egy egysoros kérdés. A kimenet pedig közvetlenül hasznos a támadónak, így egy nyitott chat-végpontot akkor is megéri szkriptből hívni, ha semmi köze a termékedhez.",{"type":147,"level":148,"id":106,"text":107},{"type":140,"content":162},[163],"Az OWASP-bejegyzés hét példát sorol fel. Egy tipikus Nuxt-funkcióra vetítve ezek néhány mintára szűkülnek, mindegyikhez egy fő védekezéssel:",{"type":165,"head":166,"rows":173},"table",[167,169,171],[168],"Minta",[170],"Hogyan néz ki",[172],"Fő védekezés",[174,181,188,195,202],[175,177,179],[176],"Potyautas proxy (denial of wallet)",[178],"Szkriptek ingyenes modell-API-ként használják az útvonalat; a termékhez nem kapcsolódó promptok",[180],"Session, rögzített system prompt, felhasználónkénti tokenkeret",[182,184,186],[183],"Ismételt kérések",[185],"Magas kérésszám néhány fiókból vagy IP-ről",[187],"Rate limit az edge-en és felhasználónként",[189,191,193],[190],"Változó hosszúságú bemenetek áradata, folyamatos bemenet-túlcsordulás",[192],"Hatalmas body, bemásolt dokumentumok, egyre hosszabb előzmények",[194],"Body-méretkorlát, sémaellenőrzés, tokenszám-korlát",[196,198,200],[197],"Erőforrás-igényes kérések",[199],"Nagyon hosszú kimenet, eszközhurkok, nehéz reasoning",[201],"Szerveroldali max_tokens, lépéskorlát, időkorlát",[203,205,207],[204],"Modellkinyerés és funkcionális replikáció",[206],"Szisztematikus kérések, amelyek tanítóadatként gyűjtik a kimenetet",[208],"Mennyiségi korlát, logprobs nélkül, anomáliafigyelés",{"type":140,"content":210},[211],"Van egy hatodik minta, amelyet az OWASP nem nevez meg, de minden csapat találkozik vele: a saját magunk okozta fogyasztás. Egy korlát nélküli retry-hurok, egy újra és újra eszközöket hívó ágens vagy egy kliensoldali hiba, amely minden billentyűleütésnél a teljes előzményt elküldi, pontosan úgy égeti a keretet, mint egy támadó. Ugyanezek a korlátok ezt is elkapják.",{"type":147,"level":148,"id":109,"text":110},{"type":140,"content":214},[215],"Egyetlen védekezés sem elég, mert mindegyik mást mér. A rate limiter kéréseket számol, a méretkorlát bájtokat, a keret tokeneket, a költéskorlát pedig pénzt. A legolcsóbbtól a legdrágábban kiértékelhetőig rendezem őket, hogy az elutasított forgalom a lehető legkevesebbe kerüljön:",{"type":217,"attrs":218,"inner":222,"caption":223},"diagram",{"viewBox":219,"role":220,"aria-labelledby":221},"0 0 720 170","img","dow1-t dow1-d","\u003Ctitle id=\"dow1-t\">Rétegek egy LLM-hívás előtt\u003C\u002Ftitle>\u003Cdesc id=\"dow1-d\">A kérés átmegy az edge-en és a botszűrésen, majd a session-ellenőrzésen, a kéréskorláton, a méret- és tokenkorláton, a felhasználónkénti tokenkereten, és csak ezután éri el a modell API-t, ahol a szolgáltatói költéskorlát az utolsó biztosíték.\u003C\u002Fdesc>\u003Cdefs>\u003Cmarker id=\"dow1-ah\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\">\u003Cpath d=\"M0 0L10 5L0 10z\" class=\"d-head\" \u002F>\u003C\u002Fmarker>\u003C\u002Fdefs>\u003Crect x=\"20\" y=\"47\" width=\"95\" height=\"76\" rx=\"10\" class=\"d-box\" \u002F>\u003Ctext x=\"68\" y=\"81\" text-anchor=\"middle\" class=\"d-text\">Edge, botok\u003C\u002Ftext>\u003Ctext x=\"68\" y=\"103\" text-anchor=\"middle\" class=\"d-small\">WAF, Turnstile\u003C\u002Ftext>\u003Cpath d=\"M115 85H135\" class=\"d-line\" marker-end=\"url(#dow1-ah)\" \u002F>\u003Crect x=\"137\" y=\"47\" width=\"95\" height=\"76\" rx=\"10\" class=\"d-sky\" \u002F>\u003Ctext x=\"184\" y=\"81\" text-anchor=\"middle\" class=\"d-text\">Session\u003C\u002Ftext>\u003Ctext x=\"184\" y=\"103\" text-anchor=\"middle\" class=\"d-small\">401, ha nincs\u003C\u002Ftext>\u003Cpath d=\"M232 85H252\" class=\"d-line\" marker-end=\"url(#dow1-ah)\" \u002F>\u003Crect x=\"254\" y=\"47\" width=\"95\" height=\"76\" rx=\"10\" class=\"d-accent\" \u002F>\u003Ctext x=\"302\" y=\"81\" text-anchor=\"middle\" class=\"d-text\">Rate limit\u003C\u002Ftext>\u003Ctext x=\"302\" y=\"103\" text-anchor=\"middle\" class=\"d-small\">kérések\u003C\u002Ftext>\u003Cpath d=\"M349 85H369\" class=\"d-line\" marker-end=\"url(#dow1-ah)\" \u002F>\u003Crect x=\"371\" y=\"47\" width=\"95\" height=\"76\" rx=\"10\" class=\"d-mint\" \u002F>\u003Ctext x=\"418\" y=\"81\" text-anchor=\"middle\" class=\"d-text\">Bemenetkorlát\u003C\u002Ftext>\u003Ctext x=\"418\" y=\"103\" text-anchor=\"middle\" class=\"d-small\">bájt, token\u003C\u002Ftext>\u003Cpath d=\"M466 85H486\" class=\"d-line\" marker-end=\"url(#dow1-ah)\" \u002F>\u003Crect x=\"488\" y=\"47\" width=\"95\" height=\"76\" rx=\"10\" class=\"d-gold\" \u002F>\u003Ctext x=\"536\" y=\"81\" text-anchor=\"middle\" class=\"d-text\">Keret\u003C\u002Ftext>\u003Ctext x=\"536\" y=\"103\" text-anchor=\"middle\" class=\"d-small\">token naponta\u003C\u002Ftext>\u003Cpath d=\"M583 85H603\" class=\"d-line\" marker-end=\"url(#dow1-ah)\" \u002F>\u003Crect x=\"605\" y=\"47\" width=\"95\" height=\"76\" rx=\"10\" class=\"d-box\" \u002F>\u003Ctext x=\"652\" y=\"81\" text-anchor=\"middle\" class=\"d-text\">Modell API\u003C\u002Ftext>\u003Ctext x=\"652\" y=\"103\" text-anchor=\"middle\" class=\"d-small\">költéskorlát\u003C\u002Ftext>",[224],"Az olcsó ellenőrzések előre: az edge-en elutasított kérés semmibe sem kerül, a költéskorlát által megállított viszont már leállította a funkciót.",{"type":147,"level":148,"id":112,"text":113},{"type":140,"content":227},[228,229,232,233,237],"Minden más korlátnak szüksége van egy identitásra, amelyhez kötődhet. Session nélkül az egyetlen kulcs az IP-cím, az pedig gyenge. A ",{"tag":153,"href":47,"children":230},[231],"nuxt-auth-utils"," esetén a ",{"tag":234,"children":235},"code",[236],"requireUserSession(event)"," visszaadja a sessiont, vagy 401-gyel válaszol, így a handler első sora eldönti, megtörténhet-e egyáltalán a modellhívás. Ha egy funkciónak bejelentkezés előtt is működnie kell, például egy termékkeresőnek, botellenőrzés után adj ki könnyű, anonim sessiont, és adj neki sokkal kisebb keretet, mint egy bejelentkezett vásárlónak.",{"type":140,"content":239},[240,241,244,245,250],"A második szabály: soha ne szállíts általános proxyt. Ha a kliens választhatja meg a modellt, a system promptot, a ",{"tag":234,"children":242},[243],"max_tokens"," értéket vagy az eszközöket, a végpontod egy kerülőúton elérhető nyitott modell-API. A kliens üzenetet küld, legfeljebb egy beszélgetés-azonosítót; minden mást a szerver dönt el. Így az API-kulcs is a szerveren marad, ahogy az ",{"tag":246,"to":247,"children":248},"link","\u002Fblog\u002Fnuxt-llm-features-ai-sdk-streaming",[249],"LLM-funkciók szállítása Nuxtban"," cikkben leírtam.",{"type":147,"level":148,"id":115,"text":116},{"type":140,"content":253},[254,255,258,259,262,263,267],"A ",{"tag":153,"href":41,"children":256},[257],"nuxt-security"," modul olyan rate limitert ad, amely alapból globálisan be van kapcsolva: IP-nként 150 kérés 300 000 ms alatt, LRU-cache-ben tárolva, túllépéskor 429 Too Many Requests válasszal. Útvonalanként a ",{"tag":234,"children":260},[261],"routeRules"," segítségével hangolható, tárolóként pedig bármely ",{"tag":153,"href":264,"children":265},"https:\u002F\u002Funstorage.unjs.io\u002F",[266],"unstorage","-driver használható. A saját dokumentációja őszinte a korlátokról: a beépített rate limiter „nem alkalmas összetett éles alkalmazásokhoz”, és infrastruktúra-szintű megoldásokat ajánl.",{"type":140,"content":269},[270,271,274,275,278,279,282,283,286],"Egy LLM-útvonalnál ez három módosítást jelent. Használj közös tárat, mert a memóriabeli LRU-cache példányonként külön él, és minden deploynál nullázódik. A modellútvonalon sokkal szigorúbb korlátot állíts be, mint az oldal többi részén. És határozd meg helyesen a kliens IP-jét: a h3 ",{"tag":234,"children":272},[273],"getRequestIP"," függvénye csak kifejezett bekapcsolás után olvassa az ",{"tag":234,"children":276},[277],"X-Forwarded-For"," fejlécet, mert annak bal szélső eleme kliensbemenet; Cloudflare mögött a nuxt-security ",{"tag":234,"children":280},[281],"ipHeader"," opciója a ",{"tag":234,"children":284},[285],"cf-connecting-ip"," fejlécre is mutathat.",{"type":234,"code":288},"\u002F\u002F nuxt.config.ts (illustrative)\nexport default defineNuxtConfig({\n  modules: ['nuxt-security', 'nuxt-auth-utils'],\n  security: {\n    rateLimiter: {\n      driver: { name: 'redis', options: { url: process.env.REDIS_URL } },\n      ipHeader: 'cf-connecting-ip', \u002F\u002F only if Cloudflare sits in front\n    },\n  },\n  routeRules: {\n    '\u002Fapi\u002Fchat': {\n      security: {\n        rateLimiter: { tokensPerInterval: 20, interval: 60_000 },\n        requestSizeLimiter: { maxRequestSizeInBytes: 32_000 },\n      },\n    },\n  },\n})",{"type":140,"content":290},[291,292,295,296,299],"Ez a limiter IP-alapú. A felhasználónkénti korlát a handlerbe való, a ",{"tag":234,"children":293},[294],"requireUserSession"," után, a felhasználói azonosítóval mint kulccsal. Hogy ott melyik algoritmus fut, kevésbé fontos, mint hogy következetesen használj egyet; a kompromisszumok az ",{"tag":153,"href":53,"children":297},[298],"Upstash dokumentációja"," szerint:",{"type":165,"head":301,"rows":308},[302,304,306],[303],"Algoritmus",[305],"Erősség",[307],"Gyengeség",[309,316,323],[310,312,314],[311],"Fix ablak",[313],"Tárolásban és számításban nagyon olcsó",[315],"Az ablakhatárokon átszivárognak a csúcsok; új ablak kezdetén roham",[317,319,321],[318],"Csúszó ablak",[320],"Megoldja a határproblémát",[322],"Több tárolás és számítás; közelítés",[324,326,328],[325],"Token bucket",[327],"Kisimítja a csúcsokat; nagyobb kezdeti csúcsot enged",[329],"Több állapotot kell átlátni",{"type":140,"content":331},[332],"Az Anthropic saját API-ja token bucketet használ, amely folyamatosan töltődik vissza a korlátig ahelyett, hogy fix időközönként nullázódna; ésszerű minta egy chat-funkcióhoz, ahol a felhasználók hullámokban gépelnek.",{"type":147,"level":148,"id":118,"text":119},{"type":140,"content":335},[336,337,340,341,344],"A kéréskorlát semmit sem mond arról, mennyibe kerül egy-egy kérés. A bemenetet három lépésben korlátozd. Először a bájtokat: a nuxt-security request size limitere a ",{"tag":234,"children":338},[339],"maxRequestSizeInBytes"," felett 413 Payload Too Large választ ad, az alapérték 2 000 000; egy chatüzenethez néhány kilobájt elég. Aztán a formát: a bodyt a ",{"tag":234,"children":342},[343],"readValidatedBody"," és egy, az üzenethosszt korlátozó séma segítségével ellenőrizd. Végül a tokeneket: hívás előtt számolj.",{"type":140,"content":346},[347,348,351],"A legnagyobb rés általában a beszélgetés előzménye. Ha a kliens minden körben a teljes beszélgetést elküldi, a támadó tetszőleges hosszúságú, kitalált előzményt küldhet. Az előzményt a szerveren tárold, a beszélgetés-azonosítóhoz kötve, és vágd le rögzített számú körre vagy tokenre. Az Anthropic ",{"tag":153,"href":62,"children":349},[350],"tokenszámláló végpontja"," ingyenes, saját, az üzenetkészítéstől független rate limitje van, és becslést ad a bemeneti tokenekre, ami egy korláthoz elég.",{"type":140,"content":353},[354,355,357,358,360],"A kimenetet a szerveren beállított ",{"tag":234,"children":356},[243]," korlátozza. Fontos, mit csinál és mit nem: az Anthropic rate limitjeinél a kimeneti tokeneket keletkezésükkor számolják, és a ",{"tag":234,"children":359},[243]," nem számít bele a percenkénti kimeneti tokenek számításába. Kérésenkénti költségplafon, nem sebességszabályozás, ezért a következő szakasz kerete továbbra is kell.",{"type":234,"code":362},"\u002F\u002F server\u002Fapi\u002Fchat.post.ts (illustrative)\nimport { z } from 'zod'\n\nconst Body = z.object({\n  conversationId: z.string().uuid(),\n  message: z.string().min(1).max(4000),\n})\nconst MAX_INPUT = 12_000   \u002F\u002F tokens, after history trimming\nconst MAX_OUTPUT = 800     \u002F\u002F the server decides, never the client\n\nexport default defineEventHandler(async (event) => {\n  const { user } = await requireUserSession(event)          \u002F\u002F 401 without a session\n  const body = await readValidatedBody(event, Body.parse)    \u002F\u002F 400 on bad or long input\n\n  const history = await loadHistory(user.id, body.conversationId, { maxTurns: 10 })\n  const messages = [...history, { role: 'user', content: body.message }]\n\n  const { input_tokens } = await anthropic.messages.countTokens({ model: MODEL, system: SYSTEM, messages })\n  if (input_tokens > MAX_INPUT) throw createError({ statusCode: 413, statusMessage: 'Conversation too long' })\n\n  const reserved = input_tokens + MAX_OUTPUT\n  const key = await reserveTokens(user.id, reserved)          \u002F\u002F null when the daily budget is spent\n  if (!key) throw createError({ statusCode: 429, statusMessage: 'Daily limit reached' })\n\n  let used = 0\n  try {\n    const res = await anthropic.messages.create({ model: MODEL, system: SYSTEM, messages, max_tokens: MAX_OUTPUT })\n    const u = res.usage\n    used = u.input_tokens + (u.cache_creation_input_tokens ?? 0) + (u.cache_read_input_tokens ?? 0) + u.output_tokens\n    await saveTurn(user.id, body.conversationId, body.message, res.content)\n    return res.content\n  } finally {\n    await settleTokens(key, reserved, used)                   \u002F\u002F refund the unused part\n  }\n})",{"type":147,"level":148,"id":121,"text":122},{"type":140,"content":365},[366,367,369],"A keret az a védekezés, amely ténylegesen korlátozza a költséget: token felhasználónként naponta, vagy anonim sessiononként óránként. Az általam használt minta: foglalás, majd elszámolás. Hívás előtt foglald le a legrosszabb esetet, vagyis a megszámolt bemenetet plusz a ",{"tag":234,"children":368},[243]," értéket. Ha a foglalás túllépné a keretet, utasítsd el 429-cel. Hívás után számolj el a válaszban szereplő tényleges felhasználással, és írd vissza a különbözetet. Így ugyanannak a felhasználónak a párhuzamos kérései sem tudnak túlkölteni, mert mindegyik már lefoglalta a részét.",{"type":217,"attrs":371,"inner":373,"caption":374},{"viewBox":219,"role":220,"aria-labelledby":372},"dow2-t dow2-d","\u003Ctitle id=\"dow2-t\">Tokenkeret foglalása és elszámolása\u003C\u002Ftitle>\u003Cdesc id=\"dow2-d\">A handler megszámolja a bemeneti tokeneket, atomi növeléssel lefoglalja a bemenet plusz max_tokens mennyiséget a napi keretből, túllépésnél elutasít, meghívja a modellt, majd visszaírja a foglalás és a tényleges felhasználás különbözetét.\u003C\u002Fdesc>\u003Cdefs>\u003Cmarker id=\"dow2-ah\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\">\u003Cpath d=\"M0 0L10 5L0 10z\" class=\"d-head\" \u002F>\u003C\u002Fmarker>\u003C\u002Fdefs>\u003Crect x=\"20\" y=\"47\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-box\" \u002F>\u003Ctext x=\"79\" y=\"81\" text-anchor=\"middle\" class=\"d-text\">Számolás\u003C\u002Ftext>\u003Ctext x=\"79\" y=\"103\" text-anchor=\"middle\" class=\"d-small\">bemeneti token\u003C\u002Ftext>\u003Cpath d=\"M138 85H158\" class=\"d-line\" marker-end=\"url(#dow2-ah)\" \u002F>\u003Crect x=\"160\" y=\"47\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-accent\" \u002F>\u003Ctext x=\"220\" y=\"81\" text-anchor=\"middle\" class=\"d-text\">Foglalás\u003C\u002Ftext>\u003Ctext x=\"220\" y=\"103\" text-anchor=\"middle\" class=\"d-small\">+ max_tokens\u003C\u002Ftext>\u003Cpath d=\"M279 85H299\" class=\"d-line\" marker-end=\"url(#dow2-ah)\" \u002F>\u003Crect x=\"301\" y=\"47\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-gold\" \u002F>\u003Ctext x=\"360\" y=\"81\" text-anchor=\"middle\" class=\"d-text\">Kereten túl?\u003C\u002Ftext>\u003Ctext x=\"360\" y=\"103\" text-anchor=\"middle\" class=\"d-small\">429, nincs hívás\u003C\u002Ftext>\u003Cpath d=\"M419 85H439\" class=\"d-line\" marker-end=\"url(#dow2-ah)\" \u002F>\u003Crect x=\"441\" y=\"47\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-sky\" \u002F>\u003Ctext x=\"500\" y=\"81\" text-anchor=\"middle\" class=\"d-text\">Modellhívás\u003C\u002Ftext>\u003Ctext x=\"500\" y=\"103\" text-anchor=\"middle\" class=\"d-small\">usage a válaszban\u003C\u002Ftext>\u003Cpath d=\"M560 85H580\" class=\"d-line\" marker-end=\"url(#dow2-ah)\" \u002F>\u003Crect x=\"582\" y=\"47\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-mint\" \u002F>\u003Ctext x=\"641\" y=\"81\" text-anchor=\"middle\" class=\"d-text\">Elszámolás\u003C\u002Ftext>\u003Ctext x=\"641\" y=\"103\" text-anchor=\"middle\" class=\"d-small\">maradék vissza\u003C\u002Ftext>",[375],"A foglalás párhuzamos kéréseknél is biztonságossá teszi a keretet; az elszámolás pontosan tartja.",{"type":140,"content":377},[378,379,382,383,386,387,390],"A számlálónak atominak kell lennie. Egy ",{"tag":234,"children":380},[381],"getItem",", majd ",{"tag":234,"children":384},[385],"setItem"," a Nitro-tárban olvasás-módosítás-írás, és két párhuzamos kérés is kiolvashatja a régi értéket. A Redis ",{"tag":234,"children":388},[389],"INCRBY"," egy lépésben adja vissza az új összeget, így az ellenőrzés és a foglalás nem kerülhet versenyhelyzetbe:",{"type":234,"code":392},"\u002F\u002F server\u002Futils\u002Fbudget.ts (illustrative, ioredis)\nconst DAILY_TOKENS = 200_000\n\nexport async function reserveTokens(userId: string, n: number) {\n  const key = `llm:budget:${userId}:${new Date().toISOString().slice(0, 10)}`\n  const total = await redis.incrby(key, n)\n  if (total === n) await redis.expire(key, 60 * 60 * 48)\n  if (total > DAILY_TOKENS) { await redis.decrby(key, n); return null }\n  return key\n}\n\nexport async function settleTokens(key: string, reserved: number, used: number) {\n  if (reserved > used) await redis.decrby(key, reserved - used)\n}",{"type":140,"content":394},[395,396,399,400,403,404,408],"Elszámoláskor minden bemeneti tokent számolj. Prompt caching mellett az Anthropic ",{"tag":234,"children":397},[398],"input_tokens"," mezője csak az utolsó cache-töréspont utáni tokeneket tartalmazza; a teljes bemenet ",{"tag":234,"children":401},[402],"cache_read_input_tokens + cache_creation_input_tokens + input_tokens",". Hogy a cache-elt tokeneket alacsonyabb árukon súlyozod-e, termékdöntés; egy egyszerű tokenkeretet könnyebb elmagyarázni a felhasználóknak. A cache-elés és a routing költségoldaláról a ",{"tag":246,"to":405,"children":406},"\u002Fblog\u002Fllm-cost-latency-prompt-caching-routing",[407],"prompt caching és model routing"," cikkben írtam.",{"type":147,"level":148,"id":124,"text":125},{"type":140,"content":411},[412,413,416,417,420],"A botellenőrzés megdrágítja az automatizálást, de nem korlátozza a költséget. Ott használom, ahol identitás keletkezik, például egy anonim chat-session kiadása előtt, nem minden üzenetnél. A ",{"tag":153,"href":56,"children":414},[415],"Cloudflare Turnstile"," esetén a Siteverify API-n keresztüli szerveroldali ellenőrzés kötelező, mert a kliensoldali widget önmagában semmit sem véd: a token hamisítható, minden token 300 másodpercig érvényes, és csak egyszer ellenőrizhető, az újrafelhasználást ",{"tag":234,"children":418},[419],"timeout-or-duplicate"," hibával utasítja el.",{"type":234,"code":422},"\u002F\u002F server\u002Fapi\u002Fsession\u002Fanonymous.post.ts (illustrative)\nexport default defineEventHandler(async (event) => {\n  const { token } = await readBody\u003C{ token: string }>(event)\n  const r = await $fetch\u003C{ success: boolean }>('https:\u002F\u002Fchallenges.cloudflare.com\u002Fturnstile\u002Fv0\u002Fsiteverify', {\n    method: 'POST',\n    body: { secret: useRuntimeConfig().turnstileSecret, response: token, remoteip: getRequestIP(event) },\n  })\n  if (!r.success) throw createError({ statusCode: 403 })\n  await setUserSession(event, { user: { id: crypto.randomUUID(), tier: 'anonymous' } })\n  return { ok: true }\n})",{"type":424,"variant":425,"title":426,"body":427},"callout","warn","A botellenőrzés nem keret",[428],[429],"Embereket is meg lehet fizetni a feladványok megoldására, és egy érvényes session egész nap küldhet drága kéréseket. A Turnstile fekvőrendőr a session létrehozása előtt, a korlátozást a sessiononkénti tokenkeret végzi.",{"type":147,"level":148,"id":127,"text":128},{"type":140,"content":432},[433],"Mindkét nagy szolgáltató lehetővé teszi a havi költés korlátozását, és érdemes azon az összegen alul beállítani, ami már fájna. De ismerd a viselkedését: ezek a korlátok a hatókörükben minden forgalmat leállítanak, minden felhasználónak, amíg a hónap le nem jár, vagy meg nem emeled őket.",{"type":165,"head":435,"rows":442},[436,438,440],[437],"Szempont",[439],"Claude API (Anthropic)",[441],"OpenAI API",[443,450,469,480],[444,446,448],[445],"Saját korlát",[447],"Szervezeti spend limit a tier-plafon alatt; saját költés- és rate limit workspace-enként",[449],"Hard spend limit szervezetenként vagy projektenként; csak értesítő spend alertek",[451,453,459],[452],"Válasz eléréskor",[454,455,458],"HTTP 400 ",{"tag":234,"children":456},[457],"invalid_request_error",", „You have reached your specified API usage limits”",[460,461,464,465,468],"HTTP 429 ",{"tag":234,"children":462},[463],"organization_spend_limit_exceeded"," vagy ",{"tag":234,"children":466},[467],"project_spend_limit_exceeded"," kóddal",[470,472,478],[471],"Beépített plafon",[473,474,477],"Havi tier-plafon: Start 500, Build 1000, Scale 200 000 USD; 429 ",{"tag":234,"children":475},[476],"enforced_spend_limit_reached",", retry-after nélkül",[479],"Havi használati korlát usage tierenként",[481,483,485],[482],"Megkötés",[484],"Az alapértelmezett workspace-re nem állítható workspace-korlát",[486],"„Enforcement is not instantaneous”; a rögzített költés kissé túllépheti a korlátot",{"type":140,"content":488},[489],"Két gyakorlati következmény. Minden nyilvános funkció kapjon saját Anthropic-workspace-t vagy OpenAI-projektet saját kulccsal és korláttal, hogy egy elszabadult chatbot ne állíthassa le a belső eszközeidet. És kezeld kifejezetten a költéskorlát-válaszokat: az SDK-k automatikusan újrapróbálják a 429-es hibákat, az Anthropic szerint pedig az újrapróbálás a hozzáférés visszaállásáig sikertelen. Fordítsd le barátságos „átmenetileg nem elérhető” állapotra, és riassz valakit.",{"type":147,"level":148,"id":130,"text":131},{"type":140,"content":492},[493],"Az OWASP ajánlásai között szerepel az átfogó naplózás anomáliafelismeréssel és a fokozatos leépülés (graceful degradation). Egy Nuxt-alkalmazásban ez funkciónként néhány mérőszámot jelent, a felhasználói és a beszélgetés-azonosítóval naplózva:",{"type":495,"items":496},"list",[497,503,508,513],[498,502],{"tag":499,"children":500},"strong",[501],"Token felhasználónként naponta",", riasztással, ha a legnagyobb felhasználó messze a medián felett van. A potyautasok itt bukkannak fel először.",[504,507],{"tag":499,"children":505},[506],"Elutasítások rétegenként",": 401, 413 és 429 óránként. Ha hirtelen megnő a keret miatti 429-ek száma, valaki tapogatózik.",[509,512],{"tag":499,"children":510},[511],"Bemeneti token kérésenként",", eloszlásként, nem átlagként. A hosszú farok azt jelzi, hogy túl lazák az előzmény- vagy méretkorlátok.",[514,517],{"tag":499,"children":515},[516],"Napi költség funkciónként egy alapvonalhoz mérve",", a szolgáltatónál beállított spend alerttel jóval a hard limit alatt.",{"type":140,"content":519},[520,521,408],"Előbb fokozd le, mint hogy leállítanád. Ha a költés átlép egy küszöböt, először az anonim felhasználókat kapcsold ki, aztán a bejelentkezetteket tedd át kisebb modellre, a fizető ügyfeleknek pedig tartsd életben a funkciót. Ez sokkal jobb hibamód, mint amikor a szolgáltatói korlát egyszerre mindent lekapcsol. A számok mögötti hívások nyomkövetéséről az ",{"tag":246,"to":522,"children":523},"\u002Fblog\u002Fagent-observability-opentelemetry",[524],"LLM-ágensek observabilityje",{"type":147,"level":148,"id":133,"text":134},{"type":495,"ordered":527,"items":528},true,[529,531,533,538,540,542,544,546,548],[530],"Az API-kulcs csak a szerver runtime configjában van; a kliens soha nem látja.",[532],"Az útvonal sessiont követel; anonim session csak szerveroldalon ellenőrzött botszűrés után jár.",[534,535,537],"A modell, a system prompt, az eszközök és a ",{"tag":234,"children":536},[243]," a szerveren rögzítettek.",[539],"Kéréskorlát IP-nként az edge-en és felhasználónként a handlerben, közös tárban.",[541],"Body-méretkorlát, sémaellenőrzés, szerveroldali előzmény és tokenszám-korlát a hívás előtt.",[543],"Napi tokenkeret felhasználónként, atomi foglalással és elszámolással.",[545],"Korlátos újrapróbálás, eszközlépés és időkorlát, hogy a saját kódod ne kerülhessen hurokba.",[547],"Funkciónként külön szolgáltatói workspace vagy projekt, spend alerttel és hard limittel.",[549],"Riasztás felhasználónkénti tokenkiugrásokra, elutasítási csúcsokra és napi költségre, plusz leépülési terv.",{"type":140,"content":551},[552,553,408],"A költséggel való visszaélés a kockázat egyik fele; a másik az, hogy mit kezd a modell az ellenséges bemenettel. Erről a ",{"tag":246,"to":554,"children":555},"\u002Fblog\u002Fprompt-injection-lethal-trifecta-patterns",[556],"prompt injection elleni védekezés",{"type":147,"level":148,"id":136,"text":137},{"type":495,"ordered":527,"items":559},[560,563,566,569,572,575,578,581,584,587,590],[561],{"tag":153,"href":38,"children":562},[37],[564],{"tag":153,"href":41,"children":565},[40],[567],{"tag":153,"href":44,"children":568},[43],[570],{"tag":153,"href":47,"children":571},[46],[573],{"tag":153,"href":50,"children":574},[49],[576],{"tag":153,"href":53,"children":577},[52],[579],{"tag":153,"href":56,"children":580},[55],[582],{"tag":153,"href":59,"children":583},[58],[585],{"tag":153,"href":62,"children":586},[61],[588],{"tag":153,"href":65,"children":589},[64],[591],{"tag":153,"href":68,"children":592},[67],[594,643,695,734],{"slug":595,"published":5,"minutes":596,"category":7,"tags":597,"keywords":602,"about":613,"sources":621,"cover":637,"og":638,"expertise":71,"locales":639,"lang":75,"title":640,"description":641,"coverAlt":642},"nuxt-i18n-multilingual-seo-hreflang",8,[9,598,599,600,601],"i18n","SEO","hreflang","Multilingual",[603,604,605,606,607,608,609,610,611,612],"nuxt i18n seo","nuxt hreflang","useLocaleHead","nuxt i18n canonical","hreflang x-default","prefix_except_default","multilingual seo nuxt","nuxt i18n sitemap hreflang","hreflang self-referencing","nuxt i18n baseUrl",[614,615,618],{"name":9,"url":34},{"name":616,"url":617},"Hreflang","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHreflang",{"name":619,"url":620},"Internationalization and localization","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FInternationalization_and_localization",[622,625,628,631,634],{"title":623,"url":624},"Nuxt i18n docs: SEO (useLocaleHead)","https:\u002F\u002Fi18n.nuxtjs.org\u002Fdocs\u002Fguide\u002Fseo",{"title":626,"url":627},"Nuxt i18n docs: Routing strategies","https:\u002F\u002Fi18n.nuxtjs.org\u002Fdocs\u002Fguide",{"title":629,"url":630},"Google Search Central: Tell Google about localized versions of your page","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fspecialty\u002Finternational\u002Flocalized-versions",{"title":632,"url":633},"Google Search Central: Managing multi-regional and multilingual sites","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fspecialty\u002Finternational\u002Fmanaging-multi-regional-sites",{"title":635,"url":636},"Google Search Central: How to specify a canonical URL","https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fcrawling-indexing\u002Fconsolidate-duplicate-urls","\u002Fimages\u002Fblog\u002Fnuxt-i18n-multilingual-seo-hreflang\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fnuxt-i18n-multilingual-seo-hreflang\u002Fog.jpg",[73,74,75],"Többnyelvű SEO Nuxt i18n-nel: URL-stratégia, hreflang, canonical és sitemap","Így állítsd be a Nuxt i18n modult, hogy a keresők minden nyelvet indexeljenek: útvonal-stratégia, useLocaleHead, hreflang, részleges fordítás, sitemap.","Egy oldal három nyelvi változata, angol, német és magyar, hreflang nyilakkal összekötve és egy x-default tartalékkal.",{"slug":644,"published":645,"minutes":646,"category":7,"tags":647,"keywords":651,"about":659,"sources":666,"cover":688,"og":689,"expertise":690,"locales":691,"lang":75,"title":692,"description":693,"coverAlt":694},"llms-txt-vs-markdown-content-negotiation","2026-10-07",9,[648,649,650,9],"llms.txt","Content negotiation","AI agents",[648,652,653,654,655,656,657,658],"markdown for agents","content negotiation","AI crawlers","generative engine optimization","Accept text\u002Fmarkdown header","llms-full.txt","agent-ready website",[660,663],{"name":661,"url":662},"Markdown","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMarkdown",{"name":664,"url":665},"Hypertext Transfer Protocol","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHypertext_Transfer_Protocol",[667,670,673,676,679,682,685],{"title":668,"url":669},"llmstxt.org: The \u002Fllms.txt file (proposal, version 2)","https:\u002F\u002Fllmstxt.org\u002F",{"title":671,"url":672},"llmstxt.org: Changes from v1 to v2","https:\u002F\u002Fllmstxt.org\u002Fchanges.html",{"title":674,"url":675},"Cloudflare changelog: Markdown for Agents (12 February 2026)","https:\u002F\u002Fdevelopers.cloudflare.com\u002Fchangelog\u002Fpost\u002F2026-02-12-markdown-for-agents\u002F",{"title":677,"url":678},"Cloudflare docs: Markdown for Agents","https:\u002F\u002Fdevelopers.cloudflare.com\u002Ffundamentals\u002Freference\u002Fmarkdown-for-agents\u002F",{"title":680,"url":681},"Ahrefs: 137K sites analyzed, 97% of llms.txt files never get read (June 2026)","https:\u002F\u002Fahrefs.com\u002Fblog\u002Fllmstxt-study\u002F",{"title":683,"url":684},"Checkly: The current state of content negotiation for AI agents (February 2026)","https:\u002F\u002Fwww.checklyhq.com\u002Fblog\u002Fstate-of-ai-agent-content-negotation\u002F",{"title":686,"url":687},"Suganthan: tracking Cloudflare Markdown for Agents on one site","https:\u002F\u002Fsuganthan.com\u002Fblog\u002Fcloudflare-markdown-for-agents\u002F","\u002Fimages\u002Fblog\u002Fllms-txt-vs-markdown-content-negotiation\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fllms-txt-vs-markdown-content-negotiation\u002Fog.jpg","ai-engineer",[73,74,75],"llms.txt vagy Markdown content negotiation: mit töltenek le valójában az ügynökök","Az llms.txt javaslat, a Markdown content negotiation fejléc. Mit töltenek le a MI-ügynökök, mit mutatnak a naplók, és hogyan működik mindez Nuxttal és nginxszel.","Négy rétegből álló halom, amely a MI-ügynököket szolgálja ki: deklarált WebMCP eszközök, minden oldal Markdown-másolata, tárgyalt Markdown válasz, és egy llms.txt index",{"slug":696,"published":697,"minutes":596,"category":7,"tags":698,"keywords":704,"about":712,"sources":721,"cover":728,"og":729,"expertise":71,"locales":730,"lang":75,"title":731,"description":732,"coverAlt":733},"multiplayer-sailing-game-threejs","2026-10-05",[699,700,701,702,703],"three.js","WebGL","GLSL","WebSockets","Game development",[705,706,707,708,709,710,711,701],"three.js game","three.js multiplayer","Gerstner waves","WebGL ocean shader","day night cycle three.js","WebSocket relay","browser game development",[713,716,718],{"name":714,"url":715},"Three.js","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FThree.js",{"name":700,"url":717},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWebGL",{"name":719,"url":720},"WebSocket","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWebSocket",[722,725],{"title":723,"url":724},"GPU Gems, Chapter 1: Effective Water Simulation from Physical Models","https:\u002F\u002Fdeveloper.nvidia.com\u002Fgpugems\u002Fgpugems\u002Fpart-i-natural-effects\u002Fchapter-1-effective-water-simulation-physical-models",{"title":726,"url":727},"RFC 6455: The WebSocket Protocol","https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc6455","\u002Fimages\u002Fblog\u002Fmultiplayer-sailing-game-threejs\u002Fdusk.webp","\u002Fimages\u002Fblog\u002Fmultiplayer-sailing-game-threejs\u002Fog.jpg",[73,74,75],"Többjátékos 3D vitorlázós játék tiszta three.js-szel","Gerstner-hullámok, amelyeket a GPU és a CPU ugyanúgy számol, egyetlen égbolt-függvény az égre, a vízre és a ködre, ötperces nap-éj ciklus és egy apró WebSocket-relé – a portfólióm játéka mögötti technika.","Sárkányorrú dzsunka világító lampionokkal vitorlázik egy hullámzó öbölben alkonyatkor, két oldalon erdős dombok.",{"slug":735,"published":736,"minutes":646,"category":7,"tags":737,"keywords":743,"about":752,"sources":761,"cover":774,"og":775,"expertise":71,"locales":776,"lang":75,"title":777,"description":778,"coverAlt":779},"home-assistant-ev-charging-energy-manager","2026-09-16",[738,739,740,741,742],"Home Assistant","EPEX Spot","Energy prices","Tesla","Energy management",[744,745,746,747,748,749,750,751],"EPEX Austria prices","spot tariff Austria EV charging","Home Assistant EV charging","cheapest hours charging","day-ahead price Austria","negative electricity prices Austria","Tesla charging spot prices","load management 3 phase",[753,756,758],{"name":754,"url":755},"EPEX SPOT","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEPEX_SPOT",{"name":738,"url":757},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHome_Assistant",{"name":759,"url":760},"Load management","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLoad_management",[762,765,768,771],{"title":763,"url":764},"aWATTar Austria: market data API","https:\u002F\u002Fwww.awattar.at\u002Fservices\u002Fapi",{"title":766,"url":767},"EPEX SPOT: market results","https:\u002F\u002Fwww.epexspot.com\u002Fen\u002Fmarket-results",{"title":769,"url":770},"Home Assistant developer docs: apps","https:\u002F\u002Fdevelopers.home-assistant.io\u002Fdocs\u002Fapps\u002F",{"title":772,"url":773},"Home Assistant: Tesla Fleet integration","https:\u002F\u002Fwww.home-assistant.io\u002Fintegrations\u002Ftesla_fleet\u002F","\u002Fimages\u002Fblog\u002Fhome-assistant-ev-charging-energy-manager\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fhome-assistant-ev-charging-energy-manager\u002Fog.jpg",[73,74,75],"Töltés EPEX ausztriai árakon: mennyit spórol a Home Assistant appom","Egy év órás EPEX ár Ausztriára, újrajátszva egy Teslára és egy bojlerre: a legolcsóbb órákban 6,5 ct\u002FkWh a töltés 18,9 helyett, évi közel 590 €, a 20 A-es biztosíték kiégetése nélkül.","Borítókép: három oszlop hasonlítja össze a 18,9, 13,1 és 6,5 ct\u002FkWh-t 18:00-kori, éjszakai és legolcsóbb órás töltésnél.",1792177150826]