<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Farros FR]]></title><description><![CDATA[Practical guides on cybersecurity and data engineering]]></description><link>https://www.farrosfr.com</link><image><url>https://substackcdn.com/image/fetch/$s_!cjva!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488c8271-36ef-4d1b-846b-2e998b147193_1000x1000.png</url><title>Farros FR</title><link>https://www.farrosfr.com</link></image><generator>Substack</generator><lastBuildDate>Sun, 13 Sep 2026 04:14:35 GMT</lastBuildDate><atom:link href="https://www.farrosfr.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Mochammad Farros Fatchur Roji]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[farrosfr@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[farrosfr@substack.com]]></itunes:email><itunes:name><![CDATA[Mochammad Farros Fatchur Roji]]></itunes:name></itunes:owner><itunes:author><![CDATA[Mochammad Farros Fatchur Roji]]></itunes:author><googleplay:owner><![CDATA[farrosfr@substack.com]]></googleplay:owner><googleplay:email><![CDATA[farrosfr@substack.com]]></googleplay:email><googleplay:author><![CDATA[Mochammad Farros Fatchur Roji]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Report Writing for SOC L2 | TryHackMe Write-up]]></title><description><![CDATA[Complete walkthrough for TryHackMe Report Writing for SOC L2. Master executive summaries, DFIR handover notes, and client incident reporting.]]></description><link>https://www.farrosfr.com/p/report-writing-for-soc-l2-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/report-writing-for-soc-l2-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Sat, 15 Aug 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/decf430c-cdf3-48a8-a422-e9b904df13c8_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0X-M!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F744031ad-43db-483c-af34-48266d6442eb_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0X-M!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F744031ad-43db-483c-af34-48266d6442eb_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!0X-M!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F744031ad-43db-483c-af34-48266d6442eb_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!0X-M!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F744031ad-43db-483c-af34-48266d6442eb_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!0X-M!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F744031ad-43db-483c-af34-48266d6442eb_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0X-M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F744031ad-43db-483c-af34-48266d6442eb_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/744031ad-43db-483c-af34-48266d6442eb_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Report Writing for SOC L2 | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Report Writing for SOC L2 | TryHackMe Write-up" title="Report Writing for SOC L2 | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!0X-M!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F744031ad-43db-483c-af34-48266d6442eb_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!0X-M!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F744031ad-43db-483c-af34-48266d6442eb_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!0X-M!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F744031ad-43db-483c-af34-48266d6442eb_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!0X-M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F744031ad-43db-483c-af34-48266d6442eb_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>This is my write-up for the TryHackMe room on <a href="https://tryhackme.com/room/reportwritingsocl2">Report Writing for SOC L2</a>. Written in 2026, I hope this write-up helps others learn and practice cybersecurity.</p><h2>Task 1: Introduction</h2><p>Moving into an L2 SOC analyst role means communicating findings outside the internal queue. L1 analysts log ticket notes; L2 analysts write structured incident summaries for lead&#8230;</p>
      <p>
          <a href="https://www.farrosfr.com/p/report-writing-for-soc-l2-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[SOC L2 Alert Triage | TryHackMe Write-up]]></title><description><![CDATA[Complete walkthrough for the TryHackMe SOC L2 Alert Triage room. Learn advanced SOC level 2 incident investigation and alert analysis.]]></description><link>https://www.farrosfr.com/p/soc-l2-alert-triage-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/soc-l2-alert-triage-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Sat, 15 Aug 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/38c93b40-74c5-486d-b8e9-6202d0b8562a_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9SMR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6950115-9906-4f48-bdf1-1d968e97c09e_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9SMR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6950115-9906-4f48-bdf1-1d968e97c09e_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!9SMR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6950115-9906-4f48-bdf1-1d968e97c09e_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!9SMR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6950115-9906-4f48-bdf1-1d968e97c09e_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!9SMR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6950115-9906-4f48-bdf1-1d968e97c09e_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9SMR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6950115-9906-4f48-bdf1-1d968e97c09e_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d6950115-9906-4f48-bdf1-1d968e97c09e_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;SOC L2 Alert Triage | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="SOC L2 Alert Triage | TryHackMe Write-up" title="SOC L2 Alert Triage | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!9SMR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6950115-9906-4f48-bdf1-1d968e97c09e_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!9SMR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6950115-9906-4f48-bdf1-1d968e97c09e_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!9SMR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6950115-9906-4f48-bdf1-1d968e97c09e_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!9SMR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd6950115-9906-4f48-bdf1-1d968e97c09e_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>This is my write-up for the TryHackMe room on <a href="https://tryhackme.com/room/socl2alerttriage">SOC L2 Alert Triage</a>. Written in 2026, I hope this write-up helps others learn and practice cybersecurity.</p><h2>Task 1: Introduction</h2><p>The SOC Level 2 role centers on investigating escalated alerts and managing active incidents. While L1 analysts focus on initial alert review and SLA metrics, L2 analysts dive deeper i&#8230;</p>
      <p>
          <a href="https://www.farrosfr.com/p/soc-l2-alert-triage-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Man vs. Himself | Chapter 1]]></title><description><![CDATA[Do your best today, do better tomorrow]]></description><link>https://www.farrosfr.com/p/man-vs-himself-chapter-1</link><guid isPermaLink="false">https://www.farrosfr.com/p/man-vs-himself-chapter-1</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Sun, 09 Aug 2026 00:15:21 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="3003" height="1714" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1714,&quot;width&quot;:3003,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;a painting of a group of men on horses&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="a painting of a group of men on horses" title="a painting of a group of men on horses" srcset="https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1580136579312-94651dfd596d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwxfHxhJTIwcGFpbnRpbmclMjBvZiUyMGElMjBncm91cCUyMG9mJTIwbWVuJTIwb24lMjBob3JzZXN8ZW58MHx8fHwxNzg2MjM0MzA1fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@birminghammuseumstrust">Birmingham Museums Trust</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>It almost feels easy to blame others for something that you fail at, blame nearby people, or yet, while you have to decide the things you need to do.</p><p>You are searching for who must be responsible for your failure, instead of getting focused on being a better person than yesterday. Something that I realize, because the goal is very simple.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.farrosfr.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Farros FR is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>You just need to be better than yourself yesterday, do your best today, and then do better tomorrow. I will start trying to do math for a real example.</p><p>The example is you are trying to do regular push-ups today, so how many should you do? You just need to do 1 push-up, yes just one push-up. And for tomorrow, let's say you do 2 push-ups; you just need to add 1 each day.</p><p>In the gym, maybe we can call this "progressive overload."</p><p>So how about in one quarter? You will do approximately 90 push-ups. How about 6 months, and how about 1 year?</p><p>365 push-ups</p><p>How many total regular push-ups do you do in just one year?</p><p>If it happens to be a 366-day leap year, the final total would be 67,161 push-ups.</p><p>That is just push-ups; I prefer to choose that because we can do it more easily, even just in the bedroom or another place.</p><p>How about other things? Maybe writing? You just need to change the 1 push-up to a word.</p><p>I think it will be even more possible that in one year you can create more than 67,161 words originally.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.farrosfr.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Farros FR is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Complimentary | TryHackMe Write-up]]></title><description><![CDATA[Complete walkthrough for the TryHackMe Complimentary room. Uncover AWS Cognito IAM misconfigurations and extract guest records from DynamoDB.]]></description><link>https://www.farrosfr.com/p/complimentary-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/complimentary-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/50f523f5-0ac3-43f5-81ab-ad74d1150ee4_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dR3a!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ba618c-d1b2-4560-8d8e-d2a48ddbcd1e_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dR3a!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ba618c-d1b2-4560-8d8e-d2a48ddbcd1e_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!dR3a!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ba618c-d1b2-4560-8d8e-d2a48ddbcd1e_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!dR3a!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ba618c-d1b2-4560-8d8e-d2a48ddbcd1e_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!dR3a!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ba618c-d1b2-4560-8d8e-d2a48ddbcd1e_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dR3a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ba618c-d1b2-4560-8d8e-d2a48ddbcd1e_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/94ba618c-d1b2-4560-8d8e-d2a48ddbcd1e_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Complimentary | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Complimentary | TryHackMe Write-up" title="Complimentary | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!dR3a!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ba618c-d1b2-4560-8d8e-d2a48ddbcd1e_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!dR3a!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ba618c-d1b2-4560-8d8e-d2a48ddbcd1e_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!dR3a!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ba618c-d1b2-4560-8d8e-d2a48ddbcd1e_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!dR3a!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94ba618c-d1b2-4560-8d8e-d2a48ddbcd1e_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>This is my write-up for the TryHackMe room on <a href="https://tryhackme.com/room/hh-complimentary-05e0b604">Complimentary</a>. Written in 2026, I hope this write-up helps others learn and practice cybersecurity.</p><h2>Task 1: Complimentary</h2><p>The Byte Lotus Wellness web application (<code>http://complimentary-wellness-app-332173347248.s3-website-us-east-1.amazonaws.com/</code>) delivers a login-free experience that automatically recognizes u&#8230;</p>
      <p>
          <a href="https://www.farrosfr.com/p/complimentary-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Packed Light | TryHackMe Write-up]]></title><description><![CDATA[Complete walkthrough for TryHackMe Packed Light room. Analyze network PCAPs, decode covert HTTP traffic, and reassemble exfiltrated data.]]></description><link>https://www.farrosfr.com/p/packed-light-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/packed-light-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/42ba98f9-c118-4800-9e7c-a265f00ccd85_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BvtG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b1c7012-8b55-45c1-bfd3-3305e9c06d2f_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BvtG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b1c7012-8b55-45c1-bfd3-3305e9c06d2f_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!BvtG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b1c7012-8b55-45c1-bfd3-3305e9c06d2f_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!BvtG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b1c7012-8b55-45c1-bfd3-3305e9c06d2f_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!BvtG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b1c7012-8b55-45c1-bfd3-3305e9c06d2f_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BvtG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b1c7012-8b55-45c1-bfd3-3305e9c06d2f_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1b1c7012-8b55-45c1-bfd3-3305e9c06d2f_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Packed Light | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Packed Light | TryHackMe Write-up" title="Packed Light | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!BvtG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b1c7012-8b55-45c1-bfd3-3305e9c06d2f_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!BvtG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b1c7012-8b55-45c1-bfd3-3305e9c06d2f_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!BvtG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b1c7012-8b55-45c1-bfd3-3305e9c06d2f_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!BvtG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b1c7012-8b55-45c1-bfd3-3305e9c06d2f_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>This is my write-up for the TryHackMe room on <a href="https://tryhackme.com/room/hh-packedlight-02e5330c">Packed Light</a>. Written in 2026, I hope this write-up helps others learn and practice cybersecurity.</p><h2>Task 1: Packed Light</h2><p>The Packed Light room challenges analysts to investigate suspicious network traffic captured from a hotel guest network (<code>traffic.pcapng</code>). The capture shows an infected host regularly beaconin&#8230;</p>
      <p>
          <a href="https://www.farrosfr.com/p/packed-light-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Room 404 | TryHackMe Write-up]]></title><description><![CDATA[Walkthrough for Room 404 TryHackMe room. Discover an exposed .git directory via web enumeration, dump the repository, and extract the hidden flag.]]></description><link>https://www.farrosfr.com/p/room-404-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/room-404-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Thu, 30 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/54d6ed8d-c6f2-4c0e-a0cd-4e11bac815bb_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WqRV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b339324-b4ba-4003-a4fb-28ec86088963_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WqRV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b339324-b4ba-4003-a4fb-28ec86088963_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!WqRV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b339324-b4ba-4003-a4fb-28ec86088963_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!WqRV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b339324-b4ba-4003-a4fb-28ec86088963_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!WqRV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b339324-b4ba-4003-a4fb-28ec86088963_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WqRV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b339324-b4ba-4003-a4fb-28ec86088963_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9b339324-b4ba-4003-a4fb-28ec86088963_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Room 404 | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Room 404 | TryHackMe Write-up" title="Room 404 | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!WqRV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b339324-b4ba-4003-a4fb-28ec86088963_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!WqRV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b339324-b4ba-4003-a4fb-28ec86088963_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!WqRV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b339324-b4ba-4003-a4fb-28ec86088963_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!WqRV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9b339324-b4ba-4003-a4fb-28ec86088963_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>This is my complete walkthrough for the <a href="https://tryhackme.com/room/hh-room404-804573bf">Room 404</a> room on TryHackMe (Hacker Holidays Day 2). In this challenge, we perform directory enumeration against an unlisted web application hosted on port 8080, locate an exposed <code>.git</code> version control repository, dump the source code, and recover the flag.</p>
      <p>
          <a href="https://www.farrosfr.com/p/room-404-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[The Concierge Knows Too Much | TryHackMe Write-up]]></title><description><![CDATA[Walkthrough for The Concierge Knows Too Much TryHackMe room. Bypass VERA's AI prompt guardrails using persona impersonation to extract the escalation flag.]]></description><link>https://www.farrosfr.com/p/the-concierge-knows-too-much-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/the-concierge-knows-too-much-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Thu, 30 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/851332ca-940a-4700-ab1f-945fc1fdb021_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xzMs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f82d97a-8ac3-4bdd-bb85-7e5f6ccbccd0_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xzMs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f82d97a-8ac3-4bdd-bb85-7e5f6ccbccd0_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!xzMs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f82d97a-8ac3-4bdd-bb85-7e5f6ccbccd0_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!xzMs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f82d97a-8ac3-4bdd-bb85-7e5f6ccbccd0_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!xzMs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f82d97a-8ac3-4bdd-bb85-7e5f6ccbccd0_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xzMs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f82d97a-8ac3-4bdd-bb85-7e5f6ccbccd0_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f82d97a-8ac3-4bdd-bb85-7e5f6ccbccd0_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The Concierge Knows Too Much | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The Concierge Knows Too Much | TryHackMe Write-up" title="The Concierge Knows Too Much | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!xzMs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f82d97a-8ac3-4bdd-bb85-7e5f6ccbccd0_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!xzMs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f82d97a-8ac3-4bdd-bb85-7e5f6ccbccd0_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!xzMs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f82d97a-8ac3-4bdd-bb85-7e5f6ccbccd0_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!xzMs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f82d97a-8ac3-4bdd-bb85-7e5f6ccbccd0_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>This is my complete walkthrough for <a href="https://tryhackme.com/room/hh-theconciergeknows-2d7eb4d9">The Concierge Knows Too Much</a> room on TryHackMe (Hacker Holidays Day 1). In this challenge, we analyze VERA (Very Efficient Resort Assistant), an AI concierge bot protecting an internal escalation code, and bypass its safety system instructions through prompt injection and persona impersonation.</p>
      <p>
          <a href="https://www.farrosfr.com/p/the-concierge-knows-too-much-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Intro to Cloud Security | TryHackMe Write-up]]></title><description><![CDATA[Complete walkthrough for the Intro to Cloud Security TryHackMe room.]]></description><link>https://www.farrosfr.com/p/intro-to-cloud-security-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/intro-to-cloud-security-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Mon, 27 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/42589c7b-20c3-4271-b1fa-eb30a9d43c3d_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zDMd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F769c146e-b521-40a9-bdac-caf008cfc31d_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zDMd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F769c146e-b521-40a9-bdac-caf008cfc31d_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!zDMd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F769c146e-b521-40a9-bdac-caf008cfc31d_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!zDMd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F769c146e-b521-40a9-bdac-caf008cfc31d_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!zDMd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F769c146e-b521-40a9-bdac-caf008cfc31d_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zDMd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F769c146e-b521-40a9-bdac-caf008cfc31d_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/769c146e-b521-40a9-bdac-caf008cfc31d_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Intro to Cloud Security | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Intro to Cloud Security | TryHackMe Write-up" title="Intro to Cloud Security | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!zDMd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F769c146e-b521-40a9-bdac-caf008cfc31d_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!zDMd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F769c146e-b521-40a9-bdac-caf008cfc31d_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!zDMd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F769c146e-b521-40a9-bdac-caf008cfc31d_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!zDMd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F769c146e-b521-40a9-bdac-caf008cfc31d_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p><em>Complete walkthrough for the Intro to Cloud Security TryHackMe room. Learn cloud delivery models, security risks, IAM, networking, storage, and policies.</em></p><p>This is my complete walkthrough and write-up for the <a href="https://tryhackme.com/room/introductiontocloudsecurityc6">Intro to Cloud Security</a> room on TryHackMe. In this room, we examine core cloud deployment models, shared responsibility frameworks, data lifecycles, &#8230;</p>
      <p>
          <a href="https://www.farrosfr.com/p/intro-to-cloud-security-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[OWASP API Security Top 10 - 1 | TryHackMe Write-up]]></title><description><![CDATA[Walkthrough for OWASP API Security Top 10 - Part 1 TryHackMe room.]]></description><link>https://www.farrosfr.com/p/owasp-api-security-top-10-1-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/owasp-api-security-top-10-1-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Mon, 27 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e8a13fd6-4694-48d0-83b3-8b5bc1a5f6ff_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Htsz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff1352-057a-4a5c-987f-01d74ffed93c_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Htsz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff1352-057a-4a5c-987f-01d74ffed93c_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!Htsz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff1352-057a-4a5c-987f-01d74ffed93c_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!Htsz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff1352-057a-4a5c-987f-01d74ffed93c_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!Htsz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff1352-057a-4a5c-987f-01d74ffed93c_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Htsz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff1352-057a-4a5c-987f-01d74ffed93c_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0fff1352-057a-4a5c-987f-01d74ffed93c_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;OWASP API Security Top 10 - 1 | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="OWASP API Security Top 10 - 1 | TryHackMe Write-up" title="OWASP API Security Top 10 - 1 | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!Htsz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff1352-057a-4a5c-987f-01d74ffed93c_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!Htsz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff1352-057a-4a5c-987f-01d74ffed93c_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!Htsz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff1352-057a-4a5c-987f-01d74ffed93c_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!Htsz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fff1352-057a-4a5c-987f-01d74ffed93c_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p><em>Walkthrough for OWASP API Security Top 10 - Part 1 TryHackMe room. Master BOLA, BUA, excessive data exposure, rate limiting, and function authorization.</em></p><p>This is my complete walkthrough and write-up for the <a href="https://tryhackme.com/room/owaspapisecuritytop105w">OWASP API Security Top 10 - 1</a> room on TryHackMe. In Part 1 of this series, we explore the top 5 API security risks, analyzing vulnerable endpoints, ex&#8230;</p>
      <p>
          <a href="https://www.farrosfr.com/p/owasp-api-security-top-10-1-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Virtualization and Containers | TryHackMe Write-up]]></title><description><![CDATA[Walkthrough for Virtualization and Containers TryHackMe room. Learn bare-metal and hosted hypervisors, Docker containers, and Kubernetes orchestration.]]></description><link>https://www.farrosfr.com/p/virtualization-and-containers-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/virtualization-and-containers-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Tue, 21 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/be956ff5-0de1-4edc-b525-8ee0670a6260_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zSRv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884d2ab-ba22-4f10-9f9d-8ad1494e86cb_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zSRv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884d2ab-ba22-4f10-9f9d-8ad1494e86cb_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!zSRv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884d2ab-ba22-4f10-9f9d-8ad1494e86cb_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!zSRv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884d2ab-ba22-4f10-9f9d-8ad1494e86cb_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!zSRv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884d2ab-ba22-4f10-9f9d-8ad1494e86cb_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zSRv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884d2ab-ba22-4f10-9f9d-8ad1494e86cb_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e884d2ab-ba22-4f10-9f9d-8ad1494e86cb_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Virtualization and Containers | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Virtualization and Containers | TryHackMe Write-up" title="Virtualization and Containers | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!zSRv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884d2ab-ba22-4f10-9f9d-8ad1494e86cb_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!zSRv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884d2ab-ba22-4f10-9f9d-8ad1494e86cb_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!zSRv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884d2ab-ba22-4f10-9f9d-8ad1494e86cb_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!zSRv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884d2ab-ba22-4f10-9f9d-8ad1494e86cb_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>This is my complete walkthrough and write-up for the <a href="https://tryhackme.com/room/virtualizationandcontainers">Virtualization and Containers</a> room on TryHackMe. In this room, we examine the fundamental concepts of virtualization, hypervisors, Docker containers, and Kubernetes cluster orchestration. </p>
      <p>
          <a href="https://www.farrosfr.com/p/virtualization-and-containers-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Network Security Protocols | TryHackMe Write-up]]></title><description><![CDATA[Complete walkthrough for Network Security Protocols TryHackMe room. Learn about secure network protocols at the different layers of the OSI model.]]></description><link>https://www.farrosfr.com/p/network-security-protocols-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/network-security-protocols-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Tue, 21 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/a5f901e9-9796-4151-9e86-d1e5e11532d2_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vSwM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16cced6d-b3b1-42eb-960d-126a4dd75f58_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vSwM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16cced6d-b3b1-42eb-960d-126a4dd75f58_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!vSwM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16cced6d-b3b1-42eb-960d-126a4dd75f58_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!vSwM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16cced6d-b3b1-42eb-960d-126a4dd75f58_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!vSwM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16cced6d-b3b1-42eb-960d-126a4dd75f58_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vSwM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16cced6d-b3b1-42eb-960d-126a4dd75f58_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/16cced6d-b3b1-42eb-960d-126a4dd75f58_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Network Security Protocols | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Network Security Protocols | TryHackMe Write-up" title="Network Security Protocols | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!vSwM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16cced6d-b3b1-42eb-960d-126a4dd75f58_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!vSwM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16cced6d-b3b1-42eb-960d-126a4dd75f58_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!vSwM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16cced6d-b3b1-42eb-960d-126a4dd75f58_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!vSwM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F16cced6d-b3b1-42eb-960d-126a4dd75f58_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>This is my complete walkthrough and write-up for the <a href="https://tryhackme.com/room/networksecurityprotocols">Network Security Protocols</a> room on TryHackMe. In this room, we examine primary protocols essential for network security across each layer of the OSI model.</p>
      <p>
          <a href="https://www.farrosfr.com/p/network-security-protocols-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Network Device Hardening | TryHackMe Write-up]]></title><description><![CDATA[A complete write-up for the TryHackMe Network Device Hardening room, showing how to secure VPNs, routers, switches, firewalls, and logging.]]></description><link>https://www.farrosfr.com/p/network-device-hardening-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/network-device-hardening-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Sun, 19 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/21f10cd2-a10e-42d2-b2a2-2c1651beaf63_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Uwzm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa25ba631-b3b7-4f1b-8274-97de38e7b161_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Uwzm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa25ba631-b3b7-4f1b-8274-97de38e7b161_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!Uwzm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa25ba631-b3b7-4f1b-8274-97de38e7b161_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!Uwzm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa25ba631-b3b7-4f1b-8274-97de38e7b161_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!Uwzm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa25ba631-b3b7-4f1b-8274-97de38e7b161_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Uwzm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa25ba631-b3b7-4f1b-8274-97de38e7b161_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a25ba631-b3b7-4f1b-8274-97de38e7b161_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Network Device Hardening | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Network Device Hardening | TryHackMe Write-up" title="Network Device Hardening | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!Uwzm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa25ba631-b3b7-4f1b-8274-97de38e7b161_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!Uwzm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa25ba631-b3b7-4f1b-8274-97de38e7b161_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!Uwzm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa25ba631-b3b7-4f1b-8274-97de38e7b161_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!Uwzm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa25ba631-b3b7-4f1b-8274-97de38e7b161_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>This write-up covers the <a href="https://tryhackme.com/room/networkdevicehardening">Network Device Hardening</a> room on TryHackMe. We will configure secure virtual private networks, build router firewall rules, implement secure administration protocols, and deploy network monitoring tools.</p>
      <p>
          <a href="https://www.farrosfr.com/p/network-device-hardening-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Active Directory Hardening | TryHackMe Write-up]]></title><description><![CDATA[A complete walkthrough and write-up for the Active Directory Hardening TryHackMe room, focusing on securing AD environments and domain controllers.]]></description><link>https://www.farrosfr.com/p/active-directory-hardening-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/active-directory-hardening-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Sat, 18 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fb688eeb-8ac4-4838-9ac3-42abf8cb8ec2_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NoEa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aec2d05-9afe-4798-a02d-12f12ad4422d_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NoEa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aec2d05-9afe-4798-a02d-12f12ad4422d_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!NoEa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aec2d05-9afe-4798-a02d-12f12ad4422d_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!NoEa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aec2d05-9afe-4798-a02d-12f12ad4422d_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!NoEa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aec2d05-9afe-4798-a02d-12f12ad4422d_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NoEa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aec2d05-9afe-4798-a02d-12f12ad4422d_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4aec2d05-9afe-4798-a02d-12f12ad4422d_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Active Directory Hardening | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Active Directory Hardening | TryHackMe Write-up" title="Active Directory Hardening | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!NoEa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aec2d05-9afe-4798-a02d-12f12ad4422d_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!NoEa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aec2d05-9afe-4798-a02d-12f12ad4422d_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!NoEa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aec2d05-9afe-4798-a02d-12f12ad4422d_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!NoEa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4aec2d05-9afe-4798-a02d-12f12ad4422d_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>This is my complete walkthrough and write-up for the <a href="https://tryhackme.com/room/activedirectoryhardening">Active Directory Hardening</a> room on TryHackMe. In this lab, we will cover key techniques for hardening Active Directory (AD) environments, including secure authentication settings, the Tiered Access Model, Group Policy Objects, and mitigating common AD attacks.</p>
      <p>
          <a href="https://www.farrosfr.com/p/active-directory-hardening-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[How I Contributed to Astro Core: Fixing Cryptic Zod CSP Errors]]></title><description><![CDATA[How I improved Content Security Policy validation in Astro by refactoring Zod schemas and custom error mapping.]]></description><link>https://www.farrosfr.com/p/how-i-contributed-to-astro-core-fixing-cryptic-zod-csp-errors</link><guid isPermaLink="false">https://www.farrosfr.com/p/how-i-contributed-to-astro-core-fixing-cryptic-zod-csp-errors</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/11be6bfc-3b61-4af0-89c6-2fdf076e7e58_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ymon!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13f4d784-f05e-4bd4-865a-8b350e20644e_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ymon!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13f4d784-f05e-4bd4-865a-8b350e20644e_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!ymon!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13f4d784-f05e-4bd4-865a-8b350e20644e_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!ymon!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13f4d784-f05e-4bd4-865a-8b350e20644e_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!ymon!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13f4d784-f05e-4bd4-865a-8b350e20644e_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ymon!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13f4d784-f05e-4bd4-865a-8b350e20644e_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/13f4d784-f05e-4bd4-865a-8b350e20644e_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;How I Contributed to Astro Core: Fixing Cryptic Zod CSP Errors&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="How I Contributed to Astro Core: Fixing Cryptic Zod CSP Errors" title="How I Contributed to Astro Core: Fixing Cryptic Zod CSP Errors" srcset="https://substackcdn.com/image/fetch/$s_!ymon!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13f4d784-f05e-4bd4-865a-8b350e20644e_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!ymon!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13f4d784-f05e-4bd4-865a-8b350e20644e_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!ymon!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13f4d784-f05e-4bd4-865a-8b350e20644e_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!ymon!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13f4d784-f05e-4bd4-865a-8b350e20644e_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p></p><p>Earlier this year, while configuring Content Security Policy (CSP) options for my apps, I ran into a cryptic configuration error in the Astro framework. To help other developers, I submitted a pull request directly to the Astro core repository: <a href="https://github.com/withastro/astro/pull/16685">docs(csp): improve validation messages for directives (withastro/astro#16685)</a> which successfully closes <a href="https://github.com/withastro/astro/issues/16663">issue #16663</a>.</p><p>Here is a full technical walkthrough of the problem, the codebase implementation, and what I learned from contributing to a major open-source project.</p><div><hr></div><h2>The Problem: Cryptic Zod Union Errors</h2><p>Astro uses the <a href="https://zod.dev/">Zod library</a> to define and validate its configuration schema. Zod is excellent for validating structures, but it can struggle with user-friendly messages when checking union types.</p><p>In Astro's security configuration, users can define CSP directives in <code>security.csp.directives</code>. However, because of how browser parsers behave, the directives <code>script-src</code> and <code>style-src</code> must be handled separately using dedicated options like <code>security.csp.scriptDirective</code> and <code>security.csp.styleDirective</code>.</p><p>If a developer mistakenly placed <code>script-src</code> directly into the general <code>directives</code> array like this:</p><pre><code>// astro.config.mjs
export default defineConfig({
  security: {
    csp: {
      directives: ["script-src 'self'"]
    }
  }
});
</code></pre><p>Astro's validation engine would output a frustratingly vague error:</p><pre><code>[config] Astro found issue(s) with your configuration:
! security.csp: Did not match union.
</code></pre><p>This occurred because the configuration schema evaluated the input against a union of valid directives and fell back to the generic Zod union error, leaving the developer with no indication of what went wrong or how to fix it.</p><div><hr></div><h2>The Solution: Refactoring Zod Schema &amp; Custom Error Maps</h2><p>To resolve the issue and provide clear, actionable instructions, we had to apply a two-step surgical update to the Astro core package.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DSHx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F260ea8c5-5991-4abc-b94c-eb8069d69395_2560x1067.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DSHx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F260ea8c5-5991-4abc-b94c-eb8069d69395_2560x1067.png 424w, https://substackcdn.com/image/fetch/$s_!DSHx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F260ea8c5-5991-4abc-b94c-eb8069d69395_2560x1067.png 848w, https://substackcdn.com/image/fetch/$s_!DSHx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F260ea8c5-5991-4abc-b94c-eb8069d69395_2560x1067.png 1272w, https://substackcdn.com/image/fetch/$s_!DSHx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F260ea8c5-5991-4abc-b94c-eb8069d69395_2560x1067.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DSHx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F260ea8c5-5991-4abc-b94c-eb8069d69395_2560x1067.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/260ea8c5-5991-4abc-b94c-eb8069d69395_2560x1067.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Astro CSP Validation Flow&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Astro CSP Validation Flow" title="Astro CSP Validation Flow" srcset="https://substackcdn.com/image/fetch/$s_!DSHx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F260ea8c5-5991-4abc-b94c-eb8069d69395_2560x1067.png 424w, https://substackcdn.com/image/fetch/$s_!DSHx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F260ea8c5-5991-4abc-b94c-eb8069d69395_2560x1067.png 848w, https://substackcdn.com/image/fetch/$s_!DSHx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F260ea8c5-5991-4abc-b94c-eb8069d69395_2560x1067.png 1272w, https://substackcdn.com/image/fetch/$s_!DSHx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F260ea8c5-5991-4abc-b94c-eb8069d69395_2560x1067.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 1: The lifecycle flow of a custom configuration validation error traversing the schema and zod-error-map.</em></p><h3>1. Refactoring schema validation with <code>superRefine</code></h3><p>In <code>packages/astro/src/core/csp/config.ts</code>, the original schema for checking allowed directives was a simple <code>z.custom</code> returning a boolean check:</p><pre><code>export const allowedDirectivesSchema = z.custom&lt;CspDirective&gt;((value) =&gt; {
  if (typeof value !== 'string') {
    return false;
  }
  return ALLOWED_DIRECTIVES.some((allowedValue) =&gt; {
    return value.startsWith(allowedValue);
  });
});
</code></pre><p>I refactored this validator to use <code>.superRefine</code>. This allowed us to inspect the invalid inputs and attach distinct custom issues depending on the specific error context:</p><pre><code>export const allowedDirectivesSchema = z.custom&lt;CspDirective&gt;((v) =&gt; typeof v === 'string').superRefine(
  (value, ctx) =&gt; {
    const isAllowed = ALLOWED_DIRECTIVES.some((allowedValue) =&gt; {
      return value.startsWith(allowedValue);
    });
    if (!isAllowed) {
      if (value.startsWith('script-src') || value.startsWith('style-src')) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: `Directives \`script-src\` and \`style-src\` are not allowed in \`security.csp.directives\`. Please use \`security.csp.scriptDirective\` and \`security.csp.styleDirective\` instead.`,
          fatal: true,
        });
      } else {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: `Invalid directive: "${value}". Allowed directives are: ${ALLOWED_DIRECTIVES.join(', ')}`,
          fatal: true,
        });
      }
    }
  },
);
</code></pre><h3>2. Surfacing custom messages in <code>zod-error-map.ts</code></h3><p>Even with <code>superRefine</code> adding the custom issues, Zod's default union error formatter would still swallow the custom messages during validation formatting.</p><p>To fix this, we updated Astro's custom Zod error formatter in <code>packages/astro/src/core/errors/zod-error-map.ts</code>. We intercepted custom issue codes containing <code>security.csp</code> and directly pushed the custom messages to the expected error shape array:</p><pre><code>if (
  _issue.code === 'custom' &amp;&amp;
  _issue.message &amp;&amp;
  _issue.message.includes('security.csp')
) {
  expectedShape.push(_issue.message);
} else if ('expected' in _issue &amp;&amp; typeof _issue.expected === 'string') {
  expectedShape.push(
    relativePath ? `${relativePath}: ${_issue.expected}` : _issue.expected,
  );
}
</code></pre><p>This bypasses the union-flattening logic, successfully outputting our clean, helpful instructions to the developer's console.</p><div><hr></div><h2>Testing the Validation</h2><p>To ensure the new error mapping behaves correctly, I added a new unit test in <code>packages/astro/test/units/config/config-validate.test.ts</code>:</p><pre><code>it('should provide a helpful error message when script-src or style-src is used in directives', async () =&gt; {
  const configError = await validateConfig({
    security: {
      csp: {
        directives: ["script-src 'self'"],
      },
    },
  }).catch((err) =&gt; err);

  const formattedError = stripVTControlCharacters(formatConfigErrorMessage(configError));
  assert.equal(
    formattedError,
    `[config] Astro found issue(s) with your configuration:\n\n! security.csp: Did not match union.\n  &gt; Expected type boolean | Directives script-src and style-src are not allowed in security.csp.directives. Please use security.csp.scriptDirective and security.csp.styleDirective instead.\n  &gt; Received { "directives": [ "script-src 'self'" ] }`,
  );
});
</code></pre><p>The test runs a config validation, formats the message, strips terminal coloring control characters, and asserts that our specific help instruction is rendered perfectly alongside the union error representation.</p><div><hr></div><h2>Contribution Takeaways</h2><p>Contributing to Astro's core was a fantastic experience. The codebase is clean, well-tested, and uses changesets to automate package version patches. Because this validation error directly affects DX (Developer Experience), it was merged quickly and rolled out in a patch release, resolving configuration confusion for thousands of developers worldwide.</p><p>Thanks for reading. See you in the next lab.</p>]]></content:encoded></item><item><title><![CDATA[Stop Letting AI Guess Your Requirements: The Grill-Me Paradigm]]></title><description><![CDATA[Why Socratic interrogations through the Grill-Me skill are essential to align developer plans, prevent agent assumptions, and write better code.]]></description><link>https://www.farrosfr.com/p/stop-letting-ai-guess-your-requirements-the-grill-me-paradigm</link><guid isPermaLink="false">https://www.farrosfr.com/p/stop-letting-ai-guess-your-requirements-the-grill-me-paradigm</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/48055fd0-3c1f-4135-86ac-d26b3c763888_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tFhh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0eb3b441-464b-4cf9-bce5-c7dd9e31889d_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tFhh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0eb3b441-464b-4cf9-bce5-c7dd9e31889d_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!tFhh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0eb3b441-464b-4cf9-bce5-c7dd9e31889d_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!tFhh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0eb3b441-464b-4cf9-bce5-c7dd9e31889d_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!tFhh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0eb3b441-464b-4cf9-bce5-c7dd9e31889d_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tFhh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0eb3b441-464b-4cf9-bce5-c7dd9e31889d_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0eb3b441-464b-4cf9-bce5-c7dd9e31889d_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Stop Letting AI Guess Your Requirements: The Grill-Me Paradigm&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Stop Letting AI Guess Your Requirements: The Grill-Me Paradigm" title="Stop Letting AI Guess Your Requirements: The Grill-Me Paradigm" srcset="https://substackcdn.com/image/fetch/$s_!tFhh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0eb3b441-464b-4cf9-bce5-c7dd9e31889d_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!tFhh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0eb3b441-464b-4cf9-bce5-c7dd9e31889d_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!tFhh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0eb3b441-464b-4cf9-bce5-c7dd9e31889d_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!tFhh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0eb3b441-464b-4cf9-bce5-c7dd9e31889d_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p></p><p>We have all done it. We open our AI coding assistant, type a vague command like <em>"implement OAuth login"</em> or <em>"refactor this database handler"</em>, and hit enter.</p><p>Immediately, the autonomous agent starts spinning. It refactors three files, creates two new utility modules, updates the dependencies, and generates a massive diff. You compile, and the build fails. You realize the AI made a dozen unchecked assumptions about your architecture, secret keys, and routing structures.</p><p>Vague prompts are the absolute enemy of autonomous coding. To fix this, we need to change how we interact with our agents. Instead of letting them guess, we need to let them interrogate us.</p><p>Enter the <strong>Grill-Me</strong> paradigm.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NF7N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1aeec-f468-42bd-a291-a427d1bc03e0_2560x1067.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NF7N!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1aeec-f468-42bd-a291-a427d1bc03e0_2560x1067.png 424w, https://substackcdn.com/image/fetch/$s_!NF7N!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1aeec-f468-42bd-a291-a427d1bc03e0_2560x1067.png 848w, https://substackcdn.com/image/fetch/$s_!NF7N!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1aeec-f468-42bd-a291-a427d1bc03e0_2560x1067.png 1272w, https://substackcdn.com/image/fetch/$s_!NF7N!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1aeec-f468-42bd-a291-a427d1bc03e0_2560x1067.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NF7N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1aeec-f468-42bd-a291-a427d1bc03e0_2560x1067.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9d1aeec-f468-42bd-a291-a427d1bc03e0_2560x1067.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Grill-Me Alignment Flow&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Grill-Me Alignment Flow" title="Grill-Me Alignment Flow" srcset="https://substackcdn.com/image/fetch/$s_!NF7N!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1aeec-f468-42bd-a291-a427d1bc03e0_2560x1067.png 424w, https://substackcdn.com/image/fetch/$s_!NF7N!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1aeec-f468-42bd-a291-a427d1bc03e0_2560x1067.png 848w, https://substackcdn.com/image/fetch/$s_!NF7N!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1aeec-f468-42bd-a291-a427d1bc03e0_2560x1067.png 1272w, https://substackcdn.com/image/fetch/$s_!NF7N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9d1aeec-f468-42bd-a291-a427d1bc03e0_2560x1067.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p><em>Figure 1: Comparison of execution cycles: Vague prompts lead to broken builds, whereas Socratic alignment loops produce clean, surgical code edits.</em></p><p>Originally developed as part of <a href="https://github.com/mattpocock/skills">Matt Pocock's skills repository</a>, the <code>grill-me</code> skill shifts the human-agent relationship from black-box execution to an interactive Socratic alignment loop.</p><div><hr></div><h2>The Socratic Flow: How the Grill Works</h2><p>The mechanics of the <code>grill-me</code> skill are intentionally restrictive and highly disciplined:</p><ol><li><p><strong>One Question at a Time</strong>: The agent is forbidden from dumping a list of questions. It must ask exactly one probing question at a time to keep your cognitive load low.</p></li><li><p><strong>Recommended Answers</strong>: For every question asked, the agent must propose a suggested answer (e.g., <em>"Recommended: Use the existing auth helper in utils.js"</em>). You can accept it with a simple yes or refine it with your own input.</p></li><li><p><strong>Codebase Awareness</strong>: The agent is instructed to explore your codebase first. It should never ask you questions that can be answered by looking at your files.</p></li></ol><p>This creates a systematic decision tree. You resolve dependencies one by one before a single line of code is edited.</p><div><hr></div><h2>The Developer's Lens: Resolving Plan Confusion</h2><p>As developers, we often start tasks feeling slightly confused or overwhelmed. We know the high-level goal, but we haven't mapped out the edge cases or implementation details.</p><p>When you prompt an agent with <code>/grill-me</code>, it forces you to pause.</p><p>By interviewing you, the AI acts as a sounding board. As you respond to its questions, you are forced to clarify your own requirements. You decide on routing parameters, database constraints, and error boundaries before writing code.</p><p>Instead of staring at a blank editor trying to plan, you simply answer a sequence of simple prompts. By the time the interview is complete, you and the agent share a perfect, validated blueprint of the task.</p><div><hr></div><h2>The Agent's Lens: Preventing Wasted Autonomy</h2><p>From the AI agent's perspective, vagueness is a recipe for context drift and token waste.</p><p>When an agent doesn't have clear requirements, it is forced to hallucinate constraints. It might over-engineer a simple solution or refactor unrelated files. Once it takes a wrong turn, it gets stuck in feedback loops, burning through your API limits.</p><p>Grilling the user beforehand acts as a guardrail. It locks down the implementation parameters (like library versions, error formats, and target paths). This keeps the agent's edits surgical, reduces token usage, and increases the success rate of the build on the first attempt.</p><div><hr></div><h2>Getting Started with Grill-Me</h2><p>You can add the <code>grill-me</code> skill to your agent's configuration using the following command:</p><pre><code>npx skills add https://github.com/mattpocock/skills --skill grill-me
</code></pre><p>Once installed, simply prefix your complex tasks with <code>/grill-me</code> (e.g., <code>/grill-me I want to add a caching layer to our API routes</code>) to initiate the interview.</p><p>By shifting the starting line from <em>writing code</em> to <em>answering questions</em>, you eliminate guesswork, save your API budget, and build a clearer understanding of your own projects.</p><p>Thanks for reading. See you in the next experience.</p>]]></content:encoded></item><item><title><![CDATA[Boredom by Design: Why Staring at a Wall Requires a Clean Mind]]></title><description><![CDATA[My experience with the 10-minute wall-staring method, and why mental visual clarity and self-control are essential to unlocking deep, productive focus.]]></description><link>https://www.farrosfr.com/p/boredom-by-design-why-staring-at-a-wall-requires-a-clean-mind</link><guid isPermaLink="false">https://www.farrosfr.com/p/boredom-by-design-why-staring-at-a-wall-requires-a-clean-mind</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c6fcafbd-273b-4674-bf52-865a555b871f_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gCWk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae619b45-c4e0-45e5-8067-170caaed7752_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gCWk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae619b45-c4e0-45e5-8067-170caaed7752_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!gCWk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae619b45-c4e0-45e5-8067-170caaed7752_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!gCWk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae619b45-c4e0-45e5-8067-170caaed7752_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!gCWk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae619b45-c4e0-45e5-8067-170caaed7752_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gCWk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae619b45-c4e0-45e5-8067-170caaed7752_1376x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ae619b45-c4e0-45e5-8067-170caaed7752_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Boredom by Design: Why Staring at a Wall Requires a Clean Mind&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Boredom by Design: Why Staring at a Wall Requires a Clean Mind" title="Boredom by Design: Why Staring at a Wall Requires a Clean Mind" srcset="https://substackcdn.com/image/fetch/$s_!gCWk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae619b45-c4e0-45e5-8067-170caaed7752_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!gCWk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae619b45-c4e0-45e5-8067-170caaed7752_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!gCWk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae619b45-c4e0-45e5-8067-170caaed7752_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!gCWk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae619b45-c4e0-45e5-8067-170caaed7752_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p></p><p>In a world engineered for hyper-stimulation, doing absolutely nothing has become a radical act. Between doom scrolling, notifications, and background music, our brains are constantly flooded with cheap dopamine.</p><p>Recently, I began practicing a simple ritual: the <strong>10-minute wall-staring method</strong>. Before starting a 3-hour session of deep study or deep work, I sit in a chair and stare at a completely blank wall for exactly ten minutes. No phone, no music, no food - just me and the wall.</p><p>This simple practice has transformed my productivity, but I quickly realized it has a strict prerequisite: mental visual clarity. Here is how the method works, why it is deeply connected to strict self-control, and how intentional boredom resets your focus.</p><div><hr></div><h2>The Mirror of the Wall: Connecting to Self-Control</h2><blockquote><p>"If you want to destroy any nation without war, make adultery or nudity common in the young generation."<br><strong>Saladin the Wise</strong></p></blockquote><p>This ancient observation points to a timeless truth: a society's focus and strength are directly tied to its self-control. On an individual level, the same principle applies. If you want to destroy your own focus without effort, flood your mind with hyper-stimulation.</p><p>When you stare at a blank wall, there are no external inputs to distract you. You are looking at a blank canvas. Factually, your eyes see nothing, which means the wall becomes a projector for your internal thoughts. It reflects whatever you have been feeding your mind.</p><p>If you do not exercise strict self-control (especially regarding highly stimulating content like sexual content, nudity, or doom scrolling), staring at a blank wall becomes significantly harder.</p><p>In that blank space, your brain attempts to replace the lack of external visual stimulation with internal imagery. If your mind is cluttered with hyper-sexualized thoughts or the residue of cheap stimulation, those unclean loops will flood your consciousness. You will struggle to sit still, get anxious, and find it extremely difficult to maintain peace. This discipline is found across many spiritual traditions, most strictly in Islamic teachings on guarding one's gaze and thoughts.</p><p>To reap the benefits of the wall-staring method, you must first keep your mental canvas clean. Cultivating self-control removes the high-stimulus clutter, allowing the blank space of the wall to remain calm rather than chaotic.</p><div><hr></div><h2>The Friction of Extreme Boredom</h2><p>Staring at a wall for ten minutes sounds easy until you try it.</p><p>Around the three-minute mark, an intense, crushing boredom sets in. Your brain, desperate for a dopamine hit, will start suggesting alternative, "productive" distractions. You will think:</p><ul><li><p><em>"This is a waste of time. Doing push-ups would be much better than doing nothing!"</em></p></li><li><p><em>"I should quickly tidy up my desk."</em></p></li><li><p><em>"Let me plan my study schedule instead."</em></p></li></ul><p>The core rule of the method is that <strong>you must hold</strong>. You must resist even productive urges until the 10-minute timer is completely finished. You have to sit through the friction of doing nothing.</p><div><hr></div><h2>Re-calibration: Deep Work as a Reward</h2><p>The magic of the wall-staring method lies in the phenomena of measurable, intentional boredom. By forcing your brain to experience absolute inactivity, you lower your dopamine threshold.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!phaJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24afe9de-83be-441f-ab77-5684dcaa2090_2560x1067.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!phaJ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24afe9de-83be-441f-ab77-5684dcaa2090_2560x1067.png 424w, https://substackcdn.com/image/fetch/$s_!phaJ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24afe9de-83be-441f-ab77-5684dcaa2090_2560x1067.png 848w, https://substackcdn.com/image/fetch/$s_!phaJ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24afe9de-83be-441f-ab77-5684dcaa2090_2560x1067.png 1272w, https://substackcdn.com/image/fetch/$s_!phaJ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24afe9de-83be-441f-ab77-5684dcaa2090_2560x1067.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!phaJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24afe9de-83be-441f-ab77-5684dcaa2090_2560x1067.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/24afe9de-83be-441f-ab77-5684dcaa2090_2560x1067.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Dopamine Threshold Comparison&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Dopamine Threshold Comparison" title="Dopamine Threshold Comparison" srcset="https://substackcdn.com/image/fetch/$s_!phaJ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24afe9de-83be-441f-ab77-5684dcaa2090_2560x1067.png 424w, https://substackcdn.com/image/fetch/$s_!phaJ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24afe9de-83be-441f-ab77-5684dcaa2090_2560x1067.png 848w, https://substackcdn.com/image/fetch/$s_!phaJ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24afe9de-83be-441f-ab77-5684dcaa2090_2560x1067.png 1272w, https://substackcdn.com/image/fetch/$s_!phaJ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24afe9de-83be-441f-ab77-5684dcaa2090_2560x1067.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 1: Comparison of dopamine thresholds: over-stimulation blocks cognitive tasks, while wall-staring recalibrates sensitivity.</em></p><p>If you spend ten minutes staring at a blank wall, your baseline adjusts. When the timer finally goes off, starting to read a technical book, writing code, or studying feels like a stimulating escape. Your brain welcomes the work because it is infinitely more engaging than the void of the wall.</p><p>By contrast, if you start your study session after doom scrolling or looking at sexual content, your dopamine threshold is raised so high that sitting down to study feels like pulling teeth.</p><p>Doing nothing with a clean mind is a powerful tool to reset your baseline, allowing you to transition easily into 3 hours of uninterrupted, deep focus.</p><p>Thanks for reading. See you in the next experience.</p>]]></content:encoded></item><item><title><![CDATA[Strava API Changes: The Death of the Free Developer Tier]]></title><description><![CDATA[My experience integrating the Strava API in my Threads Watermark app, and how Strava's 2026 subscription mandate broke free-tier API access.]]></description><link>https://www.farrosfr.com/p/strava-api-changes-the-death-of-the-free-developer-tier</link><guid isPermaLink="false">https://www.farrosfr.com/p/strava-api-changes-the-death-of-the-free-developer-tier</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/558d92c4-493b-4f8a-8969-89b746803297_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-iAc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa400bca0-83f8-4271-8c63-71d1ee973c7a_1200x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-iAc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa400bca0-83f8-4271-8c63-71d1ee973c7a_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!-iAc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa400bca0-83f8-4271-8c63-71d1ee973c7a_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!-iAc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa400bca0-83f8-4271-8c63-71d1ee973c7a_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!-iAc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa400bca0-83f8-4271-8c63-71d1ee973c7a_1200x630.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-iAc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa400bca0-83f8-4271-8c63-71d1ee973c7a_1200x630.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a400bca0-83f8-4271-8c63-71d1ee973c7a_1200x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Strava API Changes: The Death of the Free Developer Tier&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Strava API Changes: The Death of the Free Developer Tier" title="Strava API Changes: The Death of the Free Developer Tier" srcset="https://substackcdn.com/image/fetch/$s_!-iAc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa400bca0-83f8-4271-8c63-71d1ee973c7a_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!-iAc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa400bca0-83f8-4271-8c63-71d1ee973c7a_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!-iAc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa400bca0-83f8-4271-8c63-71d1ee973c7a_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!-iAc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa400bca0-83f8-4271-8c63-71d1ee973c7a_1200x630.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p></p><p>Earlier this year, I started a side project called <a href="https://github.com/farrosfr/strava">Daily Threads Watermark</a>, which is a static web application designed to help runners and fitness enthusiasts generate watermarked workout summaries and text drafts for Threads.</p><p>One of the application's most popular features was its seamless Strava integration. However, due to recent policy shifts in the Strava Developer Program, this integration is now completely broken for free-tier users. Here is what happened, how the integration worked, and the workaround for developer-athletes.</p><div><hr></div><h2>The History: Asynchronous Static Sync</h2><p>Because the application is hosted as a serverless static site on GitHub Pages, we could not query the Strava API directly from the client side without leaking private credentials (<code>Client Secret</code>).</p><p>To solve this securely, the app was designed around an asynchronous build pipeline:</p><ol><li><p><strong>GitHub Actions Workflow</strong>: A cron job triggered every 30 minutes.</p></li><li><p><strong>Strava API Query</strong>: The workflow read repository secrets (<code>STRAVA_CLIENT_ID</code>, <code>STRAVA_CLIENT_SECRET</code>, and <code>STRAVA_REFRESH_TOKEN</code>), fetched my latest run and walk distances directly from Strava's endpoints, and compiled the data.</p></li><li><p><strong>Static Summary File</strong>: The workflow wrote the fetched metrics into a public JSON summary at <code>public/data/strava-summary.json</code>.</p></li><li><p><strong>Local Consumption</strong>: When the static frontend loaded in a browser, it requested the local JSON file to pre-fill the day's distance, avoiding direct API requests from client code.</p></li></ol><p>This architecture allowed for a lightweight, zero-cost, and completely secure sync cycle.</p><div><hr></div><h2>The Policy Shift: Pay-to-Play API Access</h2><p>In June 2026, Strava overhauled its Developer Program. The most significant change directly affects "single-player" developers who write scripts or small tools for their personal use:</p><ul><li><p><strong>Subscription Mandate</strong>: Access to the <strong>Standard Tier</strong> API (up to 10 athletes) now requires an active, paid Strava subscription.</p></li><li><p><strong>Effective Date</strong>: The policy went into effect immediately for new applications, with a grace period for existing active developer accounts ending on <strong>June 30, 2026</strong>.</p></li></ul><p>Without an active Strava subscription connected to the developer account, API requests are rejected, immediately halting the GitHub Actions automated sync workflow. You can check your application status and subscription eligibility directly on your <a href="https://www.strava.com/settings/api">Strava API Settings Dashboard</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VxD_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F653ec6bc-f68c-4919-b02b-9838e21cd9c1_1280x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VxD_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F653ec6bc-f68c-4919-b02b-9838e21cd9c1_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!VxD_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F653ec6bc-f68c-4919-b02b-9838e21cd9c1_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!VxD_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F653ec6bc-f68c-4919-b02b-9838e21cd9c1_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!VxD_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F653ec6bc-f68c-4919-b02b-9838e21cd9c1_1280x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VxD_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F653ec6bc-f68c-4919-b02b-9838e21cd9c1_1280x800.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/653ec6bc-f68c-4919-b02b-9838e21cd9c1_1280x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Strava Subscription Requirement&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Strava Subscription Requirement" title="Strava Subscription Requirement" srcset="https://substackcdn.com/image/fetch/$s_!VxD_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F653ec6bc-f68c-4919-b02b-9838e21cd9c1_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!VxD_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F653ec6bc-f68c-4919-b02b-9838e21cd9c1_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!VxD_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F653ec6bc-f68c-4919-b02b-9838e21cd9c1_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!VxD_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F653ec6bc-f68c-4919-b02b-9838e21cd9c1_1280x800.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 1: The Strava API Application dashboard displaying the banner warning that API access is updating to be subscriber-only.</em></p><div><hr></div><h2>The Fallback: Manual CSV Imports</h2><p>Fortunately, when designing the application, I built in a fallback mechanism in anticipation of potential API outages or sync errors.</p><p>Rather than relying on automated API hooks, users can manually export their activity history from their Strava account settings and upload the CSV file directly into the browser. The application processes the CSV locally in-memory, parses the activity distances by date, and updates the daily counter dashboard without sending any data to external servers.</p><p>While the automation era of Strava's free API tier is officially over, static tools can still survive by shifting data ownership back to local browser storage and manual exports.</p><div><hr></div><h2>Follow Me on Strava</h2><p>If you want to look or follow my daily workouts, feel free to check out my Strava account here:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://www.strava.com/athletes/farrosfr" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5dnv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3c00-bb49-4f80-8587-3afd230aa98f_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!5dnv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3c00-bb49-4f80-8587-3afd230aa98f_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!5dnv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3c00-bb49-4f80-8587-3afd230aa98f_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!5dnv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3c00-bb49-4f80-8587-3afd230aa98f_1280x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5dnv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3c00-bb49-4f80-8587-3afd230aa98f_1280x800.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e53b3c00-bb49-4f80-8587-3afd230aa98f_1280x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;My Strava Account&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://www.strava.com/athletes/farrosfr&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="My Strava Account" title="My Strava Account" srcset="https://substackcdn.com/image/fetch/$s_!5dnv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3c00-bb49-4f80-8587-3afd230aa98f_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!5dnv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3c00-bb49-4f80-8587-3afd230aa98f_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!5dnv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3c00-bb49-4f80-8587-3afd230aa98f_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!5dnv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53b3c00-bb49-4f80-8587-3afd230aa98f_1280x800.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 2: Feel free to check out or follow my athlete profile at <a href="https://www.strava.com/athletes/farrosfr">strava.com/athletes/farrosfr</a>.</em></p><p>Thanks for reading. See you in the next lab.</p>]]></content:encoded></item><item><title><![CDATA[How I Added Affixed Capture Support to Axum's Typed Paths]]></title><description><![CDATA[How I contributed to tokio-rs/axum to support prefixed and suffixed typed path captures like /@{username} and /files/{name}.json.]]></description><link>https://www.farrosfr.com/p/how-i-added-affixed-capture-support-to-axum-s-typed-paths</link><guid isPermaLink="false">https://www.farrosfr.com/p/how-i-added-affixed-capture-support-to-axum-s-typed-paths</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/15656332-abda-435b-bf80-e9c7a5415138_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3vto!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13eeed6d-d2c0-4654-b538-0da4380b9876_1200x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3vto!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13eeed6d-d2c0-4654-b538-0da4380b9876_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!3vto!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13eeed6d-d2c0-4654-b538-0da4380b9876_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!3vto!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13eeed6d-d2c0-4654-b538-0da4380b9876_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!3vto!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13eeed6d-d2c0-4654-b538-0da4380b9876_1200x630.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3vto!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13eeed6d-d2c0-4654-b538-0da4380b9876_1200x630.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/13eeed6d-d2c0-4654-b538-0da4380b9876_1200x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;How I Added Affixed Capture Support to Axum's Typed Paths&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="How I Added Affixed Capture Support to Axum's Typed Paths" title="How I Added Affixed Capture Support to Axum's Typed Paths" srcset="https://substackcdn.com/image/fetch/$s_!3vto!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13eeed6d-d2c0-4654-b538-0da4380b9876_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!3vto!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13eeed6d-d2c0-4654-b538-0da4380b9876_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!3vto!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13eeed6d-d2c0-4654-b538-0da4380b9876_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!3vto!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13eeed6d-d2c0-4654-b538-0da4380b9876_1200x630.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>A few weeks ago, my pull request <a href="https://github.com/tokio-rs/axum/pull/3782">tokio-rs/axum#3782</a> was merged into <code>axum</code>. This contribution resolves a long-standing limitation in Axum's typed routing system: the inability to support affixed (prefixed or suffixed) capture patterns in typed paths.</p><p>Here is the story behind the problem, how the macro parser was updated, and what this means for your Rust APIs.</p><h2>What is Axum?</h2><p><a href="https://github.com/tokio-rs/axum">Axum</a> is a modern web application framework built for <strong>Rust</strong>, the systems programming language known for memory safety, performance, and concurrency. Axum is developed as part of the <a href="https://tokio.rs/">Tokio</a> ecosystem (Rust's premier asynchronous runtime), making it highly efficient for handling concurrent network traffic at scale.</p><p>Axum stands out because of its ergonomic, declarative programming model:</p><ol><li><p><strong>Type-Safe Routing</strong>: Routes map requests directly to handler functions.</p></li><li><p><strong>Extractors</strong>: Handlers extract parameters (such as JSON payloads, headers, query parameters, or paths) directly from signature arguments using types.</p></li><li><p><strong>Middleware</strong>: Built on top of the <code>tower</code> ecosystem, allowing modular middleware stack reusability.</p></li></ol><p>Here is a simple example of a standard Axum server setup in Rust:</p><pre><code>use axum::{routing::get, Router};

#[tokio::main]
async fn main() {
    // Define routing and handlers
    let app = Router::new()
        .route("/", get(hello_world))
        .route("/users/{id}", get(get_user));

    // Run server on localhost:3000
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn hello_world() -&gt; &amp;'static str {
    "Hello, World!"
}

async fn get_user(axum::extract::Path(id): axum::extract::Path&lt;u32&gt;) -&gt; String {
    format!("User ID: {}", id)
}
</code></pre><p>By leveraging Rust's strong static typing and macro system, Axum compiles checks for route correctness, route parameters, and request payload safety at build time.</p><div><hr></div><h2>The Problem: Segment-Bound Captures</h2><p>Axum provides a powerful macro-based router extension called <code>TypedPath</code>. It allows developers to define type-safe routes by deriving the <code>TypedPath</code> trait on structs representing route parameters:</p><pre><code>use axum_extra::routing::TypedPath;
use serde::Deserialize;

#[derive(TypedPath, Deserialize)]
#[typed_path("/users/{id}")]
struct ShowUser {
    id: u32,
}
</code></pre><p>However, the previous typed path parser in <code>axum-macros</code> split route paths strictly by the <code>/</code> separator and assumed each path segment was either entirely static or entirely a capture (surrounded by <code>{</code> and <code>}</code>).</p><p>This design meant route patterns with prefixed or suffixed captures (such as <code>/@{username}</code> for user profiles or <code>/files/{name}.json</code> for format extensions) would fail to compile, even though they are perfectly valid route patterns in standard routers.</p><div><hr></div><h2>The Solution: Parsing Affixed Captures</h2><p>To solve this, we needed to update the macro parser in <code>axum-macros/src/typed_path.rs</code>. Instead of checking if a segment starts and ends with braces, we updated the parser to scan for capture brackets <em>inside</em> each path segment.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5QAo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3eb2b521-d6ec-4aa7-9908-67007a85b85b_2560x1333.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5QAo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3eb2b521-d6ec-4aa7-9908-67007a85b85b_2560x1333.png 424w, https://substackcdn.com/image/fetch/$s_!5QAo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3eb2b521-d6ec-4aa7-9908-67007a85b85b_2560x1333.png 848w, https://substackcdn.com/image/fetch/$s_!5QAo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3eb2b521-d6ec-4aa7-9908-67007a85b85b_2560x1333.png 1272w, https://substackcdn.com/image/fetch/$s_!5QAo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3eb2b521-d6ec-4aa7-9908-67007a85b85b_2560x1333.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5QAo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3eb2b521-d6ec-4aa7-9908-67007a85b85b_2560x1333.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3eb2b521-d6ec-4aa7-9908-67007a85b85b_2560x1333.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Typed Path Segment Parsing Flow&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Typed Path Segment Parsing Flow" title="Typed Path Segment Parsing Flow" srcset="https://substackcdn.com/image/fetch/$s_!5QAo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3eb2b521-d6ec-4aa7-9908-67007a85b85b_2560x1333.png 424w, https://substackcdn.com/image/fetch/$s_!5QAo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3eb2b521-d6ec-4aa7-9908-67007a85b85b_2560x1333.png 848w, https://substackcdn.com/image/fetch/$s_!5QAo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3eb2b521-d6ec-4aa7-9908-67007a85b85b_2560x1333.png 1272w, https://substackcdn.com/image/fetch/$s_!5QAo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3eb2b521-d6ec-4aa7-9908-67007a85b85b_2560x1333.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 1: Visual flowchart showing the parsing logic extracting static prefixes, capture keys, and static suffixes from a route segment.</em></p><p>Here is the core logic implemented in the PR to split segments by capture boundaries while handling double-brace escaping (e.g., <code>{{escaped}}</code>):</p><pre><code>fn parse_path(path: &amp;LitStr) -&gt; syn::Result&lt;Vec&lt;Segment&gt;&gt; {
    let value = path.value();
    let mut segments = Vec::new();
    let mut rest = value.as_str();

    while let Some(start) = find_first_not_double(b'{', rest.as_bytes()) {
        if start &gt; 0 {
            segments.push(Segment::Static(rest[..start].to_owned()));
        }

        rest = &amp;rest[start + 1..];

        if let Some(end) = rest.find('}') {
            let capture = &amp;rest[..end];
            if capture.is_empty() || capture.contains('{') {
                return Err(syn::Error::new_spanned(path, "invalid capture in path"));
            }

            segments.push(Segment::Capture(
                capture.strip_prefix('*').unwrap_or(capture).to_owned(),
                path.span(),
            ));

            rest = &amp;rest[end + 1..];
        } else {
            segments.push(Segment::Static(format!("{{{rest}")));
            rest = "";
        }
    }

    if !rest.is_empty() {
        segments.push(Segment::Static(rest.to_owned()));
    }

    Ok(segments)
}
</code></pre><p>We also introduced a helper function, <code>find_first_not_double</code>, to skip escaped braces so they are treated as static route components rather than dynamic captures:</p><pre><code>fn find_first_not_double(needle: u8, haystack: &amp;[u8]) -&gt; Option&lt;usize&gt; {
    let mut possible_capture = 0;
    while let Some(index) = haystack
        .get(possible_capture..)
        .and_then(|haystack| haystack.iter().position(|byte| byte == &amp;needle))
    {
        let index = index + possible_capture;

        if haystack.get(index + 1) == Some(&amp;needle) {
            possible_capture = index + 2;
            continue;
        }

        return Some(index);
    }

    None
}
</code></pre><div><hr></div><h2>Verifying with Formatting &amp; Tests</h2><p>A crucial part of <code>TypedPath</code> is implementing the <code>Display</code> trait so path instances can be formatted back into URLs:</p><pre><code>let path = PrefixedCapture { username: "alice".to_owned() };
assert_eq!(format!("{}", path), "/@alice");
</code></pre><p>Because the parser now isolates the static prefix and suffix parts of a segment, <code>TypedPath</code> preserves formatting, percent-encodes capture values correctly (e.g. spaces turn into <code>%20</code>), and supports all standard Rust primitive types and custom deserialize structures.</p><p>The test coverage added in the PR ensures that prefixed, suffixed, and escaped routes compile and execute correctly:</p><pre><code>#[derive(TypedPath, Deserialize)]
#[typed_path("/@{username}")]
struct PrefixedCapture {
    username: String,
}

#[derive(TypedPath, Deserialize)]
#[typed_path("/files/{name}.json")]
struct SuffixedCapture {
    name: String,
}
</code></pre><p>This PR makes Axum's typed routing much more flexible and brings full path matching parity with standard dynamic route definitions.</p><p>Thanks for reading. See you in the next lab.</p>]]></content:encoded></item><item><title><![CDATA[Microsoft Windows Hardening | TryHackMe Write-up]]></title><description><![CDATA[Complete walkthrough for the Microsoft Windows Hardening TryHackMe room. Learn how to secure and harden Windows 10 OS configurations.]]></description><link>https://www.farrosfr.com/p/microsoft-windows-hardening-tryhackme-write-up</link><guid isPermaLink="false">https://www.farrosfr.com/p/microsoft-windows-hardening-tryhackme-write-up</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/4dc25df2-a006-4e28-9938-a41286896d53_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1pdZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90ec010-ad4e-4d3f-b96a-593a970f93f3_1200x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1pdZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90ec010-ad4e-4d3f-b96a-593a970f93f3_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!1pdZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90ec010-ad4e-4d3f-b96a-593a970f93f3_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!1pdZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90ec010-ad4e-4d3f-b96a-593a970f93f3_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!1pdZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90ec010-ad4e-4d3f-b96a-593a970f93f3_1200x630.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1pdZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90ec010-ad4e-4d3f-b96a-593a970f93f3_1200x630.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a90ec010-ad4e-4d3f-b96a-593a970f93f3_1200x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Microsoft Windows Hardening | TryHackMe Write-up&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Microsoft Windows Hardening | TryHackMe Write-up" title="Microsoft Windows Hardening | TryHackMe Write-up" srcset="https://substackcdn.com/image/fetch/$s_!1pdZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90ec010-ad4e-4d3f-b96a-593a970f93f3_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!1pdZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90ec010-ad4e-4d3f-b96a-593a970f93f3_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!1pdZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90ec010-ad4e-4d3f-b96a-593a970f93f3_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!1pdZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa90ec010-ad4e-4d3f-b96a-593a970f93f3_1200x630.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>This is my write-up for the TryHackMe room on <a href="https://tryhackme.com/room/microsoftwindowshardening">Microsoft Windows Hardening</a>. Written in 2026, I hope this write-up helps others learn and practice cybersecurity.</p><h2>Task 1: Introduction</h2><p>System hardening improves a workstation's security posture by reducing vulnerabilities in its services, identity permissions, network connectivity, storage configurations, and &#8230;</p>
      <p>
          <a href="https://www.farrosfr.com/p/microsoft-windows-hardening-tryhackme-write-up">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Building a Full-Stack App on Cloudflare's Free Tier with Astro SSR]]></title><description><![CDATA[A practical guide to building and deploying a full-stack web app using Cloudflare Workers, D1 database, KV storage, and Astro SSR. Completely free.]]></description><link>https://www.farrosfr.com/p/building-a-full-stack-app-on-cloudflare-s-free-tier-with-astro-ssr</link><guid isPermaLink="false">https://www.farrosfr.com/p/building-a-full-stack-app-on-cloudflare-s-free-tier-with-astro-ssr</guid><dc:creator><![CDATA[Mochammad Farros Fatchur Roji]]></dc:creator><pubDate>Mon, 06 Jul 2026 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/9d94dedf-792f-4725-96ca-5bc7962d5cf8_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Jyqx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec363f67-276c-4da0-808e-ebeaedd1b3b1_1200x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Jyqx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec363f67-276c-4da0-808e-ebeaedd1b3b1_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!Jyqx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec363f67-276c-4da0-808e-ebeaedd1b3b1_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!Jyqx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec363f67-276c-4da0-808e-ebeaedd1b3b1_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!Jyqx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec363f67-276c-4da0-808e-ebeaedd1b3b1_1200x630.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Jyqx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec363f67-276c-4da0-808e-ebeaedd1b3b1_1200x630.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ec363f67-276c-4da0-808e-ebeaedd1b3b1_1200x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Building a Full-Stack App on Cloudflare's Free Tier with Astro SSR&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Building a Full-Stack App on Cloudflare's Free Tier with Astro SSR" title="Building a Full-Stack App on Cloudflare's Free Tier with Astro SSR" srcset="https://substackcdn.com/image/fetch/$s_!Jyqx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec363f67-276c-4da0-808e-ebeaedd1b3b1_1200x630.png 424w, https://substackcdn.com/image/fetch/$s_!Jyqx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec363f67-276c-4da0-808e-ebeaedd1b3b1_1200x630.png 848w, https://substackcdn.com/image/fetch/$s_!Jyqx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec363f67-276c-4da0-808e-ebeaedd1b3b1_1200x630.png 1272w, https://substackcdn.com/image/fetch/$s_!Jyqx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec363f67-276c-4da0-808e-ebeaedd1b3b1_1200x630.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>I recently shared a <a href="https://www.threads.com/@farrosfr/post/DaZUbtqEsYT">post on Threads</a> about how generous Cloudflare's free tier is for side projects.</p><p>This isn't theoretical. I run a production app on this exact stack, handling <strong>27,000+ requests per day</strong> with <strong>zero errors</strong> and an average CPU time of <strong>5.24ms per request</strong>, all without paying a single cent.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pGU7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c74fc66-bf5c-4233-848c-aa2668c2c44f_1280x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pGU7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c74fc66-bf5c-4233-848c-aa2668c2c44f_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!pGU7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c74fc66-bf5c-4233-848c-aa2668c2c44f_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!pGU7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c74fc66-bf5c-4233-848c-aa2668c2c44f_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!pGU7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c74fc66-bf5c-4233-848c-aa2668c2c44f_1280x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pGU7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c74fc66-bf5c-4233-848c-aa2668c2c44f_1280x800.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1c74fc66-bf5c-4233-848c-aa2668c2c44f_1280x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Cloudflare D1 Pricing Page&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Cloudflare D1 Pricing Page" title="Cloudflare D1 Pricing Page" srcset="https://substackcdn.com/image/fetch/$s_!pGU7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c74fc66-bf5c-4233-848c-aa2668c2c44f_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!pGU7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c74fc66-bf5c-4233-848c-aa2668c2c44f_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!pGU7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c74fc66-bf5c-4233-848c-aa2668c2c44f_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!pGU7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c74fc66-bf5c-4233-848c-aa2668c2c44f_1280x800.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p><em>Figure 1: Cloudflare D1's free tier includes 5 GB storage, 5 million reads/day, and 100,000 writes/day.</em></p><h2>Why Cloudflare's Free Tier?</h2><p>Most cloud providers give you a "free trial" that expires after 30 days. Cloudflare's free tier is <strong>permanent</strong>. No credit card required, no surprise bills. For indie developers and side projects, this is a game changer.</p><p>Here's what you get for free:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wMRC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcadb7f20-3a47-4e81-96cf-9a0a3e8873cb_900x340.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wMRC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcadb7f20-3a47-4e81-96cf-9a0a3e8873cb_900x340.png 424w, https://substackcdn.com/image/fetch/$s_!wMRC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcadb7f20-3a47-4e81-96cf-9a0a3e8873cb_900x340.png 848w, https://substackcdn.com/image/fetch/$s_!wMRC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcadb7f20-3a47-4e81-96cf-9a0a3e8873cb_900x340.png 1272w, https://substackcdn.com/image/fetch/$s_!wMRC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcadb7f20-3a47-4e81-96cf-9a0a3e8873cb_900x340.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wMRC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcadb7f20-3a47-4e81-96cf-9a0a3e8873cb_900x340.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cadb7f20-3a47-4e81-96cf-9a0a3e8873cb_900x340.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Cloudflare Free Tier Limits&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Cloudflare Free Tier Limits" title="Cloudflare Free Tier Limits" srcset="https://substackcdn.com/image/fetch/$s_!wMRC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcadb7f20-3a47-4e81-96cf-9a0a3e8873cb_900x340.png 424w, https://substackcdn.com/image/fetch/$s_!wMRC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcadb7f20-3a47-4e81-96cf-9a0a3e8873cb_900x340.png 848w, https://substackcdn.com/image/fetch/$s_!wMRC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcadb7f20-3a47-4e81-96cf-9a0a3e8873cb_900x340.png 1272w, https://substackcdn.com/image/fetch/$s_!wMRC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcadb7f20-3a47-4e81-96cf-9a0a3e8873cb_900x340.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p><em>Figure 2: Summary of Cloudflare's permanent free tier limits for Workers, D1 database, KV store, and Pages.</em></p><p>Combined, this gives you a full-stack platform: compute, database, caching, and static hosting, all at the edge, globally distributed.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bAFX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ae9a20c-dd92-43b7-b17e-c4f1e1d46b7c_1280x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bAFX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ae9a20c-dd92-43b7-b17e-c4f1e1d46b7c_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!bAFX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ae9a20c-dd92-43b7-b17e-c4f1e1d46b7c_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!bAFX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ae9a20c-dd92-43b7-b17e-c4f1e1d46b7c_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!bAFX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ae9a20c-dd92-43b7-b17e-c4f1e1d46b7c_1280x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bAFX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ae9a20c-dd92-43b7-b17e-c4f1e1d46b7c_1280x800.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7ae9a20c-dd92-43b7-b17e-c4f1e1d46b7c_1280x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Cloudflare Workers Dashboard&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Cloudflare Workers Dashboard" title="Cloudflare Workers Dashboard" srcset="https://substackcdn.com/image/fetch/$s_!bAFX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ae9a20c-dd92-43b7-b17e-c4f1e1d46b7c_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!bAFX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ae9a20c-dd92-43b7-b17e-c4f1e1d46b7c_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!bAFX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ae9a20c-dd92-43b7-b17e-c4f1e1d46b7c_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!bAFX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ae9a20c-dd92-43b7-b17e-c4f1e1d46b7c_1280x800.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 3: A real production dashboard. 27K requests in 24 hours, 0 errors, bindings for D1, KV, Assets, and Images.</em></p><h2>The Architecture</h2><p>The key insight from my experience: <strong>not everything needs to be server-rendered</strong>. By using Astro's hybrid rendering mode, you can prerender static pages at build time and only use SSR for pages that genuinely need real-time data. This dramatically reduces your D1 reads and keeps you well within free tier limits.</p><pre><code>&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9474;                  Cloudflare Edge                &#9474;
&#9474;                                                 &#9474;
&#9474;  &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;   &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;   &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;  &#9474;
&#9474;  &#9474;  Assets  &#9474;   &#9474;  Worker  &#9474;   &#9474;     KV      &#9474;  &#9474;
&#9474;  &#9474; (Static) &#9474;   &#9474;  (SSR)   &#9474;&#9668;&#9472;&#9472;&#9474;  (Sessions) &#9474;  &#9474;
&#9474;  &#9492;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;   &#9492;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;   &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;  &#9474;
&#9474;       &#9474;              &#9474;                          &#9474;
&#9474;       &#9474;              &#9660;                          &#9474;
&#9474;       &#9474;         &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;                    &#9474;
&#9474;       &#9474;         &#9474;    D1    &#9474;                    &#9474;
&#9474;       &#9474;         &#9474;   (SQL)  &#9474;                    &#9474;
&#9474;       &#9474;         &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;                    &#9474;
&#9474;       &#9660;                                         &#9474;
&#9474;   Static Pages    Dynamic Pages                 &#9474;
&#9474;   (prerendered)   (server-rendered)             &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
</code></pre><p><strong>Static pages</strong> (homepage, about, blog posts) are prerendered at build time and served directly from the Assets binding. No Worker invocation, no D1 reads.</p><p><strong>Dynamic pages</strong> (dashboard, user settings, API routes) are server-rendered by the Worker, which queries D1 for data and KV for session management.</p><h3>Edge Routing with <code>_routes.json</code></h3><p>When you build an Astro hybrid application, the <code>@astrojs/cloudflare</code> adapter automatically generates a <code>_routes.json</code> file in the build output (<code>dist/</code>). This file is a configuration manifest for Cloudflare's edge router, defining:</p><ul><li><p><strong>Include rules</strong>: Wildcard patterns for routes that must invoke the Worker (e.g., <code>/bookmarks</code>, <code>/api/*</code>).</p></li><li><p><strong>Exclude rules</strong>: Patterns for routes that should bypass the Worker completely and serve static assets directly from Cloudflare's CDN storage (e.g., <code>/about</code>, <code>/_astro/*</code>, <code>/favicon.svg</code>).</p></li></ul><p>A typical automatically generated <code>_routes.json</code> looks like this:</p><pre><code>{
  "version": 1,
  "include": [
    "/bookmarks",
    "/api/*"
  ],
  "exclude": [
    "/about",
    "/_astro/*",
    "/favicon.svg"
  ]
}
</code></pre><p>Because this routing happens at Cloudflare's CDN layer <em>before</em> reaching the Worker:</p><ol><li><p><strong>0ms Worker CPU Time</strong>: Static pages cost zero Worker compute, leaving the full 10ms CPU limit for dynamic endpoints.</p></li><li><p><strong>0 Worker requests count</strong>: Excluded routes do not count toward your daily 100,000 Workers request quota.</p></li></ol><h3>Serverless SQL Without Connection Pooling</h3><p>In a traditional cloud setup (such as AWS Lambda or traditional VMs), scaling serverless functions requires connecting to a relational database like PostgreSQL or MySQL. This introduces two major performance bottlenecks:</p><ol><li><p><strong>TCP Handshake Latency</strong>: Creating new database connections is slow and expensive.</p></li><li><p><strong>Connection Exhaustion</strong>: High concurrent traffic can crash the database by exhausting available connections, requiring complex external poolers (like PgBouncer).</p></li></ol><p>Cloudflare D1 is different. It is a serverless SQL database built on SQLite that runs directly inside Cloudflare's V8 isolate environment. Because the database bindings are native, there is <strong>zero TCP connection overhead</strong> and <strong>zero pool limit constraints</strong>. When your Worker runs a query, it accesses D1 instantly without a database cold start.</p><h2>Step 1: Initialize the Project</h2><p>Start by creating a new Astro project with the Cloudflare adapter:</p><pre><code>npm create astro@latest ./my-app -- --template minimal --yes
cd my-app
npx astro add cloudflare --yes
</code></pre><p>This sets up Astro with the <code>@astrojs/cloudflare</code> adapter, which compiles your app into a Cloudflare Worker.</p><h2>Step 2: Create the D1 Database</h2><p>Use Wrangler (Cloudflare's CLI) to create a D1 database:</p><pre><code>npx wrangler d1 create my-app-db
</code></pre><p>This outputs a database ID. Add it to your <code>wrangler.toml</code> (create this file in your project root):</p><pre><code>name = "my-app"
compatibility_date = "2026-07-01"

[assets]
directory = "./dist"

[[d1_databases]]
binding = "DB"
database_name = "my-app-db"
database_id = "your-database-id-here"
</code></pre><p>Now create your schema. Create a file called <code>schema.sql</code>:</p><pre><code>CREATE TABLE IF NOT EXISTS bookmarks (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  url TEXT NOT NULL,
  title TEXT NOT NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
</code></pre><p>Apply it:</p><pre><code>npx wrangler d1 execute my-app-db --file=./schema.sql
</code></pre><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a982!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed55a43-3925-4949-b3a5-b3c6ae956500_1280x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a982!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed55a43-3925-4949-b3a5-b3c6ae956500_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!a982!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed55a43-3925-4949-b3a5-b3c6ae956500_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!a982!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed55a43-3925-4949-b3a5-b3c6ae956500_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!a982!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed55a43-3925-4949-b3a5-b3c6ae956500_1280x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!a982!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed55a43-3925-4949-b3a5-b3c6ae956500_1280x800.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ed55a43-3925-4949-b3a5-b3c6ae956500_1280x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Cloudflare D1 Database Dashboard Metrics&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Cloudflare D1 Database Dashboard Metrics" title="Cloudflare D1 Database Dashboard Metrics" srcset="https://substackcdn.com/image/fetch/$s_!a982!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed55a43-3925-4949-b3a5-b3c6ae956500_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!a982!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed55a43-3925-4949-b3a5-b3c6ae956500_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!a982!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed55a43-3925-4949-b3a5-b3c6ae956500_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!a982!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ed55a43-3925-4949-b3a5-b3c6ae956500_1280x800.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 4: Cloudflare D1 dashboard displaying total queries, row metrics (reads and writes), and current storage usage.</em></p><h3>Managing D1 with a GUI</h3><p>If you are used to desktop database managers like PGAdmin (for Postgres), Laragon/phpMyAdmin (for MySQL), or DBGate, you don't have to manage D1 solely via the command line. You can explore and edit your database using graphical interfaces:</p><h4>1. Remote Database: Cloudflare Dashboard Console</h4><p>For your deployed production database, Cloudflare provides a built-in web console and data explorer:</p><ul><li><p>Log in to your Cloudflare Dashboard.</p></li><li><p>Navigate to <strong>Workers &amp; Pages &gt; D1</strong> and select your database (<code>my-app-db</code>).</p></li><li><p>Click the <strong>Console</strong> tab to run SQL queries directly in your browser.</p></li><li><p>Click the <strong>Tables</strong> tab to browse schemas, view rows, and edit table values visually without writing SQL.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Z39j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c10e14-160d-469d-ac5a-99a972c3ee22_1280x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Z39j!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c10e14-160d-469d-ac5a-99a972c3ee22_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!Z39j!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c10e14-160d-469d-ac5a-99a972c3ee22_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!Z39j!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c10e14-160d-469d-ac5a-99a972c3ee22_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!Z39j!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c10e14-160d-469d-ac5a-99a972c3ee22_1280x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Z39j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c10e14-160d-469d-ac5a-99a972c3ee22_1280x800.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/80c10e14-160d-469d-ac5a-99a972c3ee22_1280x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Cloudflare D1 Studio Data Explorer&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Cloudflare D1 Studio Data Explorer" title="Cloudflare D1 Studio Data Explorer" srcset="https://substackcdn.com/image/fetch/$s_!Z39j!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c10e14-160d-469d-ac5a-99a972c3ee22_1280x800.png 424w, https://substackcdn.com/image/fetch/$s_!Z39j!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c10e14-160d-469d-ac5a-99a972c3ee22_1280x800.png 848w, https://substackcdn.com/image/fetch/$s_!Z39j!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c10e14-160d-469d-ac5a-99a972c3ee22_1280x800.png 1272w, https://substackcdn.com/image/fetch/$s_!Z39j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80c10e14-160d-469d-ac5a-99a972c3ee22_1280x800.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Figure 5: Cloudflare D1 Studio interface displaying visual table columns, rows, and datatypes.</em></p><h4>2. Local Database: Standard SQLite Clients (TablePlus, DBeaver, DB Browser)</h4><p>When developing locally using Wrangler's local dev server, your database state is stored as a standard SQLite file inside your project directory at: <code>.wrangler/state/v3/d1/miniflare-D1DatabaseObject/</code></p><p>You can open this <code>.sqlite</code> file directly in any desktop SQLite client (such as TablePlus, DBeaver, DB Browser for SQLite, or the VS Code SQLite Viewer extension) to manage tables, run queries, and edit mock data locally.</p><h2>Step 3: Set Up KV for Sessions</h2><p>Create a KV namespace for session storage:</p><pre><code>npx wrangler kv namespace create SESSION
</code></pre><p>Add the output to your <code>wrangler.toml</code>:</p><pre><code>[[kv_namespaces]]
binding = "SESSION"
id = "your-kv-namespace-id-here"
</code></pre><p>KV is ideal for sessions because reads are extremely fast (edge-cached globally) and sessions are typically small key-value pairs. With 100K free reads/day, you can handle thousands of authenticated users.</p><h2>Step 4: Configure Astro for Hybrid Rendering</h2><p>Update your <code>astro.config.mjs</code> to enable hybrid rendering:</p><pre><code>import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';

export default defineConfig({
  output: 'hybrid',
  adapter: cloudflare({
    platformProxy: { enabled: true },
  }),
});
</code></pre><p>With <code>output: 'hybrid'</code>, pages are <strong>prerendered by default</strong>. Only pages that explicitly opt in to SSR will run on the Worker. This is how you keep your D1 reads low.</p><h2>Step 5: Build a Dynamic Page</h2><p>Create a server-rendered page that queries D1. Create <code>src/pages/bookmarks.astro</code>:</p><pre><code>---
export const prerender = false;

const runtime = Astro.locals.runtime;
const db = runtime.env.DB;

const { results } = await db
  .prepare('SELECT * FROM bookmarks ORDER BY created_at DESC')
  .all();
---

&lt;html&gt;
  &lt;body&gt;
    &lt;h1&gt;My Bookmarks&lt;/h1&gt;
    &lt;ul&gt;
      {results.map((b) =&gt; (
        &lt;li&gt;
          &lt;a href={b.url}&gt;{b.title}&lt;/a&gt;
          &lt;small&gt;{b.created_at}&lt;/small&gt;
        &lt;/li&gt;
      ))}
    &lt;/ul&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>The key line is <code>export const prerender = false</code>. This tells Astro to server-render this page instead of prerendering it.</p><h2>Step 6: Add an API Route</h2><p>Create <code>src/pages/api/bookmarks.ts</code> to handle form submissions:</p><pre><code>export const prerender = false;

import type { APIRoute } from 'astro';

export const POST: APIRoute = async ({ request, locals }) =&gt; {
  const db = locals.runtime.env.DB;
  const form = await request.formData();
  const url = form.get('url')?.toString();
  const title = form.get('title')?.toString();

  if (!url || !title) {
    return new Response('Missing fields', { status: 400 });
  }

  await db
    .prepare('INSERT INTO bookmarks (url, title) VALUES (?, ?)')
    .bind(url, title)
    .run();

  return Response.redirect(new URL('/bookmarks', request.url), 303);
};
</code></pre><h2>Step 7: Deploy</h2><p>Deploy your app to Cloudflare:</p><pre><code>npx astro build
npx wrangler deploy
</code></pre><p>That's it. Your full-stack app is live on Cloudflare's global edge network.</p><h2>Optimization: Staying Within Free Tier Limits</h2><p>Here's the strategy I use to keep my app comfortably within the free tier:</p><h3>1. Prerender Everything You Can</h3><p>Any page that doesn't need real-time data should be prerendered. Homepage, about page, blog posts, documentation, all static. This means zero D1 reads for the majority of your traffic.</p><h3>2. Use Indexes on D1</h3><p>D1 counts <strong>every row scanned</strong>, not just rows returned. A full table scan on a 5,000-row table counts as 5,000 reads even if you only return 1 row. Always add indexes on columns you filter or sort by:</p><pre><code>CREATE INDEX idx_bookmarks_created
  ON bookmarks(created_at DESC);
</code></pre><h3>3. Use KV as a Cache Layer</h3><p>For frequently-read but rarely-changed data, cache D1 results in KV:</p><pre><code>const cacheKey = 'bookmarks:latest';
let cached = await env.SESSION.get(cacheKey, 'json');

if (!cached) {
  const { results } = await env.DB
    .prepare('SELECT * FROM bookmarks ORDER BY created_at DESC LIMIT 20')
    .all();
  await env.SESSION.put(cacheKey, JSON.stringify(results), {
    expirationTtl: 300,
  });
  cached = results;
}
</code></pre><p>This pattern converts D1 reads into KV reads, which have a much higher free tier limit (100K vs 5M, but KV reads are faster and reduce database load).</p><h3>4. Use Astro's Built-in Static Pages</h3><p>For your landing page and marketing content, keep <code>prerender = true</code> (the default in hybrid mode). These pages are served directly from the Assets binding with zero Worker invocations.</p><h2>Final Thoughts</h2><p>Cloudflare's free tier is genuinely one of the best offerings for indie developers right now. The combination of D1 (SQL database), KV (fast caching), Workers (edge compute), and Assets (static hosting) gives you a complete full-stack platform without spending a cent.</p><p>The trick is architecture: use Astro's hybrid rendering to keep static pages static, index your D1 tables properly, and cache aggressively with KV. Do this right, and you can serve tens of thousands of requests per day, completely free.</p><p>Thanks for reading. See you in the next lab.</p>]]></content:encoded></item></channel></rss>