[{"data":1,"prerenderedAt":698},["ShallowReactive",2],{"repo-tree":3,"repo-\u002Finfra\u002Fdns\u002Freadme":283},[4,7,10,13,16,19,22,25,28,31,34,37,40,43,46,49,52,55,58,61,64,67,69,72,75,78,81,84,86,88,90,93,96,99,102,105,108,111,114,117,120,123,125,127,129,131,133,135,138,141,143,146,149,152,155,158,161,164,167,169,172,175,178,180,183,186,189,192,195,198,201,203,206,209,212,215,218,221,224,227,230,233,236,239,242,245,248,251,254,257,260,263,266,269,272,275,278,281],{"path":5,"title":6},"\u002Fagents\u002Fbackend-code-style","Backend Conventions",{"path":8,"title":9},"\u002Fagents\u002Fdatabase","Database",{"path":11,"title":12},"\u002Fagents\u002Fportal-code-style","Portal Conventions",{"path":14,"title":15},"\u002Fagents\u002Ftranslation","Translation",{"path":17,"title":18},"\u002Fconventions\u002Fbackend-coding","Backend coding conventions",{"path":20,"title":21},"\u002Fconventions\u002Ffrontend-coding","Frontend coding conventions",{"path":23,"title":24},"\u002Fdevelopment-process","Development process",{"path":26,"title":27},"\u002Flearning-api-preview-hetzner-setup","Learning API Preview on Hetzner + Cloudflare",{"path":29,"title":30},"\u002Flearning-api-preview-vm-plan","Learning API Preview VM Plan",{"path":32,"title":33},"\u002Fmonorepo-structure","Monorepo structure",{"path":35,"title":36},"\u002Foperations","Operations — bugs and support",{"path":38,"title":39},"\u002Fpostmortems\u002F2026-03-16_onboarding-currency-regression","Onboarding Zod transform silently broken — web signups assigned wrong checkout currency",{"path":41,"title":42},"\u002Fpostmortems\u002Freadme","Postmortems",{"path":44,"title":45},"\u002Fpostmortems\u002F_template","TEMPLATE",{"path":47,"title":48},"\u002Fpostmortems\u002Fposthog-comparison","Postmortem practice — comparison with PostHog",{"path":50,"title":51},"\u002Fpreview-environment-plan","Preview Environment Plan",{"path":53,"title":54},"\u002Fprinciples","Engineering principles",{"path":56,"title":57},"\u002Fworking-with-ai","Working with AI",{"path":59,"title":60},"\u002F.claude\u002Fskills\u002Feval-playground\u002Fskill","Eval Playground — Co-development Skill",{"path":62,"title":63},"\u002F.claude\u002Fskills\u002Ffigma-diff-section\u002Fskill","Figma Diff Section Pipeline",{"path":65,"title":66},"\u002Fagents","AGENTS.md",{"path":68,"title":66},"\u002Fclaude",{"path":70,"title":71},"\u002Freadme","Studyflash",{"path":73,"title":74},"\u002Fapps\u002Fcore-api\u002Fagents","Core API (apps\u002Fcore-api)",{"path":76,"title":77},"\u002Fapps\u002Fcore-api\u002Freadme","README",{"path":79,"title":80},"\u002Fapps\u002Femail-previews\u002Fagents","Email Previews (apps\u002Femail-previews)",{"path":82,"title":83},"\u002Fapps\u002Flanding-page\u002Fagents","Landing Page (apps\u002Flanding-page)",{"path":85,"title":83},"\u002Fapps\u002Flanding-page\u002Fclaude",{"path":87,"title":66},"\u002Fapps\u002Flearning-api\u002Fagents",{"path":89,"title":77},"\u002Fapps\u002Flearning-api\u002Freadme",{"path":91,"title":92},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Feval_metrics_design","Surface-Specific Eval Metrics Design",{"path":94,"title":95},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Ftest_set","Quiz Eval Test Set",{"path":97,"title":98},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Ffrontend\u002Freadme","React + TypeScript + Vite",{"path":100,"title":101},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Fknown-issues\u002Fcontent-pillar-shallow-coverage\u002Freadme","Content pillar misses subtopics in dense documents",{"path":103,"title":104},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Fknown-issues\u002Fdocling-empty-section-headers\u002Freadme","Empty section headers dropped by docling chunker",{"path":106,"title":107},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Fknown-issues\u002Fdocling-table-reading-order\u002Freadme","Table\u002Fbox layout causes wrong reading order",{"path":109,"title":110},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Fmetrics\u002Freadme","Quiz eval metrics — canonical rubrics",{"path":112,"title":113},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Freports\u002F2026-04-12-quiz-summary-feedback-current-state","Quiz and Summary Feedback Current State",{"path":115,"title":116},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Freports\u002F2026-04-24-quiz-eval-metrics","Quiz Evaluation Metrics",{"path":118,"title":119},"\u002Fapps\u002Flearning-api\u002Fevals-playground\u002Freports\u002F2026-05-01-quiz-eval-current-state","Quiz Eval Current State",{"path":121,"title":122},"\u002Fapps\u002Flearning-api\u002Fmonitoring\u002Freadme","Monitoring Stack",{"path":124,"title":77},"\u002Fapps\u002Flearning-api\u002Fshared\u002Freadme",{"path":126,"title":77},"\u002Fapps\u002Flearning-api\u002Fworkers\u002Flearning_agents\u002Fflashcard_agent\u002Freadme",{"path":128,"title":77},"\u002Fapps\u002Flearning-api\u002Fworkers\u002Flearning_agents\u002Fingestion_agent\u002Freadme",{"path":130,"title":77},"\u002Fapps\u002Flearning-api\u002Fworkers\u002Flearning_agents\u002Fquiz_agent\u002Freadme",{"path":132,"title":77},"\u002Fapps\u002Flearning-api\u002Fworkers\u002Flearning_agents\u002Fsummary_agent\u002Freadme",{"path":134,"title":77},"\u002Fapps\u002Flearning-api\u002Fworkers\u002Fparser\u002Freadme",{"path":136,"title":137},"\u002Fapps\u002Fmarketing-emails-preview\u002Fagents","Marketing Emails Preview (apps\u002Fmarketing-emails-preview)",{"path":139,"title":140},"\u002Fapps\u002Fmobile-app\u002Fagents","StudyFlash Mobile App - Claude Code Configuration",{"path":142,"title":140},"\u002Fapps\u002Fmobile-app\u002Fclaude",{"path":144,"title":145},"\u002Fapps\u002Fmountain-max\u002Fagents","Mountain Max (apps\u002Fmountain-max)",{"path":147,"title":148},"\u002Fapps\u002Fmountain-max\u002Fgame\u002Freadme","Mountain Max Game",{"path":150,"title":151},"\u002Fapps\u002Fportal\u002Fagents","Portal (apps\u002Fportal)",{"path":153,"title":154},"\u002Fapps\u002Fportal\u002Freadme","Nuxt Minimal Starter",{"path":156,"title":157},"\u002Fapps\u002Fportal\u002Fapp\u002Fcomposables\u002Ffiles\u002Freadme","File Upload Composables",{"path":159,"title":160},"\u002Fapps\u002Fportal\u002Fdocs\u002Flibrary-routing","Library Routing Documentation",{"path":162,"title":163},"\u002Fapps\u002Fsupabase\u002Fagents","Supabase (apps\u002Fsupabase)",{"path":165,"title":166},"\u002Fapps\u002Fwrapped\u002Fagents","Wrapped (apps\u002Fwrapped)",{"path":168,"title":98},"\u002Fapps\u002Fwrapped\u002Freadme",{"path":170,"title":171},"\u002Finfra\u002Freadme","infra\u002F",{"path":173,"title":174},"\u002Finfra\u002Fdns\u002Freadme","DNS Infrastructure",{"path":176,"title":177},"\u002Finfra\u002Fdokploy\u002Freadme","studyflash-dokploy",{"path":179,"title":77},"\u002Finfra\u002Fdokploy\u002Fsdk\u002Fnodejs\u002Freadme",{"path":181,"title":182},"\u002Finfra\u002Finfisical\u002Freadme","Infisical Infrastructure",{"path":184,"title":185},"\u002Finfra\u002Flearning-api\u002Freadme","Pulumi GCP TypeScript Template",{"path":187,"title":188},"\u002Finfra\u002Fopenreplay\u002Freadme","OpenReplay on Hetzner",{"path":190,"title":191},"\u002Finfra\u002Fscripts\u002Freadme","infra\u002Fscripts\u002F",{"path":193,"title":194},"\u002Finfra\u002Fturborepo-cache\u002Freadme","Turborepo Remote Cache Infrastructure",{"path":196,"title":197},"\u002Finternal\u002Fchatwoot\u002Freadme","Chatwoot Infrastructure",{"path":199,"title":200},"\u002Finternal\u002Fchatwoot\u002Fprovider\u002Freadme","studyflash-chatwoot-provider",{"path":202,"title":77},"\u002Finternal\u002Fchatwoot\u002Fprovider\u002Fsdk\u002Fnodejs\u002Freadme",{"path":204,"title":205},"\u002Finternal\u002Fdocs\u002Freadme","internal\u002Fdocs",{"path":207,"title":208},"\u002Finternal\u002Fsupport-bot\u002Fclaude","Support Bot (Maximilian)",{"path":210,"title":211},"\u002Finternal\u002Fsupport-bot\u002Freadme","Studyflash Customer Support Bot (Maximilian)",{"path":213,"title":214},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Faccount_issues","Account Issues",{"path":216,"title":217},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fbilling_invoice","Billing Invoice",{"path":219,"title":220},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fcontent_upload","Content Upload",{"path":222,"title":223},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fdata_loss","Data Loss",{"path":225,"title":226},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fflashcard_issues","Flashcard Issues",{"path":228,"title":229},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fgarbage","Garbage",{"path":231,"title":232},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fgeneral_how_to","General How To",{"path":234,"title":235},"\u002Finternal\u002Fsupport-bot\u002Fkb","Knowledge Base Index",{"path":237,"title":238},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Flanguage_issues","Language Issues",{"path":240,"title":241},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fmindmap_issues","Mindmap Issues",{"path":243,"title":244},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fmisunderstanding","Misunderstanding",{"path":246,"title":247},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fmock_exam_issues","Mock Exam Issues",{"path":249,"title":250},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fpodcast_issues","Podcast Issues",{"path":252,"title":253},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fquiz_issues","Quiz Issues",{"path":255,"title":256},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Frefund_request","Refund Request",{"path":258,"title":259},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fsubscription_cancellation","Subscription Cancellation",{"path":261,"title":262},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fsubscription_info","Subscription Info",{"path":264,"title":265},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fsummary_issues","Summary Issues",{"path":267,"title":268},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Ftechnical_errors","Technical Errors",{"path":270,"title":271},"\u002Finternal\u002Fsupport-bot\u002Fkb\u002Fvideo_issues","Video Issues",{"path":273,"title":274},"\u002Fpackages\u002Fcommon\u002Fdocs\u002Fearly-access-features","Declarative Early Access Features",{"path":276,"title":277},"\u002Fpackages\u002Fcommon\u002Fscripts\u002Freadme","Common Package Scripts",{"path":279,"title":280},"\u002Fpackages\u002Fdevtools\u002Ffigma-plugins\u002Freadme","Figma plugins",{"path":282,"title":77},"\u002Fpackages\u002Fpulumi-infisical\u002Freadme",{"id":284,"title":174,"body":285,"description":690,"extension":691,"lastReviewed":692,"meta":693,"navigation":694,"owner":692,"path":173,"seo":695,"status":692,"stem":696,"tags":692,"__hash__":697},"repo\u002Finfra\u002Fdns\u002FREADME.md",{"type":286,"value":287,"toc":682},"minimark",[288,292,301,306,441,445,455,459,470,532,547,551,558,596,600,655,659,671,678],[289,290,174],"h1",{"id":291},"dns-infrastructure",[293,294,295,296,300],"p",{},"Pulumi project managing all ",[297,298,299],"code",{},"studyflash.*"," Cloudflare zones, DNS records, and redirect rulesets.",[302,303,305],"h2",{"id":304},"managed-domains","Managed domains",[307,308,309,322],"table",{},[310,311,312],"thead",{},[313,314,315,319],"tr",{},[316,317,318],"th",{},"Domain",[316,320,321],{},"Purpose",[323,324,325,336,349,359,372,383,394,405,417,429],"tbody",{},[313,326,327,333],{},[328,329,330],"td",{},[297,331,332],{},"studyflash.ai",[328,334,335],{},"Primary domain — landing page, app, API, email",[313,337,338,343],{},[328,339,340],{},[297,341,342],{},"studyflash.ch",[328,344,345,346],{},"Internal services, landing page, redirects to ",[297,347,348],{},".ai",[313,350,351,356],{},[328,352,353],{},[297,354,355],{},"studyflash.dev",[328,357,358],{},"Dev environments + internal tools — wildcard A + Zero Trust access",[313,360,361,366],{},[328,362,363],{},[297,364,365],{},"studyflash.at",[328,367,368,369],{},"Redirect → ",[297,370,371],{},"studyflash.ai\u002Fde",[313,373,374,379],{},[328,375,376],{},[297,377,378],{},"studyflash.com",[328,380,368,381],{},[297,382,332],{},[313,384,385,390],{},[328,386,387],{},[297,388,389],{},"studyflash.de",[328,391,368,392],{},[297,393,371],{},[313,395,396,401],{},[328,397,398],{},[297,399,400],{},"studyflash.eu",[328,402,368,403],{},[297,404,332],{},[313,406,407,412],{},[328,408,409],{},[297,410,411],{},"studyflash.fr",[328,413,368,414],{},[297,415,416],{},"studyflash.ai\u002Ffr",[313,418,419,424],{},[328,420,421],{},[297,422,423],{},"studyflash.it",[328,425,368,426],{},[297,427,428],{},"studyflash.ai\u002Fit",[313,430,431,436],{},[328,432,433],{},[297,434,435],{},"studyflash.nl",[328,437,368,438],{},[297,439,440],{},"studyflash.ai\u002Fnl",[302,442,444],{"id":443},"file-structure","File structure",[446,447,452],"pre",{"className":448,"code":450,"language":451},[449],"language-text","index.ts                    Engine — loops over zone configs, creates Pulumi resources\ntypes.ts                    Shared types (DnsRecord, Redirect, ZoneConfig)\nzones\u002F\n  studyflash.ai.ts          DNS records + geofence redirect ruleset\n  studyflash.ch.ts          DNS records + redirect ruleset\n  studyflash.dev.ts         Wildcard DNS + Zero Trust access app\n  redirects.ts              Simple redirect domains (at, com, de, eu, fr, it, nl)\n","text",[297,453,450],{"__ignoreMap":454},"",[302,456,458],{"id":457},"adding-a-dns-record","Adding a DNS record",[293,460,461,462,465,466,469],{},"Edit the zone file (e.g. ",[297,463,464],{},"zones\u002Fstudyflash.ai.ts",") and add an entry to the ",[297,467,468],{},"records"," array:",[446,471,475],{"className":472,"code":473,"language":474,"meta":454,"style":454},"language-ts shiki shiki-themes github-light github-dark","{ name: 'new-subdomain', type: 'CNAME', content: 'target.example.com', ttl: 3600 },\n","ts",[297,476,477],{"__ignoreMap":454},[478,479,482,486,490,493,497,500,503,505,508,510,513,515,518,520,523,525,529],"span",{"class":480,"line":481},"line",1,[478,483,485],{"class":484},"sVt8B","{ ",[478,487,489],{"class":488},"sScJk","name",[478,491,492],{"class":484},": ",[478,494,496],{"class":495},"sZZnC","'new-subdomain'",[478,498,499],{"class":484},", ",[478,501,502],{"class":488},"type",[478,504,492],{"class":484},[478,506,507],{"class":495},"'CNAME'",[478,509,499],{"class":484},[478,511,512],{"class":488},"content",[478,514,492],{"class":484},[478,516,517],{"class":495},"'target.example.com'",[478,519,499],{"class":484},[478,521,522],{"class":488},"ttl",[478,524,492],{"class":484},[478,526,528],{"class":527},"sj4cs","3600",[478,530,531],{"class":484}," },\n",[293,533,534,535,538,539,542,543,546],{},"Run ",[297,536,537],{},"pnpm preview"," to verify, then merge to ",[297,540,541],{},"main"," — CI runs ",[297,544,545],{},"pulumi up"," automatically.",[302,548,550],{"id":549},"adding-a-new-redirect-domain","Adding a new redirect domain",[293,552,553,554,557],{},"Add a row to ",[297,555,556],{},"zones\u002Fredirects.ts",":",[446,559,561],{"className":472,"code":560,"language":474,"meta":454,"style":454},"{ tld: 'es', zoneId: '\u003Ccloudflare-zone-id>', locale: 'es' },\n",[297,562,563],{"__ignoreMap":454},[478,564,565,567,570,572,575,577,580,582,585,587,590,592,594],{"class":480,"line":481},[478,566,485],{"class":484},[478,568,569],{"class":488},"tld",[478,571,492],{"class":484},[478,573,574],{"class":495},"'es'",[478,576,499],{"class":484},[478,578,579],{"class":488},"zoneId",[478,581,492],{"class":484},[478,583,584],{"class":495},"'\u003Ccloudflare-zone-id>'",[478,586,499],{"class":484},[478,588,589],{"class":488},"locale",[478,591,492],{"class":484},[478,593,574],{"class":495},[478,595,531],{"class":484},[302,597,599],{"id":598},"usage","Usage",[446,601,605],{"className":602,"code":603,"language":604,"meta":454,"style":454},"language-bash shiki shiki-themes github-light github-dark","pnpm preview      # diff against live state (via infisical)\npnpm run pulumi:up # apply changes\npnpm typecheck    # type-check\npnpm format       # apply prettier\n","bash",[297,606,607,619,633,644],{"__ignoreMap":454},[478,608,609,612,615],{"class":480,"line":481},[478,610,611],{"class":488},"pnpm",[478,613,614],{"class":495}," preview",[478,616,618],{"class":617},"sJ8bj","      # diff against live state (via infisical)\n",[478,620,622,624,627,630],{"class":480,"line":621},2,[478,623,611],{"class":488},[478,625,626],{"class":495}," run",[478,628,629],{"class":495}," pulumi:up",[478,631,632],{"class":617}," # apply changes\n",[478,634,636,638,641],{"class":480,"line":635},3,[478,637,611],{"class":488},[478,639,640],{"class":495}," typecheck",[478,642,643],{"class":617},"    # type-check\n",[478,645,647,649,652],{"class":480,"line":646},4,[478,648,611],{"class":488},[478,650,651],{"class":495}," format",[478,653,654],{"class":617},"       # apply prettier\n",[302,656,658],{"id":657},"prerequisites","Prerequisites",[660,661,662,666,669],"ul",{},[663,664,665],"li",{},"Pulumi CLI",[663,667,668],{},"Infisical CLI (authenticated)",[663,670,611],{},[293,672,673,674,677],{},"State is stored in an R2 bucket accessed via ",[297,675,676],{},"PULUMI_BACKEND_URL"," from Infisical.",[679,680,681],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":454,"searchDepth":621,"depth":621,"links":683},[684,685,686,687,688,689],{"id":304,"depth":621,"text":305},{"id":443,"depth":621,"text":444},{"id":457,"depth":621,"text":458},{"id":549,"depth":621,"text":550},{"id":598,"depth":621,"text":599},{"id":657,"depth":621,"text":658},"Pulumi project managing all studyflash.* Cloudflare zones, DNS records, and redirect rulesets.","md",null,{},true,{"title":174,"description":690},"infra\u002Fdns\u002FREADME","qXZZ-RLqldtj_PfXi-0GRDNQAZk-nCJFpEdhemZ3e34",1779007963923]